This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Patch to further harden glibc malloc metadata against 1-byte overflows


Hi,

[This is a resend of a private conversation started with Florian]

Back in 2014, we landed a malloc hardening measure against 1-byte
overflows. Here is a reference:

https://www.sourceware.org/ml/glibc-bugs/2014-09/msg00181.html

Since then, there's been this really interesting piece of work that
exploited Chrome OS using a 1-byte overflow to attack glibc malloc()
metadata in a different way:

https://googleprojectzero.blogspot.com/2016/12/chrome-os-exploit-one-byte-overflow-and.html

It has been bothering me that there's still a known, generic way to
attack 1-byte overflows in glibc malloc(). So I put some thought into
whether the situation can be cleanly detected and prevented.
It can. I've attached a 2-line patch (malloc.c.diff) that cleanly
detects and aborts on the condition. I've also attached an old test
case I had for this issue
(shrink_free_hole_alloc_overlap_consolidate_backward.c). Without the
patch, the program exits normally, having aliased a couple of heap
chunks. With the patch, the program exits with something like "***
Error in `./a.out': corrupted size vs. prev_size: 0x00000000022a0400
***"

I would be very excited to see this patch land :) I think it shuts
down a known, significant mitigation bypass. I also worry that if
there remain any serious bugs in important Linux services, they are
likely to bias towards subtle "1-byte overflows" and the like, which
can currently be exploited as demonstrated by the blog post above.
This patch can't defend against more powerful corruption primitives,
such as more arbitrary overflows or multiple overflows, but I think
it's a step forward for 1-byte overflows and particularly 1-byte NUL
overflows.


Cheers
Chris

Attachment: malloc.c.diff
Description: Text document

#include <stdlib.h>
#include <string.h>

int main(int argc, const char* argv[])
{
  // Allocate three large contiguous chunks.
  char* p = malloc(1024 - 8);
  // Make sure the chunk size (including metadata) is not 1024 exactly. If it
  // were 1024 exactly, setting the LSB to 0 would not affect the size.
  char* p2 = malloc(1024 + 16 - 8);
  char* p3 = malloc(1024 - 8);
  char* p2_1;
  char* p2_2;
  // Set them up with easily recognizable values.
  memset(p, 'A', 1024 - 8);
  memset(p2, 'B', 1024 + 16 - 8);
  memset(p3, 'C', 1024 - 8);
  // Free the second chunk.
  // This writes the "prev_size" size of the freed chunk, at the end of the
  // free()'d p2 buffer / the beginning of the in-use p3 buffer.
  free(p2);
  // Overflow the first chunk, off-by-one, with a NUL byte.
  // This truncates the size of the free chunk.
  p[1024 - 8] = '\0';
  // Allocate a subsection of the free'd second chunk. Since the free chunk we
  // are allocating out of has a truncated size, we end up miscalculting the
  // remaining space, and writing the "prev_size" value of the remaining space
  // at a slightly off location, before where it should be. This leaves the
  // old "prev_size" at the end of p2 / beginning p3.
  p2_1 = malloc(512 - 8);
  memset(p2_1, 'B', 512 - 8);
  // Allocate another subsection of the free'd second chunk.
  p2_2 = malloc(256 - 8);
  memset(p2_2, 'D', 256 - 8);
  // Free the first re-allocated subsection of the free'd second chunk.
  free(p2_1);
  // Free the third chunk.
  // We will see that the old, incorrect "prev_size" value and end up treating
  // p2 as a free'd chunk, and coalescing it, even though we have handed out
  // a still-live pointer half-way through. 
  free(p3);
  p2 = malloc(2048);
  // Now, p2_2 and p2 are both pointing to some areas of the same memory!!
  // Note that we achieved this without having to know any pointer values, so
  // this particular attack would be a nice ASLR defeat on 64-bit.
  // You can now deploy application-specific attacks.
}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]