Introduction
Buffer overflow/underflow frequently happens when we did something wrong with the array index, no matter the array is heap or stack, no matter you are reading the memory or writing the memory.
1 # To get a precise source code line number by valgrind report, 2 # It's better to build debug version, such as '-O0' for GCC.
Example 1: heap overflow
1 // head_overflow.c 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <math.h> 5 int main(int argc, char** argv) 6 { 7 int i; 8 int *a = malloc(sizeof(int) * 10); 9 if (!a) return -1; /*malloc failed*/ 10 // init 11 for (i = 0; i < 11; i++) 12 { 13 a[i] = i; 14 } 15 // square 16 for(i=0; i<10; i++) 17 { 18 a[i] *= a[i]; 19 } 20 free(a); 21 printf("I am finished. "); 22 return 0; 23 }
Let's build and run this file:
$ gcc -O3 -g -o heap_overflow heap_overflow.c $ ./heap_overflow *** Error in `./heap_overflow': free(): invalid next size (fast): 0x0000000001653010 *** Aborted (core dumped) |
Oops, Aborted while invoking free(). What's wrong?? Let's try gdb:
$ gdb ./heap_overflow (gdb) r Starting program: /home/jxion/jp4/depot/lechin/users/jxion/test_toys/test_valgrind/heap_overflow *** Error in `/home/jxion/jp4/depot/lechin/users/jxion/test_toys/test_valgrind/heap_overflow': free(): invalid next size (fast): 0x0000000000602010 *** Program received signal SIGABRT, Aborted. 0x00007ffff7a4af79 in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56 56 ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory. (gdb) (gdb) bt #0 0x00007ffff7a4af79 in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56 #1 0x00007ffff7a4e388 in __GI_abort () at abort.c:89 #2 0x00007ffff7a881d4 in __libc_message (do_abort=do_abort@entry=1, fmt=fmt@entry=0x7ffff7b96a10 "*** Error in `%s': %s: 0x%s *** ") at ../sysdeps/posix/libc_fatal.c:175 #3 0x00007ffff7a944ae in malloc_printerr (ptr=<optimized out>, str=0x7ffff7b96bb0 "free(): invalid next size (fast)", action=1) at malloc.c:4996 #4 _int_free (av=<optimized out>, p=<optimized out>, have_lock=0) at malloc.c:3840 #5 0x000000000040062c in main (argc=<optimized out>, argv=<optimized out>) at heap_overflow.c:21 (gdb) |
We still can only get the same err msg that crach in free(), which is line 21. But if we try valgrind:
$ valgrind ./heap_overflow ==31634== Memcheck, a memory error detector ==31634== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==31634== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info ==31634== Command: ./heap_overflow ==31634== ==31634== Invalid write of size 4 ==31634== at 0x40053C: main (heap_overflow.c:14) ==31634== Address 0x51fd068 is 0 bytes after a block of size 40 alloc'd ==31634== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==31634== by 0x4004DD: main (heap_overflow.c:8) ==31634== I am finished. ==31634== ==31634== HEAP SUMMARY: ==31634== in use at exit: 0 bytes in 0 blocks ==31634== total heap usage: 1 allocs, 1 frees, 40 bytes allocated ==31634== ==31634== All heap blocks were freed -- no leaks are possible ==31634== ==31634== For counts of detected and suppressed errors, rerun with: -v ==31634== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) |
Valgrind tells exactly where is the problem:
-
Invalid write of size 4 at 0x40053C: main (heap_overflow.c:14)
Example 2: heap underflow
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <math.h> 4 int main(int argc, char** argv) 5 { 6 int i; 7 int *a = malloc(sizeof(int) * 10); 8 if (!a) return -1; /*malloc failed*/ 9 // init 10 for (i = 0; i < 10; i++) 11 { 12 a[i] = i; 13 } 14 // sum 15 for(i=0; i<10; i++) 16 { 17 a[i] += a[i-1]; 18 } 19 free(a); 20 printf("I am finished. "); 21 return 0; 22 }
Also build and run:
$ gcc -O3 -g -o heap_underflow heap_underflow.c $ ./heap_underflow I am finished. |
Oh my GOD, it's finished. It means everything seems OK.
Really?
Let's try gdb:
$ gdb ./heap_underflow (gdb) r Starting program: /home/jxion/jp4/depot/lechin/users/jxion/test_toys/test_valgrind/heap_underflow I am finished. [Inferior 1 (process 32307) exited normally] (gdb) bt No stack. (gdb) |
Everything seems to be OK. Let's try Valgrind:
$ valgrind ./heap_underflow ==32275== Memcheck, a memory error detector ==32275== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==32275== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info ==32275== Command: ./heap_underflow ==32275== ==32275== Invalid read of size 4 ==32275== at 0x4004E3: main (heap_underflow.c:19) ==32275== Address 0x51fd03c is 4 bytes before a block of size 40 alloc'd ==32275== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==32275== by 0x4004DD: main (heap_underflow.c:8) ==32275== I am finished. ==32275== ==32275== HEAP SUMMARY: ==32275== in use at exit: 0 bytes in 0 blocks ==32275== total heap usage: 1 allocs, 1 frees, 40 bytes allocated ==32275== ==32275== All heap blocks were freed -- no leaks are possible ==32275== ==32275== For counts of detected and suppressed errors, rerun with: -v ==32275== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) |
Ooops, we find an error here:
-
Invalid read of size 4 at 0x4004E3: main (heap_underflow.c:19)
That's because we read data from a[-1] in line 19 !!
OS and gdb either didn't find this error, while Valgrind check it out.
Example 3: Stack overflow/underflow, none of OS/GDB/Valgrind realize the issue
It's weird that no matter OS/GDB/Valgrind, they cannot realize these issues.
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <math.h> 4 int main(int argc, char** argv) 5 { 6 int i; 7 int a[10]; 8 // init 9 a[-1] = -1; 10 a[11] = 11; 11 printf(" a[-1]= = %d, a[11] = %d ", a[-1], a[11]); 12 printf("I am finished. "); 13 return a[-1]; 14 }
build and run:
$ gcc -O0 -g -o stack_overflow stack_overflow.c $ ./stack_overflow a[-1]= = -1, a[11] = 11 I am finished. $ valgrind ./stack_overflow ==3705== Memcheck, a memory error detector ==3705== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==3705== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info ==3705== Command: ./stack_overflow ==3705== a[-1]= = -1, a[11] = 11 I am finished. ==3705== ==3705== HEAP SUMMARY: ==3705== in use at exit: 0 bytes in 0 blocks ==3705== total heap usage: 0 allocs, 0 frees, 0 bytes allocated ==3705== ==3705== All heap blocks were freed -- no leaks are possible ==3705== ==3705== For counts of detected and suppressed errors, rerun with: -v ==3705== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) $ gdb ./stack_overflow (gdb) r Starting program: /home/jxion/jp4/depot/lechin/users/jxion/test_toys/test_valgrind/stack_overflow a[-1]= = -1, a[11] = 11 I am finished. [Inferior 1 (process 3714) exited with code 0377] (gdb) bt No stack.