S
Simon Biber
jacob said:Replace all your malloc calls with this two functions.
They will allow you to know immediately the size of an
allocated object, besides giving you more security in handling
pointers, since they can give you a hint if the passed
pointer really points to an allocated buffer.
-------------------------------------------------------------cut here
/* this will be written at the end of the allocated block.
If this is overwritten it means something has written
beyond the block */
#define MAGIC 0xFFFF
/* This signature will be written at the start of the
allocated block to avoid freeing memory that wasn't
allocated at all */
#define SIGNATURE 12345678L
/* This variable counts the size of the memory allocated
so far. This is useful for detecting memory leaks */
static long AllocatedMemory;
/* This allocates a block, writing the signature, then
the size of the block. The useful data starts after
those two ints. At the end of the block, the "MAGIC"
number is written to detect overwrites */
void *allocate(int size)
This function should take a size_t value as argument. Many systems have
smaller int than size_t, and this unnecessarily restricts what can be
allocated. For example most 64-bit systems have 32-bit int and 64-bit
size_t.
{
register char *r;
register int *ip = NULL;
if (size <= 0)
return NULL;
size += 3 * sizeof(int);
r = malloc((size_t)size);
if (r == NULL)
return NULL;
memset(r, 0, (size_t) size);
AllocatedMemory += size;
ip = (int *) r;
*ip++ = SIGNATURE;
On an implementation with int less than 32 bits, you just mangled your
signature. According to the C99 standard, when converting integer values
to a type in which the value is not representable: "either the result is
implementation-defined or an implementation-defined signal is raised".
*ip++ = size;
If you took my advice to make size a size_t object, then the same
problem applies here: you're forcing it into an int.
memset(ip, 0, size - 3*sizeof(int));
ip = (int *) (&r[size - sizeof(int)]);
*ip = MAGIC;
If size was not a multiple of sizeof(int) then this will be an unaligned
access. Such accesses will trap on many implementations. For example, on
the Sun SPARC architecture.
In addition, if int is 16 bits then 0xFFFF is outside the range of
signed int, and the implementation-defined result or signal referred to
above may occur.
return (r + 2 * sizeof(int));
It's possible that 2*sizeof(int) is not sufficient alignment for some
larger types. For example, if sizeof(int) is 2 but sizeof(long long) is
8 and long long requires 8-byte alignment then one will not be able to
store long longs into this block. Also, if sizeof(int) is 4 but
sizeof(long double) is 16 and requires 16-byte alignment then one will
not be able to store long doubles into this block.
} [...]
/* This will return the size of a block or -1 if it is not
a valid block
*/
int GetSize(void *block)
{
int *ip = block;
ip -= 2;
if (*ip != SIGNATURE)
return -1;
ip++;
return *ip;
}
If it is not a valid block as allocated by the allocate function above,
then trying to read two ints before the pointed-to location will
probably go outside of the object pointed to. This is of course
undefined behaviour, and may result in a segmentation fault.