Catching weird C pointer arithmetic bugs
c, pointers
Solution
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.
Problem
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? UPDATE 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) ```