Warning: This section is extremely system dependent. The ideas should be generally applicable; however, the exact implementations described will need to be tailored to fit your system.
There are many different ways of implementing malloc() and free(). However, most modern implementations share the following features:
malloc() is always aligned to one
8 or 4 byte boundaries (with 8 byte being more likely).
free() to manage
memory.
In the system I work on:
Thus p = malloc(13) returns the following memory map:
__________
| 25 | header: 4 bytes: 24 (actual size) + 1 (in use)
|________|
p --> | | data: 13 bytes
|________|
| |
|________|
| |
|________|
| XXXXXX| padding: 7 bytes
|________|
|XXXXXXXX|
|________|
In this system, we can compute the value the header should have as:
unsigned
header_for_size_ptr(
unsigned sizeof_type
)
{
unsigned size;
size = (sizeof_type + 4); /* size + header */
size = (size+7)&~0x7; /* round off to nearest 8 */
size = size<16?16:size; /* minimum size */
size += 1; /* in use */
return size;
}
Using the knowledge we have gained so far, we can define some functions/macros that help us identify some errant pointers.
#define xtype_ptr(_t, _p) \
check_ptr(sizeof(_t), (_p))
#define xntype_ptr(_t, _n, _p)\
check_ptr(sizeof(_t)*(_n), (_p))
int
check_ptr(
unsigned size,
void * ptr
)
{
unsigned actual = ((unsigned *)ptr)[-1];
unsigned expect = header_for_size_ptr(size);
return actual == expect;
}