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;
}