Buffer overflows are a big one. Nothing in C is range-checked by default, so it's very easy to overwrite a buffer. There's a standard library function,
gets(), that cannot be stopped from overflowing the buffer, and should almost never be used.
There are some implementation-level techniques to hinder exploitation, such as scrambling heap blocks, but that won't stop buffer overflows in local buffers, which can often do interesting things like change the address a function will return to.
There is no good general solution in C. Many library functions have versions that will limit the amount they will write. although calculating that can be clumsy. There's software that can detect heap buffer overflows in test, as long as the appropriate test is run, and stack overflow will often show up as a crash in testing. Other than that, it's a matter of careful coding and code review.
A related issue is the problem of writing into a buffer too small by one character, forgetting that a C string that's n characters long requires n+1 characters in memory, because of the
'\0' terminator. If the attacker can manage to store a string without the terminator, any C function expecting a string will continue processing until it hits a zero byte, which could result in copying or outputting more information than is desired (or hitting protected memory for a DOS attack). The solution, again, is awareness, care, and code reviews.
There's another risk with the
printf() family. If you ever write
char * str; ... printf(str);, you're setting yourself up for problems if
str contains a '%' when printed. The
%n format directive allows
printf() to write into memory. The solution is
printf("%s", str); or
puts(str);. (Also, use the C99
snprintf() instead of
Using unsigned integers, particularly as loop indexes, can cause problems. If you assign a small negative value to an unsigned, you get a large positive value. That can undermine things like processing only N instances of something, or in limited functions like
strncpy(). Examine all unsigned integers. You might want to avoid
unsigned short, since a large value in one of those will convert to a large positive value in an
Don't forget that a character constant, in C, is actually an
int. Writing something like
char c; while((c = getchar()) != EOF) ... can easily fail, since
EOF won't be representable in a
There's a lot more characteristic C mistakes I can think of, but these could cause security problems.