Personally; I have a resource tracking library (basically a balanced binary tree) and I have wrappers for all allocation functions.
Resources (such as memory, sockets, file descriptors, semaphores, etc - anything you allocate and deallocate) can belong to a set.
I also have an error handling library, whereby the first argument to each function is an error set and if something goes wrong, the function experiencing an error submits an error into the error set.
If an error set contains an error, no functions execute. (I have a macro at the top of every function which causes it to return).
So multiple mallocs look like this;
mem[0] = malloc_wrapper( error_set, resource_set, 100 );
mem[1] = malloc_wrapper( error_set, resource_set, 50 );
mem[2] = malloc_wrapper( error_set, resource_set, 20 );
There is no need to check the return value, because if an error occurs, no following functions will execute, e.g. the following mallocs never occur.
So, when the time comes for me to deallocate resources (say at the end of a function, where all the resources used internally by that function have been placed into a set), I deallocate the set. It's just one function call.
res_delete_set( resource_set );
I don't need to specifically check for errors - there are no if()s in my code checking return values, which makes it maintainable; I find the profliferation of in-line error check destroys readability and so maintainability. I just have a nice plain list of function calls.
It's art, man :-)