I recently ran into a very sneaky bug, in which I forget to dereference a pointer to a string (char array) and thus sometimes overwrote one byte on the stack.
Bad:
char ** str;
(*str) = malloc(10);
...
str[2] = 'a'; //overwrites 3 bytes from the location in which str is stored
Corrected:
char ** str;
(*str) = malloc(10);
...
(*str)[2] = 'a';
GCC produced no warnings, and this error would've resulted in a very serious and real exploit as the value it sometimes overwrote held the size of a buffer. I only caught this bug because I got luckly and it caused an obvious failure.
Other than relying on luck and/or never using C for anything, what defensive coding techniques and tricks do you use to catch wierd C bugs?
I'm thinking about moving to valgrind's MemCheck, has anyone used it? I suspect it wouldn't have caught this bug. Anyone know?
Are there tools for catching pointer dereferencing or arithmetic bugs? Is that even possible?
Here is the requested example code, it does not throw any warnings.
#include <stdlib.h>
void test(unsigned char** byteArray){
(*byteArray) = (unsigned char*)malloc(5);
byteArray[4] = 0x0;
}
int main(void){
unsigned char* str;
test(&str);
return 0;
}
Compiling causes no errors:
gcc -Wall testBug.c -o testBug
Running causes a seg fault:
./testBug
Segmentation fault
This is the version of GCC I'm using:
gcc -v
Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.4.1-4ubuntu9' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --program-suffix=-4.4 --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --enable-targets=all --disable-werror --with-arch-32=i486 --with-tune=generic --enable-checking=release --build=i486-linux-gnu --host=i486-linux-gnu --target=i486-linux-gnu
Thread model: posix
gcc version 4.4.1 (Ubuntu 4.4.1-4ubuntu9)
My best defensive pointer strategy: Strongly avoid using more than one level of indirection. Dereferencing the pointer-to-pointer to assign memory to it is OK. But to then use the assigned memory as an array is asking for trouble, which you got. I would make it something like:
char **outStr;
*outStr = malloc(10);
char *str = *outStr;
str[2] = 10;
OK, actually it's just a keep-my-sanity strategy that happens to have defensive value as well. Pointers are fairly easy to understand when there's never more than one level of indirection at a time, and it's easier to make code work right when you understand it well.
I use Valgrind, it's a lifesaver!
valgrind --tool=memcheck -v ./yourapp
And MemCheck will detect that you have an invalid write with `str[2] = 'a';´.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With