~/bin $ gcc -fbounds-checking m.c -o m
~/bin $ ./m
<snip>
0x177da000
<snip>
~/bin $ gcc m.c -o m
~/bin $ ./m
(nil)
~/bin $ cat m.c
int main()
{
printf("%p\n", malloc(-1));
}
~/bin $
(added 21/7/2007)
The GCC bounds checking patch has several integer overflows where (size_t)0-1 will become 0, and then be able to be malloc'd.
The below is a test case which shows this in progress:
~/bin $ gcc -fbounds-checking m.c -o m
~/bin $ ./m
<snip>
0x177da000
<snip>
~/bin $ gcc m.c -o m
~/bin $ ./m
(nil)
~/bin $ cat m.c
int main()
{
printf("%p\n", malloc(-1));
}
~/bin $
This particular bug happens in the below code path:
/* Wrapper functions for the major C library dynamic memory functions.
These call the GNU library with the side effect of adding or
deleting memory objects where necessary. */
void *
__bounds_check_malloc (const char *filename, int line, size_t size)
{
void *pointer;
sigset_t old_mask;
/* Although malloc (0) is a strange usage, we are required to support it.
We can return any pointer that cannot be dereferenced here, in
particular, some libraries return NULL. However, some software thinks
that means out of memory. We could also return ILLEGAL, but X11 has
a habit of passing that pointer back to free (). Instead we make
malloc (0) == malloc (1), and you'd better not dereference it, since
the objects library doesn't support zero-sized objects. */
if (size == 0)
size = 1;
enter_critical_section (&old_mask);
pointer = __bounds_malloc (size + 1); <--- HERE
if (pointer != NULL)
__bounds_add_heap_object (pointer, size, 1, "malloc", 0, filename, line);
leave_critical_section (&old_mask);
return pointer;
}
At the size + 1 line, an overflow can occur if the size to be allocated is (size_t)0-1 bytes. These leads to a successful memory allocation. Other overflows exist regarding size + 1.
That can cause problems where previously unexploitable software bugs become become exploitable due to these overflows. An example of this bug may be (code written off the top of my head, and is meant to be a trivial example):
int readstuff(int fd)
{
unsigned char *buf;
size_t len, count;
int n;
if(read(fd, &len, sizeof(len)) != sizeof(len)) return FAILURE;
buf = malloc(len);
if(buf == NULL) return FAILURE;
count = 0;
while(count < len) {
n = read(fd, buf + count, len - count);
if(n < 1) {
return FAILURE;
}
count += n;
// blah
}
return SUCCESS;
}
If the len parameter was 0xffffffff, it would wrap around to 0 bytes with the bounds checking patch. Inside the while(count < len) loop (with the previous condition) allows the attacker to write more memory to the heap/whatever than the program anticipated. As the writing to memory is performed in the kernel, and the bounds checking patch doesn't wrap functions like read() to check if it's valid, you may have an exploitable condition.
At any rate, the GCC Bounds checking patch is quite awesome, and will be involved in future posts, with various things you can do with it.