tags:

views:

494

answers:

11

So I have this simple piece of code which demonstrates a simple buffer overflow:

#include <stdio.h>

int main(void)
{
    char c[4] = { 'A', 'B', 'C', 'D' };
    char d[4] = { 'W', 'X', 'Y', 'Z' };

    printf("c[0] is '%c'\n", c[0]);

    d[4] = 'Z'; /* Overflow that overwrites c[0] */

    printf("c[0] is '%c'\n", c[0]);

    return 0;
}

The output:

$ ./a.out
c[0] is 'A'
c[0] is 'Z'

I have tried compiling this code with the following gcc options and it passed with flying colors:

gcc -Wall -Wextra -Wformat=2 -Wswitch-default -Wcast-align -Wpointer-arith \
    -Wbad-function-cast -Wstrict-prototypes -Winline -Wundef -Wnested-externs \
    -Wcast-qual -Wshadow -Wwrite-strings -Wconversion -Wunreachable-code \
    -Wstrict-aliasing=2 -ffloat-store -fno-common -fstrict-aliasing \
    -Wstack-protector -fstack-protector-all -std=c99 -pedantic -O0 -ggdb3

I also tried libefence and valgrind. I expected libefence to pass since it's made to catch out of bounds read/writes on the heap, but I was surprised that valgrind passed.

This code does not produce a Segfault since c[4] and d[0] happen to overlap and I think it is this that is causing tools to miss it.

So, what out there CAN catch this? Something free that works on Linux would be nice.

A: 

If it is on linux I would try out valgrind first, I think it will handle it.

Edit: If it is like SiegeX says I guess valgrind doesn't (which makes sense because it have no way to insert guards on the stack as it is only involved in the process runtime). It is however a good tool anyway that is worth having in your toolbox so I will keep the post.

Fredrik
Valgrind *DOES NOT* catch this as clearly stated in the question. Please stop up-voting this.
SiegeX
-1 after the edit wasn't really fair imho.
Fredrik
@SiegeX: "Valgrind also pass" looks, to me, as it catches it. I must say I was a bit surprised, since I didn't expect it to deal with that, as it's designed to catch memory that's been heap-allocated.
Vatine
A: 

I think there's an implied null character in this case, since you're referencing a literal string. Hence, d[4] is still in bounds (imagine d as const char*)....I may be incorrect though.

Snazzer
If I had declared it as c[5] = "ABCD"; then the compiler would have thrown in the NUL byte at the end. But since there is no room for the NUL byte with only 4 characters, it is discarded. However, I have changed the code to explicitly use chars for initialization to reduce confusion.
SiegeX
+2  A: 

As valgrind works on binary, it did not see anything wrong with this code. Check these (http://www.thefreecountry.com/programming/debuggers.shtml) static source code analyzers, they should find. If they do not work, PC-lint (http://www.gimpel.com/html/pcl.htm) will handle this....

Malkocoglu
+1 for recommending static source analyzers... That and/or peer reviews are probably the most likely to catch it if things like valgrind doesn't (and thinking about it, I realize there is no way valgrind can find it unless it gets involved in the compilation process)
Fredrik
Although your 'debuggers.shtml' link does not talk about 'cppcheck' ( accepted answer), this is a very good link otherwise. Thanks
SiegeX
I have not heard of cppcheck before. Oldest release was on 2007, so it is relatively new. Especially when compared to PC-Lint...
Malkocoglu
A: 

GuardMalloc/libgmalloc???

Justin
Probably not as it is stack based data.
Fredrik
A: 

Rational Purify works quite well with detecting buffer overflows, memory leaks, corruptions, etc. It is pretty expensive, though.

The mem package mentioned in this SO answer may be another option.

Paradigm
+1  A: 

Coverity (a static analysis tool) will catch this.

AndreyT
+2  A: 

Try cppcheck.

Teddy
Good call on 'cppcheck'. I've never heard of this source analyzer before but *IT DOES* find the error!
SiegeX
A: 

Buffer overflows are fairly hard to catch in C since they don't happen until runtime. To minimize the chance of causing one you should use the somewhat safer standard library functions that do some bounds checking -- e.g. fgets() instead of gets().

If your manipulating a lot of arrays manually you should probably write unit tests to check edge cases in your algorithms. Cmockery is an example a unit test framework for C.

chris.nullptr
This specific buffer overrun shoudl be catchable compile-time, as the code (statically) accesses *(c+4), with an allocation where c+3 is the last valid address.
Vatine
A: 

Valgrind doesn't catch overflows in automatic and static storage. At least not by default. IIRC to make it work you should either turn on some option or run of the "in development" tools that comes with it, not the default "memcheck".

Eli Bendersky
I upgraded to the latest valgrind 3.5.0 and used the --tool=exp-ptrcheck and this still passed with zero warnings and errors.
SiegeX
A: 

visual studio (with -Zi I think, but I could be wrong) catches this sort of assignment with its runtime stack checker. It's not the free linux solution you preferred, but does work nicely.

Peeter Joot
A: 

Try with Bugfighter C/C++.

I use it every day and it works fine, even with multidimensional array like array[5][5][5].

The Bugfighter web page is www.bugfighter-soft.com.

Bye

luca