$ cat example.c
main() {
char *malloc(); char *a = malloc(2);
a[2] = 'x';
}
$ ./bgcc -fbounds-checking example.c
$ ./a.out
Bounds Checking GCC v gcc-4.0.4-3.2 Copyright (C) 1995 Richard W.M. Jones
Bounds Checking comes with ABSOLUTELY NO WARRANTY. For details see file
`COPYING' that should have come with the source to this program.
Bounds Checking is free software, and you are welcome to redistribute it
under certain conditions. See the file `COPYING' for details.
For more information, set GCC_BOUNDS_OPTS to `-help'
example.c:3:Bounds error: attempt to reference memory overrunning the end of an object.
example.c:3: Pointer value: 0x8431002, Size: 1
example.c:3: Object `malloc':
example.c:3: Address in memory: 0x8431000 .. 0x8431001
example.c:3: Size: 2 bytes
example.c:3: Element size: 1 bytes
example.c:3: Number of elements: 2
example.c:3: Created at: example.c, line 2
example.c:3: Storage class: heap
This example shows some additional features:
malloc
or by modules compiled with
bounds checking) are checked in calls to the str*
and mem*
library functions and in calls to modules compiled with bounds checking.
GCC_BOUNDS_OPTS
environment variable passes options to the bounds checker.
-never-fatal
option continues after errors.
-print-heap
option shows memory leaks.
$ cat example2.c
void test(char *p) { p[1] = 'x'; } /* access a pointer out of range */
static char static_var[1]; /* example static variable */
main() {
char local_var[1]; /* example stack variable */
char *heap_var, *malloc();
struct { char *a; char *b; } s; /* example structure with pointers */
heap_var = malloc(1); /* example heap variable */
test(local_var); /* access a local variable out of range */
test(static_var); /* access a static variable out of range */
test(heap_var); /* access a heap variable out of range */
s.a = local_var;
s.b = static_var;
unchecked(&s); /* pass to a routine in unchecked module */
}
$ cat example2u.c
/* example unchecked module mixed with checked module */
/* No special preparation is required, even for */
/* structures containing pointers. */
struct s_tag { char *a; char *b; };
void unchecked(struct s_tag *s) {
memset(s->a, 0, 2); /* example of catching checked pointers in */
memcpy(s->b, s->a, 2); /* library functions even from unchecked code */
}
$ gcc -c example2u.c
$ ./bgcc -fbounds-checking example2.c example2u.o
$ GCC_BOUNDS_OPTS="-never-fatal -print-heap" ./a.out
Bounds Checking GCC v gcc-4.0.4-3.2 Copyright (C) 1995 Richard W.M. Jones
Bounds Checking comes with ABSOLUTELY NO WARRANTY. For details see file
`COPYING' that should have come with the source to this program.
Bounds Checking is free software, and you are welcome to redistribute it
under certain conditions. See the file `COPYING' for details.
For more information, set GCC_BOUNDS_OPTS to `-help'
example2.c:1:Bounds error: attempt to reference memory overrunning the end of an object.
example2.c:1: Pointer value: 0xbff63f6b, Size: 1
example2.c:1: Object `local_var':
example2.c:1: Address in memory: 0xbff63f6a .. 0xbff63f6a
example2.c:1: Size: 1 bytes
example2.c:1: Element size: 1 bytes
example2.c:1: Number of elements: 1
example2.c:1: Created at: example2.c, line 6
example2.c:1: Storage class: stack
example2.c:1:Bounds error: attempt to reference memory overrunning the end of an object.
example2.c:1: Pointer value: 0x80691e9, Size: 1
example2.c:1: Object `static_var':
example2.c:1: Address in memory: 0x80691e8 .. 0x80691e8
example2.c:1: Size: 1 bytes
example2.c:1: Element size: 1 bytes
example2.c:1: Number of elements: 1
example2.c:1: Created at: example2.c, line 3
example2.c:1: Storage class: static
example2.c:1:Bounds error: attempt to reference memory overrunning the end of an object.
example2.c:1: Pointer value: 0x8f46001, Size: 1
example2.c:1: Object `malloc':
example2.c:1: Address in memory: 0x8f46000 .. 0x8f46000
example2.c:1: Size: 1 bytes
example2.c:1: Element size: 1 bytes
example2.c:1: Number of elements: 1
example2.c:1: Created at: example2.c, line 9
example2.c:1: Storage class: heap
<unknown>:0:Bounds error: memset with this destination pointer and size 2 would overrun the end of the object's allocated memory.
<unknown>:0: Pointer value: 0xbff63f6a
<unknown>:0: Object `local_var':
<unknown>:0: Address in memory: 0xbff63f6a .. 0xbff63f6a
<unknown>:0: Size: 1 bytes
<unknown>:0: Element size: 1 bytes
<unknown>:0: Number of elements: 1
<unknown>:0: Created at: example2.c, line 6
<unknown>:0: Storage class: stack
<unknown>:0:Bounds error: memcpy with this destination pointer and size 2 would overrun the end of the object's allocated memory.
<unknown>:0: Pointer value: 0x80691e8
<unknown>:0: Object `static_var':
<unknown>:0: Address in memory: 0x80691e8 .. 0x80691e8
<unknown>:0: Size: 1 bytes
<unknown>:0: Element size: 1 bytes
<unknown>:0: Number of elements: 1
<unknown>:0: Created at: example2.c, line 3
<unknown>:0: Storage class: static
<unknown>:0:Bounds error: memcpy with this source pointer and size 2 would overrun the end of the object's allocated memory.
<unknown>:0: Pointer value: 0xbff63f6a
<unknown>:0: Object `local_var':
<unknown>:0: Address in memory: 0xbff63f6a .. 0xbff63f6a
<unknown>:0: Size: 1 bytes
<unknown>:0: Element size: 1 bytes
<unknown>:0: Number of elements: 1
<unknown>:0: Created at: example2.c, line 6
<unknown>:0: Storage class: stack
Bounds library call frequency statistics:
Calls to push, pop, param function: 1, 1, 0
Calls to add, delete stack: 2, 2
Calls to add, delete heap: 1, 0
Calls to check pointer +/- integer: 3
Calls to check array references: 0
Calls to check pointer differences: 0
Calls to check object references: 3
Calls to check component references: 0
Calls to check truth, falsity of pointers: 0, 0
Calls to check <, >, <=, >= of pointers: 0
Calls to check ==, != of pointers: 0
Calls to check p++, ++p, p--, --p: 0, 0, 0, 0
Calls to add, find, delete oob pointers: 0, 0, 0
References to unchecked static, stack: 0, 0
Filename = example2.c, Line = 9, Function = malloc, Count = 1 Avg Size = 1, Total = 1
To build a bounds checking gcc:
cd /u/gnu # go to a work area
tar xzf gcc-x.y.tar.gz # unpack gcc
mv gcc-x.y bgcc-x.y # rename the gcc directory
cd bgcc-x.y # go to the bgcc directory
patch -p1 -T < gcc-x.y-bgcc-x.y.pat # apply the patches
touch gcc/c-parse.in # force a rebuild of .y and .c
mkdir objdir # make an object file area
cd objdir # enter the area
/u/gnu/bgcc-x.y/configure # initialize the build
make bootstrap # do the build
You can run the bounds checking gcc from its build area using the script
/u/gnu/bgcc-x.y/gcc/bounds/bgcc
/u/gnu/bgcc-x.y/gcc/bounds/
.
These patches add a -fbounds-checking
flag that
adds bounds checking tests to pointer and array accesses.
Richard Jones developed the
patches against gcc-2.7
in 1995.
Herman ten Brugge is the current maintainer and updates patches to the
boundschecking project at sourceforge.
William Bader has
unofficial updates.
You may freely mix object modules compiled with and without bounds checking.
The bounds checker also includes replacements for
mem*
and str*
routines
and can detect invalid calls against checked memory objects, even from modules
compiled without bounds checking.
These patches are unrelated to the fat pointer bounds checking patches by Greg McGary [email protected] which change the size of pointers and require building modified versions of libc and most other libraries that your program calls. Greg's patches will eventually be incorporated into GCC. If you can use Greg's fat pointer bounds checker, it has the advantage of better run-time performance and support for languages other than C.
Valgrind by Julian Seward is another open-source memory debugger for Intel x86-based Linux systems.
Valgrind runs unmodified ELF x86 Linux executables within a Pentium emulator and detects
accesses to uninitialized variables, accesses to unallocated memory, and memory leaks.
In comparison to bounds checking gcc, valgrind has the advantages of detecting accesses to
uninitialized variables, of not requiring recompilation or relinking, and of supporting C++.
Valgrind has the disadvantages of working only on x86-based Linux systems and of not detecting
out-of-bounds array accesses as long as the accesses still produce valid addresses (so it often
misses off-by-one accesses to local arrays).
Valgrind has more memory overhead than bounds checking gcc but about the same amount of cpu overhead.
I regularly use both bounds checking gcc and valgrind.
Valgrind Home
http://valgrind.kde.org/
Valgrind freshmeat project
http://freshmeat.net/projects/valgrind/
Valgrind HowTo
http://www.tldp.org/HOWTO/Valgrind-HOWTO/