-
Website
http://www.matasano.com/log -
Original page
http://www.matasano.com/log/182/four-c-programming-anti-idioms/ -
Subscribe
All Comments -
Community
-
Top Commenters
-
Press Controls
3 comments · 2 points
-
ChrisMtso
12 comments · 1 points
-
Eric Monti
11 comments · 1 points
-
StatlerAndWaldorf
12 comments · 3 points
-
Dave G.
7 comments · 1 points
-
-
Popular Threads
Casting a void pointer has always been wrong in C even though *everybody* does it. With malloc() there is at least one other subtle but possibly catastrophic problem. If you forget to include stdlib.h and you are casting all of the malloc return values the compiler will not give you a hint that the malloc prototype is missing. The return value will then default to integer. Now if you or somebody else tries to use your software on some platform where integers and pointers have different sizes or semantics you have a bug. If you're really unlucky the bug will even be exploitable.
Don't you love that just missing a header file can create vulnerabilities?
I hope to hear your opinion on this.
-daniel
/Matt
Once I had to debug memory corruption caused by something like this:
s = malloc(10000)
s[1025] = 'a';
malloc returned NULL but the assignment didn't segfault. What happened?
The problem was the page at 0 was protected, but the next page wasn't. s[1025] = 'a' was writing to somewhere on the heap. This was a pain to debug.
I prefer using a malloc() wrapper that asserts on the return value or just asserting on return value if there isn't one. Not checking the return value of a function is suspect to me.
v = malloc(sizeof *v);
will do the trick. Less typing, easier to read.
The parentheses are only needed when you're naming a type, e.g. "malloc(sizeof (struct V))".
I can't agree with #1, at least when it comes to writing a library. It's just not the library-writer's place to make decisions like that for an application, and if the library-writer's answer to me was "just preload a malloc that does what you actually want" I'd be pissed. Preloading is a useful and clever hack when you need it, but it's no way to engineer the common case. What if one of your libraries statically linked libc into it? What if some libc implementation inlines malloc?
When I'm really investing a lot of effort into a library, or writing a library that is intended to be useful in space-constrained situations, I'll usually either avoid allocating memory internally or let the client pass me a pointer to their own malloc/free (with an additional user-defined void* so it can be reentrant, if desired). That gives the client maximal flexibility to define their own memory-allocation policy.
If X's program is so badly written that the heap can overflow (several GB in todays date) without X being aware of that when writing the program, trying to fix the symptom by checking the value of malloc and exiting is a waste of X's time.
Better that X advertise X's incompetency by letting the user see a SIGSEGV or Fatal error dialog box depending on the platform.
'xmalloc'?"
It's 2007. Why are you wasting time with C?
Especially when malloc/free were in separate modules (passing pointers around), this caught all kinds of bugs (arrays of arrays, etc) that assumed different things. Don't seem to have this problem anymore with Ruby. ;-)
See: http://labs.musecurity.com/2007/07/23/writing-c...
nope... unfortunately, you forget that on most systems malloc used to return a char *. (char *malloc(unsigned size);) the cast was a necessity. pre ansi-C89 void pointers were not even guaranteed on your compiler -- much less the behavior regarding their assignment.
when c++ eventually became its own forked language (still pre-ansi-c89) this behavior remained (though i believe c++ adopted the void * return value for malloc as well).
If you want to follow old-school C programming techniques (which is a valid argument), I would suggest using a errno-like global variable to store the cause of the fault and then do check for errors all the way back to main(). Functions would return the so-called -1 on error, which would be checked when called (possibly with a ALL-CAPS satellite macro to perform the check more verbosely), but generally ignore the errno-like value (unless if it really needs to). Then, centralize all the user error messages in one function.
This way, the resources can be unallocated easily, and you can reuse the code easily and in many scenarios.