J
jacob navia
In the C tutorial for lcc-win32, I have a small chapter
about a debugging implementation of malloc.
Here is the code, and the explanations that go with it.
I would appreciate your feedback both about the code and
the associated explanations.
---------------------------------------------------------------------
Instead of using directly malloc/free here are two implementations of
equivalent functions with some added safety features:
o Freeing NULL is allowed and is not an error.
o Double freeing is made impossible.
o Any overwrite immediately at the end of the block is checked for.
o Memory is initialized to zero.
o A count of allocated memory is kept in a global variable.
#include <stdlib.h>
#define MAGIC 0xFFFF
#define SIGNATURE 12345678L
size_t AllocatedMemory;
void *allocate(size_t size)
{
register char *r;
register int *ip = NULL;
size += 3 * sizeof(int);
r = malloc(size);
if (r == NULL)
return r;
AllocatedMemory += size;
ip = (int *) r;
// At the start of the block we write the signature
*ip++ = SIGNATURE;
// Then we write the size of the block in bytes
*ip++ = (int) size;
// We zero the data space
memset(ip, 0, size - 3*sizeof(int));
// We write the magic number at the end of the block,
// just behind the data
ip = (int *) (&r[size - sizeof(int)]);
*ip = MAGIC;
// Return a pointer to the start of the data area
return (r + 2 * sizeof(int));
}
void release(void *pp)
{
register int *ip = NULL;
int s;
register char *p = pp;
if (p == NULL) // Freeing NULL is allowed
return;
// The start of the block is two integers before the data.
p -= 2 * sizeof(int);
ip = (int *) p;
if (*ip == SIGNATURE) {
// Overwrite the signature so that this block
// can’t be freed again
*ip++ = 0;
s = *ip;
ip = (int *) (&p[s - sizeof(int)]);
if (*ip != MAGIC) {
ErorPrintf(“Overwritten block size %d”, s);
return;
}
*ip = 0;
AllocatedMemory -= s;
free(p);
}
else {
/* The block has been overwritten. Complain. */
ErrorPrintf(“Wrong block passed to release”);
}
}
The allocate function adds to the requested size space for 3 integers.
1) The first is a magic number (a signature) that allows the
identification of this block as a block allocated by our allocation
system.
2) The second is the size of the block. After this two numbers, the data
follows.
3) The data is followed by a third number that is placed at the end of
the block. Any memory overwrite of any block will overwrite probably
this number first. Since the “release” function checks for this, we
will be able to detect when a block has been overwritten.
At any time, the user can ask for the size of total allocated memory
(valid blocks in circulation) by querying the AllocatedMemory variable.
The “release function” accepts NULL (that is ignored). If the pointer
passed to it is not NULL, it will check that it is a valid block, and
that the signature is still there, i.e. that no memory overwrites have
happened during the usage of the block.
about a debugging implementation of malloc.
Here is the code, and the explanations that go with it.
I would appreciate your feedback both about the code and
the associated explanations.
---------------------------------------------------------------------
Instead of using directly malloc/free here are two implementations of
equivalent functions with some added safety features:
o Freeing NULL is allowed and is not an error.
o Double freeing is made impossible.
o Any overwrite immediately at the end of the block is checked for.
o Memory is initialized to zero.
o A count of allocated memory is kept in a global variable.
#include <stdlib.h>
#define MAGIC 0xFFFF
#define SIGNATURE 12345678L
size_t AllocatedMemory;
void *allocate(size_t size)
{
register char *r;
register int *ip = NULL;
size += 3 * sizeof(int);
r = malloc(size);
if (r == NULL)
return r;
AllocatedMemory += size;
ip = (int *) r;
// At the start of the block we write the signature
*ip++ = SIGNATURE;
// Then we write the size of the block in bytes
*ip++ = (int) size;
// We zero the data space
memset(ip, 0, size - 3*sizeof(int));
// We write the magic number at the end of the block,
// just behind the data
ip = (int *) (&r[size - sizeof(int)]);
*ip = MAGIC;
// Return a pointer to the start of the data area
return (r + 2 * sizeof(int));
}
void release(void *pp)
{
register int *ip = NULL;
int s;
register char *p = pp;
if (p == NULL) // Freeing NULL is allowed
return;
// The start of the block is two integers before the data.
p -= 2 * sizeof(int);
ip = (int *) p;
if (*ip == SIGNATURE) {
// Overwrite the signature so that this block
// can’t be freed again
*ip++ = 0;
s = *ip;
ip = (int *) (&p[s - sizeof(int)]);
if (*ip != MAGIC) {
ErorPrintf(“Overwritten block size %d”, s);
return;
}
*ip = 0;
AllocatedMemory -= s;
free(p);
}
else {
/* The block has been overwritten. Complain. */
ErrorPrintf(“Wrong block passed to release”);
}
}
The allocate function adds to the requested size space for 3 integers.
1) The first is a magic number (a signature) that allows the
identification of this block as a block allocated by our allocation
system.
2) The second is the size of the block. After this two numbers, the data
follows.
3) The data is followed by a third number that is placed at the end of
the block. Any memory overwrite of any block will overwrite probably
this number first. Since the “release” function checks for this, we
will be able to detect when a block has been overwritten.
At any time, the user can ask for the size of total allocated memory
(valid blocks in circulation) by querying the AllocatedMemory variable.
The “release function” accepts NULL (that is ignored). If the pointer
passed to it is not NULL, it will check that it is a valid block, and
that the signature is still there, i.e. that no memory overwrites have
happened during the usage of the block.