When programming in C, heap management is critical. Most of today’s exploitation happens in that segment of memory !
Malloc
The most common way to allocate memory is to use the malloc function from the Standard C library.
The function is prototyped :
#include <stdlib.h>
void *malloc(size_t size);
It takes a number of bytes to allocate and then returns a pointer to a memory region on the heap with that many bytes reserved.
Important considerations
Type of parameter : size_t
The parameter is the largest possible size for a given system and is unsigned. It is important to make sure the parameter and any calculation is comes from are marked as unsigned types and ideally size_t variables.
At selmelc cybersecurity we’ve identified numerous vulnerabilities stemming from a size calculation that was done on signed typed variable which led to abnormally long allocation when the calculation resulted in a negative number.
Another common pattern is the following :
...
int len_str = strlen(str) + 1;
char *ptr = malloc(len_str);
...
memcpy(ptr, str, strlen(str) + 1);
The problem here is that len_str is a signed integer, if the length of the string coming in is equal to UINT_MAX we suddenly have int len_str = 0 as the result of the calculation. So now we malloc 0 bytes but memcpy UINT_MAX bytes… Buffer overflow achieved and the rest is history.
Return values
On error the function will return NULL, that is where the term “protecting your mallocs” comes from. In reality this protection is simply standard error handling as many functions display similar behavior on error.
Good example:
void *ptr = malloc(1337);
if (!ptr)
// ... Handle error
... //Operations on the pointer
If this condition is not verified we might dereference a null pointer which comes with its own set of risks (See CWE-476).
The size zero case
As noted in the man page :
“The behavior of these functions when the requested size is zero is glibc specific”
In the most common versions of the libc the behavior that can be observed is that the smallest chunk possible is allocated (the size of the smallest chunk depends on the malloc implementation). This is particularly interesting in cases such as the one previously mentioned where the result of an addition is 0 but later the variable is used to copy memory. Instead of writing at 0x0 the buffer overflow will happen in an actual tiny valid heap region which is surrounded by other valid allocations.
