2339 lines
64 KiB
C++
2339 lines
64 KiB
C++
/*
|
|
* Modification History
|
|
*
|
|
* 2002-March-29 Jason Rohrer
|
|
* Fixed so that delete [] can be used on strdup strings.
|
|
* Changed to violate memory upon CheckPointer failing (so we can get
|
|
* a stack trace).
|
|
* Changed to use mutex locks for thread safety.
|
|
* Changed to use pthread mutex directly to avoid
|
|
* stack overflow (since MutexLock uses the new operator).
|
|
*/
|
|
|
|
/* fortify.cxx - A fortified memory allocation shell - V2.2 */
|
|
|
|
/*
|
|
* This software is not public domain. All material in
|
|
* this archive is (C) Copyright 1995 Simon P. Bullen. The
|
|
* software is freely distributable, with the condition that
|
|
* no more than a nominal fee is charged for media.
|
|
* Everything in this distribution must be kept together, in
|
|
* original, unmodified form.
|
|
* The software may be modified for your own personal use,
|
|
* but modified files may not be distributed.
|
|
* The material is provided "as is" without warranty of
|
|
* any kind. The author accepts no responsibility for damage
|
|
* caused by this software.
|
|
* This software may not be used in any way by Microsoft
|
|
* Corporation or its subsidiaries, or current employees of
|
|
* Microsoft Corporation or its subsidiaries.
|
|
* This software may not be used for the construction,
|
|
* development, production, or testing of weapon systems of
|
|
* any kind.
|
|
* This software may not be used for the construction,
|
|
* development, production, or use of plants/installations
|
|
* which include the processing of radioactive/fissionable
|
|
* material.
|
|
*/
|
|
|
|
/*
|
|
* If you use this software at all, I'd love to hear from
|
|
* you. All questions, criticisms, suggestions, praise and
|
|
* postcards are most welcome.
|
|
*
|
|
* email: sbullen@cybergraphic.com.au
|
|
*
|
|
* snail: Simon P. Bullen
|
|
* PO BOX 12138
|
|
* A'Beckett St.
|
|
* Melbourne 3000
|
|
* Australia
|
|
*/
|
|
|
|
#ifdef FORTIFY
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
#include <signal.h>
|
|
|
|
|
|
/* the user's options */
|
|
#include "ufortify.h"
|
|
|
|
|
|
|
|
/* Prototypes and such */
|
|
#define __FORTIFY_C__
|
|
#include "fortify.h"
|
|
|
|
|
|
|
|
#include <pthread.h>
|
|
|
|
// this gives us a mutex that allows the same
|
|
// thread to lock it recursively. Fortify needs this kind of mutex.
|
|
// Who knows what this syntax means? (found in pthread.h)
|
|
pthread_mutex_t mutex = {0, 0, 0, PTHREAD_MUTEX_RECURSIVE_NP, {0, 0}};
|
|
|
|
void Fortify_Lock() {
|
|
pthread_mutex_lock( &mutex );
|
|
}
|
|
|
|
void Fortify_Unlock() {
|
|
pthread_mutex_unlock( &mutex );
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Round x up to the nearest multiple of n.
|
|
*/
|
|
#define ROUND_UP(x, n) ((((x) + (n)-1)/(n))*(n))
|
|
|
|
/*
|
|
* struct Header - this structure is used
|
|
* internally by Fortify to manage it's
|
|
* own private lists of memory.
|
|
*/
|
|
struct Header
|
|
{
|
|
unsigned short Checksum; /* For the integrity of our goodies */
|
|
const char *File; /* The sourcefile of the allocator */
|
|
unsigned long Line; /* The sourceline of the allocator */
|
|
#ifdef FORTIFY_TRACK_DEALLOCATED_MEMORY
|
|
const char *FreedFile; /* The sourcefile of the deallocator */
|
|
unsigned long FreedLine; /* The sourceline of the deallocator */
|
|
unsigned char Deallocator; /* The deallocator used */
|
|
#endif
|
|
size_t Size; /* The size of the malloc'd block */
|
|
struct Header *Prev; /* Previous link */
|
|
struct Header *Next; /* Next link */
|
|
char *Label; /* User's Label (may be null) */
|
|
unsigned char Scope; /* Scope level of the caller */
|
|
unsigned char Allocator; /* malloc/realloc/new/etc */
|
|
};
|
|
|
|
#define FORTIFY_HEADER_SIZE ROUND_UP(sizeof(struct Header), sizeof(unsigned short))
|
|
|
|
|
|
|
|
/*
|
|
* FORTIFY_ALIGNED_BEFORE_SIZE is FORTIFY_BEFORE_SIZE rounded up to the
|
|
* next multiple of FORTIFY_ALIGNMENT. This is so that we can guarantee
|
|
* the alignment of user memory for such systems where this is important
|
|
* (eg storing doubles on a SPARC)
|
|
*/
|
|
#define FORTIFY_ALIGNED_BEFORE_SIZE ( \
|
|
ROUND_UP(FORTIFY_HEADER_SIZE + FORTIFY_BEFORE_SIZE, FORTIFY_ALIGNMENT) \
|
|
- FORTIFY_HEADER_SIZE)
|
|
|
|
/*
|
|
* FORTIFY_OVERHEAD is the total overhead added by Fortify to each
|
|
* memory block.
|
|
*/
|
|
#define FORTIFY_OVERHEAD ( FORTIFY_HEADER_SIZE \
|
|
+ FORTIFY_ALIGNED_BEFORE_SIZE \
|
|
+ FORTIFY_AFTER_SIZE)
|
|
|
|
|
|
/*
|
|
*
|
|
* Static Function Prototypes
|
|
*
|
|
*/
|
|
static int st_CheckBlock(struct Header *h, const char *file, unsigned long line);
|
|
static int st_CheckFortification (unsigned char *ptr, unsigned char value, size_t size);
|
|
static void st_SetFortification (unsigned char *ptr, unsigned char value, size_t size);
|
|
static void st_OutputFortification(unsigned char *ptr, unsigned char value, size_t size);
|
|
static void st_HexDump(unsigned char *ptr, size_t offset, size_t size, int title);
|
|
static int st_IsHeaderValid(struct Header *h);
|
|
static void st_MakeHeaderValid(struct Header *h);
|
|
static unsigned short st_ChecksumHeader(struct Header *h);
|
|
static int st_IsOnAllocatedList(struct Header *h);
|
|
static void st_OutputHeader(struct Header *h);
|
|
static void st_OutputMemory(struct Header *h);
|
|
static void st_OutputLastVerifiedPoint(void);
|
|
static void st_DefaultOutput(const char *String);
|
|
static const char *st_MemoryBlockString(struct Header *h);
|
|
static void st_OutputDeleteTrace();
|
|
|
|
#ifdef FORTIFY_TRACK_DEALLOCATED_MEMORY
|
|
#ifdef FORTIFY_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY
|
|
#ifdef FORTIFY_VERBOSE_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY
|
|
static const char *st_DeallocatedMemoryBlockString(struct Header *h);
|
|
#endif /* FORTIFY_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY */
|
|
#endif /* FORTIFY_VERBOSE_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY */
|
|
static int st_IsOnDeallocatedList(struct Header *h);
|
|
static int st_PurgeDeallocatedBlocks(unsigned long Bytes, const char *file, unsigned long line);
|
|
static int st_PurgeDeallocatedScope(unsigned char Scope, const char *file, unsigned long line);
|
|
static int st_CheckDeallocatedBlock(struct Header *h, const char *file, unsigned long line);
|
|
static void st_FreeDeallocatedBlock(struct Header *h, const char *file, unsigned long line);
|
|
#endif /* FORTIFY_TRACK_DEALLOCATED_MEMORY */
|
|
|
|
|
|
/*
|
|
*
|
|
* Static variables
|
|
*
|
|
*/
|
|
static struct Header *st_AllocatedHead = 0;
|
|
static int st_AllocateFailRate = 0;
|
|
static char st_Buffer[256];
|
|
static Fortify_OutputFuncPtr st_Output = st_DefaultOutput;
|
|
static const char *st_LastVerifiedFile = "unknown";
|
|
static unsigned long st_LastVerifiedLine = 0;
|
|
static unsigned char st_Scope = 0;
|
|
static unsigned char st_Disabled = 0;
|
|
|
|
#ifdef __cplusplus
|
|
int gbl_FortifyMagic = 0;
|
|
static const char *st_DeleteFile[FORTIFY_DELETE_STACK_SIZE];
|
|
static unsigned long st_DeleteLine[FORTIFY_DELETE_STACK_SIZE];
|
|
static unsigned long st_DeleteStackTop;
|
|
#endif /* __cplusplus */
|
|
|
|
/* statistics */
|
|
static unsigned long st_MaxBlocks = 0;
|
|
static unsigned long st_MaxAllocation = 0;
|
|
static unsigned long st_CurBlocks = 0;
|
|
static unsigned long st_CurAllocation = 0;
|
|
static unsigned long st_Allocations = 0;
|
|
static unsigned long st_Frees = 0;
|
|
static unsigned long st_TotalAllocation = 0;
|
|
static unsigned long st_AllocationLimit = 0xffffffff;
|
|
|
|
#ifdef FORTIFY_TRACK_DEALLOCATED_MEMORY
|
|
static struct Header *st_DeallocatedHead = 0;
|
|
static struct Header *st_DeallocatedTail = 0;
|
|
static unsigned long st_TotalDeallocated = 0;
|
|
#endif
|
|
|
|
|
|
/* allocators */
|
|
static const char *st_AllocatorName[] =
|
|
{
|
|
"malloc()",
|
|
"calloc()",
|
|
"realloc()",
|
|
"strdup()",
|
|
"new",
|
|
"new[]"
|
|
};
|
|
|
|
/* deallocators */
|
|
static const char *st_DeallocatorName[] =
|
|
{
|
|
"nobody",
|
|
"free()",
|
|
"realloc()",
|
|
"delete",
|
|
"delete[]"
|
|
};
|
|
|
|
static const unsigned char st_ValidDeallocator[] =
|
|
{
|
|
(1<<Fortify_Deallocator_free) | (1<<Fortify_Deallocator_realloc),
|
|
(1<<Fortify_Deallocator_free) | (1<<Fortify_Deallocator_realloc),
|
|
(1<<Fortify_Deallocator_free) | (1<<Fortify_Deallocator_realloc),
|
|
(1<<Fortify_Deallocator_free) | (1<<Fortify_Deallocator_realloc) |
|
|
(1<<Fortify_Deallocator_array_delete),
|
|
#if defined(FORTIFY_PROVIDE_ARRAY_NEW) && defined(FORTIFY_PROVIDE_ARRAY_DELETE)
|
|
(1<<Fortify_Deallocator_delete),
|
|
(1<<Fortify_Deallocator_array_delete)
|
|
#else
|
|
(1<<Fortify_Deallocator_delete) | (1<<Fortify_Deallocator_array_delete),
|
|
(1<<Fortify_Deallocator_delete) | (1<<Fortify_Deallocator_array_delete)
|
|
#endif
|
|
};
|
|
|
|
|
|
/*
|
|
* Fortify_Allocate() - allocate a block of fortified memory
|
|
*/
|
|
void *FORTIFY_STORAGE
|
|
Fortify_Allocate(size_t size, unsigned char allocator, const char *file, unsigned long line)
|
|
{
|
|
unsigned char *ptr;
|
|
struct Header *h;
|
|
int another_try;
|
|
|
|
/*
|
|
* If Fortify has been disabled, then it's easy
|
|
*/
|
|
if(st_Disabled)
|
|
{
|
|
#ifdef FORTIFY_FAIL_ON_ZERO_MALLOC
|
|
if(size == 0 && (allocator == Fortify_Allocator_new
|
|
|| allocator == Fortify_Allocator_array_new))
|
|
{
|
|
/*
|
|
* A new of zero bytes must succeed, but a malloc of
|
|
* zero bytes probably won't
|
|
*/
|
|
return malloc(1);
|
|
}
|
|
#endif
|
|
|
|
return malloc(size);
|
|
}
|
|
|
|
#ifdef FORTIFY_CHECK_ALL_MEMORY_ON_ALLOCATE
|
|
Fortify_CheckAllMemory(file, line);
|
|
#endif
|
|
|
|
if(st_AllocateFailRate > 0)
|
|
{
|
|
if(rand() % 100 < st_AllocateFailRate)
|
|
{
|
|
#ifdef FORTIFY_WARN_ON_FALSE_FAIL
|
|
sprintf(st_Buffer,
|
|
"\nFortify: A \"%s\" of %lu bytes \"false failed\" at %s.%lu\n",
|
|
st_AllocatorName[allocator], (unsigned long)size, file, line);
|
|
st_Output(st_Buffer);
|
|
#endif
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
/* Check to see if this allocation will
|
|
* push us over the artificial limit
|
|
*/
|
|
if(st_CurAllocation + size > st_AllocationLimit)
|
|
{
|
|
#ifdef FORTIFY_WARN_ON_FALSE_FAIL
|
|
sprintf(st_Buffer,
|
|
"\nFortify: A \"%s\" of %lu bytes \"false failed\" at %s.%lu\n",
|
|
st_AllocatorName[allocator], (unsigned long)size, file, line);
|
|
st_Output(st_Buffer);
|
|
#endif
|
|
return(0);
|
|
}
|
|
|
|
#ifdef FORTIFY_WARN_ON_ZERO_MALLOC
|
|
if(size == 0 && (allocator == Fortify_Allocator_malloc ||
|
|
allocator == Fortify_Allocator_calloc ||
|
|
allocator == Fortify_Allocator_realloc ))
|
|
{
|
|
sprintf(st_Buffer,
|
|
"\nFortify: A \"%s\" of 0 bytes attempted at %s.%lu\n",
|
|
st_AllocatorName[allocator], file, line);
|
|
st_Output(st_Buffer);
|
|
}
|
|
#endif /* FORTIFY_WARN_ON_ZERO_MALLOC */
|
|
|
|
#ifdef FORTIFY_FAIL_ON_ZERO_MALLOC
|
|
if(size == 0 && (allocator == Fortify_Allocator_malloc ||
|
|
allocator == Fortify_Allocator_calloc ||
|
|
allocator == Fortify_Allocator_realloc ))
|
|
{
|
|
#ifdef FORTIFY_WARN_ON_ALLOCATE_FAIL
|
|
sprintf(st_Buffer, "\nFortify: A \"%s\" of %lu bytes failed at %s.%lu\n",
|
|
st_AllocatorName[allocator], (unsigned long)size, file, line);
|
|
st_Output(st_Buffer);
|
|
#endif /* FORTIFY_WARN_ON_ALLOCATE_FAIL */
|
|
return 0;
|
|
}
|
|
#endif /* FORTIFY_FAIL_ON_ZERO_MALLOC */
|
|
|
|
#ifdef FORTIFY_WARN_ON_SIZE_T_OVERFLOW
|
|
/*
|
|
* Ensure the size of the memory block
|
|
* plus the overhead isn't bigger than
|
|
* size_t (that'd be a drag)
|
|
*/
|
|
{
|
|
size_t private_size = FORTIFY_HEADER_SIZE
|
|
+ FORTIFY_ALIGNED_BEFORE_SIZE + size + FORTIFY_AFTER_SIZE;
|
|
|
|
if(private_size < size)
|
|
{
|
|
sprintf(st_Buffer,
|
|
"\nFortify: A \"%s\" of %lu bytes has overflowed size_t at %s.%lu\n",
|
|
st_AllocatorName[allocator], (unsigned long)size, file, line);
|
|
st_Output(st_Buffer);
|
|
return(0);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
another_try = 1;
|
|
do
|
|
{
|
|
/*
|
|
* malloc the memory, including the space
|
|
* for the header and fortification buffers
|
|
*/
|
|
ptr = (unsigned char *)malloc( FORTIFY_HEADER_SIZE
|
|
+ FORTIFY_ALIGNED_BEFORE_SIZE
|
|
+ size
|
|
+ FORTIFY_AFTER_SIZE );
|
|
|
|
#ifdef FORTIFY_TRACK_DEALLOCATED_MEMORY
|
|
/*
|
|
* If we're tracking deallocated memory, then
|
|
* we can free some of it, rather than let
|
|
* this malloc fail
|
|
*/
|
|
if(!ptr)
|
|
{
|
|
another_try = st_PurgeDeallocatedBlocks(size, file, line);
|
|
}
|
|
#endif /* FORTIFY_TRACK_DEALLOCATED_MEMORY */
|
|
|
|
}
|
|
while(!ptr && another_try);
|
|
|
|
if(!ptr)
|
|
{
|
|
#ifdef FORTIFY_WARN_ON_ALLOCATE_FAIL
|
|
sprintf(st_Buffer, "\nFortify: A \"%s\" of %lu bytes failed at %s.%lu\n",
|
|
st_AllocatorName[allocator], (unsigned long)size, file, line);
|
|
st_Output(st_Buffer);
|
|
#endif
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* Begin Critical Region
|
|
*/
|
|
FORTIFY_LOCK();
|
|
|
|
|
|
/*
|
|
* Make the head's prev pointer point to us
|
|
* ('cos we're about to become the head)
|
|
*/
|
|
if(st_AllocatedHead)
|
|
{
|
|
st_CheckBlock(st_AllocatedHead, file, line);
|
|
/* what should we do if this fails? (apart from panic) */
|
|
|
|
st_AllocatedHead->Prev = (struct Header *)ptr;
|
|
st_MakeHeaderValid(st_AllocatedHead);
|
|
}
|
|
|
|
/*
|
|
* Initialize and validate the header
|
|
*/
|
|
h = (struct Header *)ptr;
|
|
h->Size = size;
|
|
h->File = file;
|
|
h->Line = line;
|
|
h->Next = st_AllocatedHead;
|
|
h->Prev = 0;
|
|
h->Scope = st_Scope;
|
|
h->Allocator = allocator;
|
|
h->Label = 0;
|
|
#ifdef FORTIFY_TRACK_DEALLOCATED_MEMORY
|
|
h->FreedFile = 0;
|
|
h->FreedLine = 0;
|
|
h->Deallocator = Fortify_Deallocator_nobody;
|
|
#endif /* FORTIFY_TRACK_DEALLOCATED_MEMORY */
|
|
st_MakeHeaderValid(h);
|
|
st_AllocatedHead = h;
|
|
|
|
/*
|
|
* Initialize the fortifications
|
|
*/
|
|
st_SetFortification(ptr + FORTIFY_HEADER_SIZE,
|
|
FORTIFY_BEFORE_VALUE, FORTIFY_ALIGNED_BEFORE_SIZE);
|
|
st_SetFortification(ptr + FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE + size,
|
|
FORTIFY_AFTER_VALUE, FORTIFY_AFTER_SIZE);
|
|
|
|
#ifdef FORTIFY_FILL_ON_ALLOCATE
|
|
/*
|
|
* Fill the actual user memory
|
|
*/
|
|
st_SetFortification(ptr + FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE,
|
|
FORTIFY_FILL_ON_ALLOCATE_VALUE, size);
|
|
#endif
|
|
|
|
/*
|
|
* End Critical Region
|
|
*/
|
|
FORTIFY_UNLOCK();
|
|
|
|
|
|
/*
|
|
* update the statistics
|
|
*/
|
|
st_TotalAllocation += size;
|
|
st_Allocations++;
|
|
st_CurBlocks++;
|
|
st_CurAllocation += size;
|
|
if(st_CurBlocks > st_MaxBlocks)
|
|
st_MaxBlocks = st_CurBlocks;
|
|
if(st_CurAllocation > st_MaxAllocation)
|
|
st_MaxAllocation = st_CurAllocation;
|
|
|
|
/*
|
|
* We return the address of the user's memory, not the start of the block,
|
|
* which points to our magic cookies
|
|
*/
|
|
return(ptr + FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Fortify_Deallocate() - Free a block of memory allocated with Fortify_Allocate()
|
|
*/
|
|
void FORTIFY_STORAGE
|
|
Fortify_Deallocate(void *uptr, unsigned char deallocator, const char *file, unsigned long line)
|
|
{
|
|
unsigned char *ptr = (unsigned char *)uptr
|
|
- FORTIFY_HEADER_SIZE
|
|
- FORTIFY_ALIGNED_BEFORE_SIZE;
|
|
struct Header *h = (struct Header *)ptr;
|
|
|
|
#ifdef FORTIFY_CHECK_ALL_MEMORY_ON_DEALLOCATE
|
|
Fortify_CheckAllMemory(file, line);
|
|
#endif
|
|
|
|
/*
|
|
* If Fortify has been disabled, then it's easy
|
|
* (well, almost)
|
|
*/
|
|
if(st_Disabled)
|
|
{
|
|
/* there is a possibility that this memory
|
|
* block was allocated when Fortify was
|
|
* enabled, so we must check the Allocated
|
|
* list before we free it.
|
|
*/
|
|
if(!st_IsOnAllocatedList(h))
|
|
{
|
|
free(uptr);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
/* the block was allocated by Fortify, so we
|
|
* gotta free it differently.
|
|
*/
|
|
/*
|
|
* Begin critical region
|
|
*/
|
|
FORTIFY_LOCK();
|
|
|
|
/*
|
|
* Remove the block from the list
|
|
*/
|
|
if(h->Prev)
|
|
h->Prev->Next = h->Next;
|
|
else
|
|
st_AllocatedHead = h->Next;
|
|
|
|
if(h->Next)
|
|
h->Next->Prev = h->Prev;
|
|
|
|
/*
|
|
* End Critical Region
|
|
*/
|
|
FORTIFY_UNLOCK();
|
|
|
|
/*
|
|
* actually free the memory
|
|
*/
|
|
free(ptr);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef FORTIFY_PARANOID_DEALLOCATE
|
|
if(!st_IsOnAllocatedList(h))
|
|
{
|
|
#ifdef FORTIFY_TRACK_DEALLOCATED_MEMORY
|
|
if(st_IsOnDeallocatedList(h))
|
|
{
|
|
sprintf(st_Buffer, "\nFortify: \"%s\" twice of %s detected at %s.%lu\n",
|
|
st_DeallocatorName[deallocator],
|
|
st_MemoryBlockString(h), file, line);
|
|
st_Output(st_Buffer);
|
|
|
|
sprintf(st_Buffer, " Memory block was deallocated by \"%s\" at %s.%lu\n",
|
|
st_DeallocatorName[h->Deallocator], h->FreedFile, h->FreedLine);
|
|
st_Output(st_Buffer);
|
|
st_OutputDeleteTrace();
|
|
return;
|
|
}
|
|
#endif /* FORTIFY_TRACK_DEALLOCATED_MEMORY */
|
|
|
|
#ifdef FORTIFY_NO_PERCENT_P
|
|
sprintf(st_Buffer, "\nFortify: Possible \"%s\" twice of (0x%08lx) was detected at %s.%lu\n",
|
|
#else
|
|
sprintf(st_Buffer, "\nFortify: Possible \"%s\" twice of (%p) was detected at %s.%lu\n",
|
|
#endif
|
|
st_DeallocatorName[deallocator],
|
|
uptr, file, line);
|
|
st_Output(st_Buffer);
|
|
st_OutputDeleteTrace();
|
|
return;
|
|
}
|
|
#endif /* FORTIFY_PARANOID_DELETE */
|
|
|
|
/*
|
|
* Make sure the block is okay before we free it.
|
|
* If it's not okay, don't free it - it might not
|
|
* be a real memory block. Or worse still, someone
|
|
* might still be writing to it
|
|
*/
|
|
if(!st_CheckBlock(h, file, line))
|
|
{
|
|
st_OutputDeleteTrace();
|
|
return;
|
|
}
|
|
|
|
#ifdef FORTIFY_TRACK_DEALLOCATED_MEMORY
|
|
/*
|
|
* Make sure the block hasn't been freed already
|
|
* (we can get to here if FORTIFY_PARANOID_DELETE
|
|
* is off, but FORTIFY_TRACK_DEALLOCATED_MEMORY
|
|
* is on).
|
|
*/
|
|
if(h->Deallocator != Fortify_Deallocator_nobody)
|
|
{
|
|
sprintf(st_Buffer, "\nFortify: \"%s\" twice of %s detected at %s.%lu\n",
|
|
st_DeallocatorName[deallocator],
|
|
st_MemoryBlockString(h), file, line);
|
|
st_Output(st_Buffer);
|
|
|
|
sprintf(st_Buffer, " Memory block was deallocated by \"%s\" at %s.%lu\n",
|
|
st_DeallocatorName[h->Deallocator], h->FreedFile, h->FreedLine);
|
|
st_Output(st_Buffer);
|
|
st_OutputDeleteTrace();
|
|
return;
|
|
}
|
|
#endif /* FORTIFY_TRACK_DEALLOCATED_MEMORY */
|
|
|
|
/*
|
|
* Make sure the block is being freed with a valid
|
|
* deallocator. If not, complain. (but free it anyway)
|
|
*/
|
|
if((st_ValidDeallocator[h->Allocator] & (1<<deallocator)) == 0)
|
|
{
|
|
sprintf(st_Buffer, "\nFortify: Incorrect deallocator \"%s\" detected at %s.%lu\n",
|
|
st_DeallocatorName[deallocator], file, line);
|
|
st_Output(st_Buffer);
|
|
sprintf(st_Buffer, " %s was allocated with \"%s\"\n",
|
|
st_MemoryBlockString(h), st_AllocatorName[h->Allocator]);
|
|
st_Output(st_Buffer);
|
|
st_OutputDeleteTrace();
|
|
}
|
|
|
|
/*
|
|
* Begin critical region
|
|
*/
|
|
FORTIFY_LOCK();
|
|
|
|
/*
|
|
* Remove the block from the list
|
|
*/
|
|
if(h->Prev)
|
|
{
|
|
if(!st_CheckBlock(h->Prev, file, line))
|
|
{
|
|
FORTIFY_UNLOCK();
|
|
st_OutputDeleteTrace();
|
|
return;
|
|
}
|
|
|
|
h->Prev->Next = h->Next;
|
|
st_MakeHeaderValid(h->Prev);
|
|
}
|
|
else
|
|
st_AllocatedHead = h->Next;
|
|
|
|
if(h->Next)
|
|
{
|
|
if(!st_CheckBlock(h->Next, file, line))
|
|
{
|
|
FORTIFY_UNLOCK();
|
|
st_OutputDeleteTrace();
|
|
return;
|
|
}
|
|
|
|
h->Next->Prev = h->Prev;
|
|
st_MakeHeaderValid(h->Next);
|
|
}
|
|
|
|
/*
|
|
* End Critical Region
|
|
*/
|
|
FORTIFY_UNLOCK();
|
|
|
|
/*
|
|
* update the statistics
|
|
*/
|
|
st_Frees++;
|
|
st_CurBlocks--;
|
|
st_CurAllocation -= h->Size;
|
|
|
|
|
|
#ifdef FORTIFY_TRACK_DEALLOCATED_MEMORY
|
|
if(st_Scope > 0)
|
|
{
|
|
/*
|
|
* Don't _actually_ free the memory block, just yet.
|
|
* Place it onto the deallocated list, instead, so
|
|
* we can check later to see if it's been written to.
|
|
*/
|
|
#ifdef FORTIFY_FILL_ON_DEALLOCATE
|
|
/*
|
|
* Nuke out all user memory that is about to be freed
|
|
*/
|
|
st_SetFortification(ptr + FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE,
|
|
FORTIFY_FILL_ON_DEALLOCATE_VALUE,
|
|
h->Size);
|
|
#endif /* FORTIFY_FILL_ON_DEALLOCATE */
|
|
|
|
/*
|
|
* Begin critical region
|
|
*/
|
|
FORTIFY_LOCK();
|
|
|
|
/*
|
|
* Place the block on the deallocated list
|
|
*/
|
|
if(st_DeallocatedHead)
|
|
{
|
|
st_DeallocatedHead->Prev = (struct Header *)ptr;
|
|
st_MakeHeaderValid(st_DeallocatedHead);
|
|
}
|
|
|
|
h = (struct Header *)ptr;
|
|
h->FreedFile = file;
|
|
h->FreedLine = line;
|
|
h->Deallocator = deallocator;
|
|
h->Next = st_DeallocatedHead;
|
|
h->Prev = 0;
|
|
st_MakeHeaderValid(h);
|
|
st_DeallocatedHead = h;
|
|
|
|
if(!st_DeallocatedTail)
|
|
{
|
|
st_DeallocatedTail = h;
|
|
}
|
|
|
|
st_TotalDeallocated += h->Size;
|
|
|
|
#ifdef FORTIFY_DEALLOCATED_MEMORY_LIMIT
|
|
/*
|
|
* If we've got too much on the deallocated list; free some
|
|
*/
|
|
if(st_TotalDeallocated > FORTIFY_DEALLOCATED_MEMORY_LIMIT)
|
|
{
|
|
st_PurgeDeallocatedBlocks(st_TotalDeallocated - FORTIFY_DEALLOCATED_MEMORY_LIMIT, file, line);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* End critical region
|
|
*/
|
|
FORTIFY_UNLOCK();
|
|
}
|
|
else
|
|
#endif /* FORTIFY_TRACK_DEALLOCATED_MEMORY */
|
|
{
|
|
/*
|
|
* Free the User Label
|
|
*/
|
|
if(h->Label)
|
|
{
|
|
free(h->Label);
|
|
}
|
|
|
|
#ifdef FORTIFY_FILL_ON_DEALLOCATE
|
|
/*
|
|
* Nuke out all memory that is about to be freed, including the header
|
|
*/
|
|
st_SetFortification(ptr, FORTIFY_FILL_ON_DEALLOCATE_VALUE,
|
|
FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE + h->Size + FORTIFY_AFTER_SIZE);
|
|
#endif /* FORTIFY_FILL_ON_DEALLOCATE */
|
|
|
|
/*
|
|
* And do the actual free
|
|
*/
|
|
free(ptr);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Fortify_LabelPointer() - Labels the memory block
|
|
* with a string provided by the user. This function
|
|
* takes a copy of the passed in string.
|
|
* The pointer MUST be one returned by a Fortify
|
|
* allocation function.
|
|
*/
|
|
void
|
|
Fortify_LabelPointer(void *uptr, const char *label, const char *file, unsigned long line)
|
|
{
|
|
if(!st_Disabled)
|
|
{
|
|
unsigned char *ptr = (unsigned char *)uptr
|
|
- FORTIFY_HEADER_SIZE - FORTIFY_ALIGNED_BEFORE_SIZE;
|
|
struct Header *h = (struct Header *)ptr;
|
|
|
|
/* make sure the pointer is okay */
|
|
Fortify_CheckPointer(uptr, file, line);
|
|
|
|
/* free the previous label */
|
|
if(h->Label)
|
|
{
|
|
free(h->Label);
|
|
}
|
|
|
|
/* make sure the label is sensible */
|
|
assert(label);
|
|
|
|
/* copy it in */
|
|
h->Label = (char*)malloc(strlen(label)+1);
|
|
strcpy(h->Label, label);
|
|
|
|
/* update the checksum */
|
|
st_MakeHeaderValid(h);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Fortify_CheckPointer() - Returns true if the uptr
|
|
* points to a valid piece of Fortify_Allocated()'d
|
|
* memory. The memory must be on the allocated list,
|
|
* and it's fortifications must be intact.
|
|
* Always returns TRUE if Fortify is disabled.
|
|
*/
|
|
int FORTIFY_STORAGE
|
|
Fortify_CheckPointer(void *uptr, const char *file, unsigned long line)
|
|
{
|
|
unsigned char *ptr = (unsigned char *)uptr
|
|
- FORTIFY_HEADER_SIZE - FORTIFY_ALIGNED_BEFORE_SIZE;
|
|
struct Header *h = (struct Header *)ptr;
|
|
int r;
|
|
|
|
if(st_Disabled)
|
|
return 1;
|
|
|
|
FORTIFY_LOCK();
|
|
|
|
if(!st_IsOnAllocatedList(h))
|
|
{
|
|
#ifdef FORTIFY_NO_PERCENT_P
|
|
sprintf(st_Buffer, "\nFortify: Invalid pointer (0x%08lx) detected at %s.%lu\n",
|
|
#else
|
|
sprintf(st_Buffer, "\nFortify: Invalid pointer (%p) detected at %s.%lu\n",
|
|
#endif
|
|
uptr, file, line);
|
|
st_Output(st_Buffer);
|
|
FORTIFY_UNLOCK();
|
|
|
|
// this should seg fault...
|
|
int *x = (int *)2;
|
|
*x = 0;
|
|
return(0);
|
|
}
|
|
|
|
#ifdef FORTIFY_TRACK_DEALLOCATED_MEMORY
|
|
if(st_IsOnDeallocatedList(h))
|
|
{
|
|
#ifdef FORTIFY_NO_PERCENT_P
|
|
sprintf(st_Buffer, "\nFortify: Deallocated pointer (0x%08lx) detected at %s.%lu\n",
|
|
#else
|
|
sprintf(st_Buffer, "\nFortify: Deallocated pointer (%p) detected at %s.%lu\n",
|
|
#endif
|
|
uptr, file, line);
|
|
st_Output(st_Buffer);
|
|
sprintf(st_Buffer, " Memory block was deallocated by \"%s\" at %s.%lu\n",
|
|
st_DeallocatorName[h->Deallocator], h->FreedFile, h->FreedLine);
|
|
st_Output(st_Buffer);
|
|
FORTIFY_UNLOCK();
|
|
return(0);
|
|
}
|
|
#endif
|
|
|
|
r = st_CheckBlock(h, file, line);
|
|
FORTIFY_UNLOCK();
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* Fortify_SetOutputFunc(Fortify_OutputFuncPtr Output) -
|
|
* Sets the function used to output all error and
|
|
* diagnostic messages. The output function takes
|
|
* a single const unsigned char * argument, and must be
|
|
* able to handle newlines. This function returns the
|
|
* old output function.
|
|
*/
|
|
Fortify_OutputFuncPtr FORTIFY_STORAGE
|
|
Fortify_SetOutputFunc(Fortify_OutputFuncPtr Output)
|
|
{
|
|
Fortify_OutputFuncPtr Old = st_Output;
|
|
|
|
st_Output = Output;
|
|
|
|
return(Old);
|
|
}
|
|
|
|
/*
|
|
* Fortify_SetAllocateFailRate(int Percent) -
|
|
* Fortify_Allocate() will "fail" this Percent of
|
|
* the time, even if the memory is available.
|
|
* Useful to "stress-test" an application.
|
|
* Returns the old value.
|
|
* The fail rate defaults to 0 (a good default I think).
|
|
*/
|
|
int FORTIFY_STORAGE
|
|
Fortify_SetAllocateFailRate(int Percent)
|
|
{
|
|
int Old = st_AllocateFailRate;
|
|
|
|
st_AllocateFailRate = Percent;
|
|
|
|
return(Old);
|
|
}
|
|
|
|
|
|
/*
|
|
* Fortify_CheckAllMemory() - Checks the fortifications
|
|
* of all memory on the allocated list. And, if
|
|
* FORTIFY_DEALLOCATED_MEMORY is enabled, all the
|
|
* known deallocated memory as well.
|
|
* Returns the number of blocks that failed.
|
|
* Always returns 0 if Fortify is disabled.
|
|
*/
|
|
unsigned long FORTIFY_STORAGE
|
|
Fortify_CheckAllMemory(const char *file, unsigned long line)
|
|
{
|
|
struct Header *curr = st_AllocatedHead;
|
|
unsigned long count = 0;
|
|
|
|
if(st_Disabled)
|
|
return 0;
|
|
|
|
FORTIFY_LOCK();
|
|
|
|
/*
|
|
* Check the allocated memory
|
|
*/
|
|
while(curr)
|
|
{
|
|
if(!st_CheckBlock(curr, file, line))
|
|
count++;
|
|
|
|
curr = curr->Next;
|
|
}
|
|
|
|
/*
|
|
* Check the deallocated memory while you're at it
|
|
*/
|
|
#ifdef FORTIFY_TRACK_DEALLOCATED_MEMORY
|
|
curr = st_DeallocatedHead;
|
|
while(curr)
|
|
{
|
|
if(!st_CheckDeallocatedBlock(curr, file, line))
|
|
count++;
|
|
|
|
curr = curr->Next;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* If we know where we are, and everything is cool,
|
|
* remember that. It might be important.
|
|
*/
|
|
if(file && count == 0)
|
|
{
|
|
st_LastVerifiedFile = file;
|
|
st_LastVerifiedLine = line;
|
|
}
|
|
|
|
FORTIFY_UNLOCK();
|
|
return(count);
|
|
}
|
|
|
|
|
|
/*
|
|
* Fortify_EnterScope() - enters a new Fortify scope
|
|
* level. Returns the new scope level.
|
|
*/
|
|
unsigned char FORTIFY_STORAGE
|
|
Fortify_EnterScope(const char *file, unsigned long line)
|
|
{
|
|
return(++st_Scope);
|
|
}
|
|
|
|
/* Fortify_LeaveScope - leaves a Fortify scope level,
|
|
* also prints a memory dump of all non-freed memory
|
|
* that was allocated during the scope being exited.
|
|
* Does nothing and returns 0 if Fortify is disabled.
|
|
*/
|
|
unsigned char FORTIFY_STORAGE
|
|
Fortify_LeaveScope(const char *file, unsigned long line)
|
|
{
|
|
struct Header *curr = st_AllocatedHead;
|
|
unsigned long size = 0, count = 0;
|
|
|
|
if(st_Disabled)
|
|
return 0;
|
|
|
|
FORTIFY_LOCK();
|
|
|
|
st_Scope--;
|
|
while(curr)
|
|
{
|
|
if(curr->Scope > st_Scope)
|
|
{
|
|
if(count == 0)
|
|
{
|
|
sprintf(st_Buffer, "\nFortify: Memory leak detected leaving scope at %s.%lu\n", file, line);
|
|
st_Output(st_Buffer);
|
|
sprintf(st_Buffer, "%10s %8s %s\n", "Address", "Size", "Allocator");
|
|
st_Output(st_Buffer);
|
|
}
|
|
|
|
st_OutputHeader(curr);
|
|
count++;
|
|
size += curr->Size;
|
|
}
|
|
|
|
curr = curr->Next;
|
|
}
|
|
|
|
if(count)
|
|
{
|
|
sprintf(st_Buffer,"%10s %8lu bytes in %lu blocks with %lu bytes overhead\n",
|
|
"total", size, count, count * FORTIFY_OVERHEAD);
|
|
st_Output(st_Buffer);
|
|
}
|
|
|
|
#ifdef FORTIFY_TRACK_DEALLOCATED_MEMORY
|
|
/*
|
|
* Quietly free all the deallocated memory
|
|
* that was allocated in this scope that
|
|
* we are still tracking
|
|
*/
|
|
st_PurgeDeallocatedScope( st_Scope, file, line );
|
|
#endif /* FORTIFY_TRACK_DEALLOCATED_MEMORY */
|
|
|
|
FORTIFY_UNLOCK();
|
|
return(st_Scope);
|
|
}
|
|
|
|
/*
|
|
* Fortify_ListAllMemory() - Outputs the entire
|
|
* list of currently allocated memory. For each block
|
|
* is output it's Address, Size, and the SourceFile and
|
|
* Line that allocated it.
|
|
*
|
|
* If there is no memory on the list, this function
|
|
* outputs nothing.
|
|
*
|
|
* It returns the number of blocks on the list, unless
|
|
* Fortify has been disabled, in which case it always
|
|
* returns 0.
|
|
*/
|
|
unsigned long FORTIFY_STORAGE
|
|
Fortify_ListAllMemory(const char *file, unsigned long line)
|
|
{
|
|
struct Header *curr = st_AllocatedHead;
|
|
unsigned long size = 0, count = 0;
|
|
|
|
if(st_Disabled)
|
|
return 0;
|
|
|
|
Fortify_CheckAllMemory(file, line);
|
|
|
|
FORTIFY_LOCK();
|
|
|
|
if(curr)
|
|
{
|
|
sprintf(st_Buffer, "\nFortify: Memory List at %s.%lu\n", file, line);
|
|
st_Output(st_Buffer);
|
|
sprintf(st_Buffer, "%10s %8s %s\n", "Address", "Size", "Allocator");
|
|
st_Output(st_Buffer);
|
|
|
|
while(curr)
|
|
{
|
|
st_OutputHeader(curr);
|
|
count++;
|
|
size += curr->Size;
|
|
curr = curr->Next;
|
|
}
|
|
|
|
sprintf(st_Buffer, "%10s %8lu bytes in %lu blocks and %lu bytes overhead\n",
|
|
"total", size, count, count * FORTIFY_OVERHEAD);
|
|
st_Output(st_Buffer);
|
|
}
|
|
|
|
FORTIFY_UNLOCK();
|
|
return(count);
|
|
}
|
|
|
|
/*
|
|
* Fortify_DumpAllMemory() - Outputs the entire list of
|
|
* currently allocated memory. For each allocated block
|
|
* is output it's Address, Size, the SourceFile and Line
|
|
* that allocated it, a hex dump of the contents of the
|
|
* memory and an ascii dump of printable characters.
|
|
*
|
|
* If there is no memory on the list, this function outputs nothing.
|
|
*/
|
|
unsigned long FORTIFY_STORAGE
|
|
Fortify_DumpAllMemory(const char *file, unsigned long line)
|
|
{
|
|
struct Header *curr = st_AllocatedHead;
|
|
unsigned long count = 0;
|
|
|
|
if(st_Disabled)
|
|
return 0;
|
|
|
|
Fortify_CheckAllMemory(file, line);
|
|
|
|
FORTIFY_LOCK();
|
|
|
|
while(curr)
|
|
{
|
|
sprintf(st_Buffer, "\nFortify: Hex Dump of %s at %s.%lu\n",
|
|
st_MemoryBlockString(curr), file, line);
|
|
st_Output(st_Buffer);
|
|
st_OutputMemory(curr);
|
|
st_Output("\n");
|
|
count++;
|
|
|
|
curr = curr->Next;
|
|
}
|
|
|
|
FORTIFY_UNLOCK();
|
|
return(count);
|
|
}
|
|
|
|
/* Fortify_OutputStatistics() - displays statistics
|
|
* about the maximum amount of memory that was
|
|
* allocated at any one time.
|
|
*/
|
|
void FORTIFY_STORAGE
|
|
Fortify_OutputStatistics(const char *file, unsigned long line)
|
|
{
|
|
if(st_Disabled)
|
|
return;
|
|
|
|
sprintf(st_Buffer, "\nFortify: Statistics at %s.%lu\n", file, line);
|
|
st_Output(st_Buffer);
|
|
|
|
sprintf(st_Buffer, " Memory currently allocated: %lu bytes in %lu blocks\n",
|
|
st_CurAllocation, st_CurBlocks);
|
|
st_Output(st_Buffer);
|
|
sprintf(st_Buffer, " Maximum memory allocated at one time: %lu bytes in %lu blocks\n",
|
|
st_MaxAllocation, st_MaxBlocks);
|
|
st_Output(st_Buffer);
|
|
sprintf(st_Buffer, " There have been %lu allocations and %lu deallocations\n",
|
|
st_Allocations, st_Frees);
|
|
st_Output(st_Buffer);
|
|
sprintf(st_Buffer, " There was a total of %lu bytes allocated\n",
|
|
st_TotalAllocation);
|
|
st_Output(st_Buffer);
|
|
|
|
if(st_Allocations > 0)
|
|
{
|
|
sprintf(st_Buffer, " The average allocation was %lu bytes\n",
|
|
st_TotalAllocation / st_Allocations);
|
|
st_Output(st_Buffer);
|
|
}
|
|
}
|
|
|
|
/* Fortify_GetCurrentAllocation() - returns the number of
|
|
* bytes currently allocated.
|
|
*/
|
|
unsigned long FORTIFY_STORAGE
|
|
Fortify_GetCurrentAllocation(const char *file, unsigned long line)
|
|
{
|
|
if(st_Disabled)
|
|
return 0;
|
|
|
|
return st_CurAllocation;
|
|
}
|
|
|
|
/* Fortify_SetAllocationLimit() - set a limit on the total
|
|
* amount of memory allowed for this application.
|
|
*/
|
|
void FORTIFY_STORAGE
|
|
Fortify_SetAllocationLimit(unsigned long NewLimit, const char *file, unsigned long line)
|
|
{
|
|
st_AllocationLimit = NewLimit;
|
|
}
|
|
|
|
/*
|
|
* Fortify_Disable() - Run time method of disabling Fortify.
|
|
* Useful if you need to turn off Fortify without recompiling
|
|
* everything. Not as effective as compiling out, of course.
|
|
* The less memory allocated by Fortify when it is disabled
|
|
* the better.
|
|
* (Previous versions of Fortify did not allow it to be
|
|
* disabled if there was any memory allocated at the time,
|
|
* but since in C++ memory is often allocated before main
|
|
* is even entered, this was useless so Fortify is now
|
|
* able to cope).
|
|
*/
|
|
void FORTIFY_STORAGE
|
|
Fortify_Disable(const char *file, unsigned long line)
|
|
{
|
|
#ifdef FORTIFY_TRACK_DEALLOCATED_MEMORY
|
|
/* free all deallocated memory we might be tracking */
|
|
st_PurgeDeallocatedScope( 0, file, line );
|
|
#endif /* FORTIFY_TRACK_DEALLOCATED_MEMORY */
|
|
|
|
st_Disabled = 1;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* st_CheckBlock - Check a block's header and fortifications.
|
|
* Returns true if the block is happy.
|
|
*/
|
|
static int
|
|
st_CheckBlock(struct Header *h, const char *file, unsigned long line)
|
|
{
|
|
unsigned char *ptr = (unsigned char *)h;
|
|
int result = 1;
|
|
|
|
if(!st_IsHeaderValid(h))
|
|
{
|
|
sprintf(st_Buffer,
|
|
#ifdef FORTIFY_NO_PERCENT_P
|
|
"\nFortify: Invalid pointer (0x%08lx) or corrupted header detected at %s.%lu\n",
|
|
#else
|
|
"\nFortify: Invalid pointer (%p) or corrupted header detected at %s.%lu\n",
|
|
#endif
|
|
ptr + FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE, file, line);
|
|
st_Output(st_Buffer);
|
|
st_OutputLastVerifiedPoint();
|
|
|
|
// this should seg fault...
|
|
int *x = (int *)2;
|
|
*x = 0;
|
|
|
|
return(0);
|
|
}
|
|
|
|
if(!st_CheckFortification(ptr + FORTIFY_HEADER_SIZE,
|
|
FORTIFY_BEFORE_VALUE, FORTIFY_ALIGNED_BEFORE_SIZE))
|
|
{
|
|
sprintf(st_Buffer, "\nFortify: Underwrite detected before block %s at %s.%lu\n",
|
|
st_MemoryBlockString(h), file, line);
|
|
st_Output(st_Buffer);
|
|
|
|
st_OutputLastVerifiedPoint();
|
|
st_OutputFortification(ptr + FORTIFY_HEADER_SIZE,
|
|
FORTIFY_BEFORE_VALUE, FORTIFY_ALIGNED_BEFORE_SIZE);
|
|
result = 0;
|
|
|
|
#ifdef FORTIFY_FILL_ON_CORRUPTION
|
|
st_SetFortification(ptr + FORTIFY_HEADER_SIZE, FORTIFY_BEFORE_VALUE, FORTIFY_ALIGNED_BEFORE_SIZE);
|
|
#endif
|
|
}
|
|
|
|
if(!st_CheckFortification(ptr + FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE + h->Size,
|
|
FORTIFY_AFTER_VALUE, FORTIFY_AFTER_SIZE))
|
|
{
|
|
sprintf(st_Buffer, "\nFortify: Overwrite detected after block %s at %s.%lu\n",
|
|
st_MemoryBlockString(h), file, line);
|
|
st_Output(st_Buffer);
|
|
|
|
st_OutputLastVerifiedPoint();
|
|
st_OutputFortification(ptr + FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE + h->Size,
|
|
FORTIFY_AFTER_VALUE, FORTIFY_AFTER_SIZE);
|
|
result = 0;
|
|
|
|
#ifdef FORTIFY_FILL_ON_CORRUPTION
|
|
st_SetFortification(ptr + FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE + h->Size,
|
|
FORTIFY_AFTER_VALUE, FORTIFY_AFTER_SIZE);
|
|
#endif
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
#ifdef FORTIFY_TRACK_DEALLOCATED_MEMORY
|
|
|
|
/*
|
|
* st_CheckDeallocatedBlock - Check a deallocated block's header and fortifications.
|
|
* Returns true if the block is happy.
|
|
*/
|
|
static int
|
|
st_CheckDeallocatedBlock(struct Header *h, const char *file, unsigned long line)
|
|
{
|
|
unsigned char *ptr = (unsigned char *)h;
|
|
int result = 1;
|
|
|
|
if(!st_IsHeaderValid(h))
|
|
{
|
|
sprintf(st_Buffer,
|
|
#ifdef FORTIFY_NO_PERCENT_P
|
|
"\nFortify: Invalid deallocated pointer (0x%08lx) or corrupted header detected at %s.%lu\n",
|
|
#else
|
|
"\nFortify: Invalid deallocated pointer (%p) or corrupted header detected at %s.%lu\n",
|
|
#endif
|
|
ptr + FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE, file, line);
|
|
st_Output(st_Buffer);
|
|
st_OutputLastVerifiedPoint();
|
|
return(0);
|
|
}
|
|
|
|
if(!st_CheckFortification(ptr + FORTIFY_HEADER_SIZE,
|
|
FORTIFY_BEFORE_VALUE, FORTIFY_ALIGNED_BEFORE_SIZE))
|
|
{
|
|
sprintf(st_Buffer, "\nFortify: Underwrite detected before deallocated block %s at %s.%lu\n",
|
|
st_MemoryBlockString(h), file, line);
|
|
st_Output(st_Buffer);
|
|
sprintf(st_Buffer, " Memory block was deallocated by \"%s\" at %s.%lu\n",
|
|
st_DeallocatorName[h->Deallocator], h->FreedFile, h->FreedLine);
|
|
st_Output(st_Buffer);
|
|
|
|
st_OutputLastVerifiedPoint();
|
|
st_OutputFortification(ptr + FORTIFY_HEADER_SIZE,
|
|
FORTIFY_BEFORE_VALUE, FORTIFY_ALIGNED_BEFORE_SIZE);
|
|
|
|
#ifdef FORTIFY_FILL_ON_CORRUPTION
|
|
st_SetFortification(ptr + FORTIFY_HEADER_SIZE, FORTIFY_BEFORE_VALUE, FORTIFY_ALIGNED_BEFORE_SIZE);
|
|
#endif
|
|
result = 0;
|
|
}
|
|
|
|
if(!st_CheckFortification(ptr + FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE + h->Size,
|
|
FORTIFY_AFTER_VALUE, FORTIFY_AFTER_SIZE))
|
|
{
|
|
sprintf(st_Buffer, "\nFortify: Overwrite detected after deallocated block %s at %s.%lu\n",
|
|
st_MemoryBlockString(h), file, line);
|
|
st_Output(st_Buffer);
|
|
sprintf(st_Buffer, " Memory block was deallocated by \"%s\" at %s.%lu\n",
|
|
st_DeallocatorName[h->Deallocator], h->FreedFile, h->FreedLine);
|
|
st_Output(st_Buffer);
|
|
|
|
st_OutputLastVerifiedPoint();
|
|
st_OutputFortification(ptr + FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE + h->Size,
|
|
FORTIFY_AFTER_VALUE, FORTIFY_AFTER_SIZE);
|
|
|
|
#ifdef FORTIFY_FILL_ON_CORRUPTION
|
|
st_SetFortification(ptr + FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE + h->Size,
|
|
FORTIFY_AFTER_VALUE, FORTIFY_AFTER_SIZE);
|
|
#endif
|
|
result = 0;
|
|
}
|
|
|
|
#ifdef FORTIFY_FILL_ON_DEALLOCATE
|
|
if(!st_CheckFortification(ptr + FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE,
|
|
FORTIFY_FILL_ON_DEALLOCATE_VALUE, h->Size))
|
|
{
|
|
sprintf(st_Buffer, "\nFortify: Write to deallocated block %s detected at %s.%lu\n",
|
|
st_MemoryBlockString(h), file, line);
|
|
st_Output(st_Buffer);
|
|
|
|
sprintf(st_Buffer, " Memory block was deallocated by \"%s\" at %s.%lu\n",
|
|
st_DeallocatorName[h->Deallocator], h->FreedFile, h->FreedLine);
|
|
st_Output(st_Buffer);
|
|
st_OutputLastVerifiedPoint();
|
|
|
|
st_OutputFortification(ptr + FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE,
|
|
FORTIFY_FILL_ON_DEALLOCATE_VALUE, h->Size);
|
|
|
|
#ifdef FORTIFY_FILL_ON_CORRUPTION
|
|
st_SetFortification(ptr + FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE,
|
|
FORTIFY_FILL_ON_DEALLOCATE_VALUE, h->Size);
|
|
#endif /* FORTIFY_FILL_ON_CORRUPTION */
|
|
result = 0;
|
|
}
|
|
#endif /* FORTIFY_FILL_ON_DEALLOCATE */
|
|
return result;
|
|
}
|
|
|
|
#endif /* FORTIFY_TRACK_DEALLOCATED_MEMORY */
|
|
|
|
|
|
/*
|
|
* st_CheckFortification - Checks if the _size_
|
|
* bytes from _ptr_ are all set to _value_
|
|
* Returns true if all is happy.
|
|
*/
|
|
static int
|
|
st_CheckFortification(unsigned char *ptr, unsigned char value, size_t size)
|
|
{
|
|
while(size--)
|
|
if(*ptr++ != value)
|
|
return(0);
|
|
|
|
return(1);
|
|
}
|
|
|
|
/*
|
|
* st_SetFortification - Set the _size_ bytes from _ptr_ to _value_.
|
|
*/
|
|
static void
|
|
st_SetFortification(unsigned char *ptr, unsigned char value, size_t size)
|
|
{
|
|
memset(ptr, value, size);
|
|
}
|
|
|
|
/*
|
|
* st_OutputFortification - Output the corrupted section of the fortification
|
|
*/
|
|
static void
|
|
st_OutputFortification(unsigned char *ptr, unsigned char value, size_t size)
|
|
{
|
|
size_t offset, skipped, advance;
|
|
offset = 0;
|
|
|
|
sprintf(st_Buffer, " Address Offset Data (%02x)", value);
|
|
st_Output(st_Buffer);
|
|
|
|
while(offset < size)
|
|
{
|
|
/*
|
|
* Skip 3 or more 'correct' lines
|
|
*/
|
|
if((size - offset) < 3 * 16)
|
|
advance = size - offset;
|
|
else
|
|
advance = 3 * 16;
|
|
if(advance > 0 && st_CheckFortification(ptr+offset, value, advance))
|
|
{
|
|
offset += advance;
|
|
skipped = advance;
|
|
|
|
if(size - offset < 16)
|
|
advance = size - offset;
|
|
else
|
|
advance = 16;
|
|
|
|
while(advance > 0 && st_CheckFortification(ptr+offset, value, advance))
|
|
{
|
|
offset += advance;
|
|
skipped += advance;
|
|
if(size - offset < 16)
|
|
advance = size - offset;
|
|
else
|
|
advance = 16;
|
|
}
|
|
sprintf(st_Buffer, "\n ...%lu bytes skipped...", (unsigned long)skipped);
|
|
st_Output(st_Buffer);
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if(size - offset < 16)
|
|
st_HexDump(ptr, offset, size-offset, 0);
|
|
else
|
|
st_HexDump(ptr, offset, 16, 0);
|
|
|
|
offset += 16;
|
|
}
|
|
}
|
|
|
|
st_Output("\n");
|
|
}
|
|
|
|
/*
|
|
* st_HexDump - output a nice hex dump of "size" bytes, starting at "ptr" + "offset"
|
|
*/
|
|
static void
|
|
st_HexDump(unsigned char *ptr, size_t offset, size_t size, int title)
|
|
{
|
|
char ascii[17];
|
|
int column;
|
|
int output;
|
|
|
|
if(title)
|
|
st_Output(" Address Offset Data");
|
|
|
|
column = 0;
|
|
ptr += offset;
|
|
output = 0;
|
|
|
|
while(output < size)
|
|
{
|
|
if(column == 0)
|
|
{
|
|
#ifdef FORTIFY_NO_PERCENT_P
|
|
sprintf(st_Buffer, "\n0x%08lx %8lu ", ptr, (unsigned long)offset);
|
|
#else
|
|
sprintf(st_Buffer, "\n%10p %8lu ", ptr, (unsigned long)offset);
|
|
#endif
|
|
st_Output(st_Buffer);
|
|
}
|
|
|
|
sprintf(st_Buffer, "%02x%s", *ptr, ((column % 4) == 3) ? " " : "");
|
|
st_Output(st_Buffer);
|
|
|
|
ascii[ column ] = isprint( *ptr ) ? (char)(*ptr) : (char)('.');
|
|
ascii[ column + 1 ] = '\0';
|
|
|
|
ptr++;
|
|
offset++;
|
|
output++;
|
|
column++;
|
|
|
|
if(column == 16)
|
|
{
|
|
st_Output( " \"" );
|
|
st_Output( ascii );
|
|
st_Output( "\"" );
|
|
column = 0;
|
|
}
|
|
}
|
|
|
|
if ( column != 0 )
|
|
{
|
|
while ( column < 16 )
|
|
{
|
|
if( column % 4 == 3 )
|
|
st_Output( " " );
|
|
else
|
|
st_Output( " " );
|
|
|
|
column++;
|
|
}
|
|
st_Output( " \"" );
|
|
st_Output( ascii );
|
|
st_Output( "\"" );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* st_IsHeaderValid - Returns true if the
|
|
* supplied pointer does indeed point to a
|
|
* real Header
|
|
*/
|
|
static int
|
|
st_IsHeaderValid(struct Header *h)
|
|
{
|
|
return(st_ChecksumHeader(h) == FORTIFY_CHECKSUM_VALUE);
|
|
}
|
|
|
|
/*
|
|
* st_MakeHeaderValid - Updates the checksum
|
|
* to make the header valid
|
|
*/
|
|
static void
|
|
st_MakeHeaderValid(struct Header *h)
|
|
{
|
|
h->Checksum = 0;
|
|
h->Checksum = (unsigned short)(FORTIFY_CHECKSUM_VALUE - st_ChecksumHeader(h));
|
|
}
|
|
|
|
/*
|
|
* st_ChecksumHeader - Calculate (and return)
|
|
* the checksum of the header. (Including the
|
|
* Checksum field itself. If all is well, the
|
|
* checksum returned by this function should
|
|
* be FORTIFY_CHECKSUM_VALUE
|
|
*/
|
|
static unsigned short
|
|
st_ChecksumHeader(struct Header *h)
|
|
{
|
|
unsigned short c, checksum, *p;
|
|
|
|
for(c = 0, checksum = 0, p = (unsigned short *)h;
|
|
c < FORTIFY_HEADER_SIZE/sizeof(unsigned short); c++)
|
|
{
|
|
checksum += *p++;
|
|
}
|
|
|
|
return(checksum);
|
|
}
|
|
|
|
/*
|
|
* st_IsOnAllocatedList - Examines the allocated
|
|
* list to see if the given header is on it.
|
|
*/
|
|
static int
|
|
st_IsOnAllocatedList(struct Header *h)
|
|
{
|
|
struct Header *curr;
|
|
|
|
curr = st_AllocatedHead;
|
|
while(curr)
|
|
{
|
|
if(curr == h)
|
|
return(1);
|
|
|
|
curr = curr->Next;
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
#ifdef FORTIFY_TRACK_DEALLOCATED_MEMORY
|
|
/*
|
|
* st_IsOnDeallocatedList - Examines the deallocated
|
|
* list to see if the given header is on it.
|
|
*/
|
|
static int
|
|
st_IsOnDeallocatedList(struct Header *h)
|
|
{
|
|
struct Header *curr;
|
|
|
|
curr = st_DeallocatedHead;
|
|
while(curr)
|
|
{
|
|
if(curr == h)
|
|
return(1);
|
|
|
|
curr = curr->Next;
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* st_PurgeDeallocatedBlocks - free at least "Bytes"
|
|
* worth of deallocated memory, starting at the
|
|
* oldest deallocated block.
|
|
* Returns true if any blocks were freed.
|
|
*/
|
|
static int
|
|
st_PurgeDeallocatedBlocks(unsigned long Bytes, const char *file, unsigned long line)
|
|
{
|
|
unsigned long FreedBytes = 0;
|
|
unsigned long FreedBlocks = 0;
|
|
|
|
#ifdef FORTIFY_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY
|
|
sprintf(st_Buffer, "\nFortify: Warning - Discarding deallocated memory at %s.%lu\n",
|
|
file, line);
|
|
st_Output(st_Buffer);
|
|
#endif /* FORTIFY_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY */
|
|
|
|
while(st_DeallocatedTail && FreedBytes < Bytes)
|
|
{
|
|
st_CheckDeallocatedBlock(st_DeallocatedTail, file, line);
|
|
FreedBytes += st_DeallocatedTail->Size;
|
|
FreedBlocks++;
|
|
#ifdef FORTIFY_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY
|
|
#ifdef FORTIFY_VERBOSE_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY
|
|
sprintf(st_Buffer, " %s\n",
|
|
st_DeallocatedMemoryBlockString(st_DeallocatedTail));
|
|
st_Output(st_Buffer);
|
|
#endif /* FORTIFY_VERBOSE_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY */
|
|
#endif /* FORTIFY_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY */
|
|
st_FreeDeallocatedBlock(st_DeallocatedTail, file, line);
|
|
}
|
|
|
|
return FreedBlocks != 0;
|
|
}
|
|
|
|
/*
|
|
* st_PurgeDeallocatedScope - free all deallocated
|
|
* memory blocks that were allocated within "Scope"
|
|
*/
|
|
static int
|
|
st_PurgeDeallocatedScope(unsigned char Scope, const char *file, unsigned long line)
|
|
{
|
|
struct Header *curr, *next;
|
|
unsigned long FreedBlocks = 0;
|
|
|
|
curr = st_DeallocatedHead;
|
|
while(curr)
|
|
{
|
|
next = curr->Next;
|
|
if(curr->Scope >= Scope)
|
|
{
|
|
st_FreeDeallocatedBlock(curr, file, line);
|
|
FreedBlocks++;
|
|
}
|
|
|
|
curr = next;
|
|
}
|
|
|
|
return FreedBlocks != 0;
|
|
}
|
|
|
|
/*
|
|
* st_FreeDeallocatedBlock - actually remove
|
|
* a deallocated block from the deallocated
|
|
* list, and actually free it's memory.
|
|
*/
|
|
static void
|
|
st_FreeDeallocatedBlock(struct Header *h, const char *file, unsigned long line)
|
|
{
|
|
st_CheckDeallocatedBlock( h, file, line );
|
|
|
|
/*
|
|
* Begin Critical region
|
|
*/
|
|
FORTIFY_LOCK();
|
|
|
|
st_TotalDeallocated -= h->Size;
|
|
|
|
if(st_DeallocatedHead == h)
|
|
{
|
|
st_DeallocatedHead = h->Next;
|
|
}
|
|
|
|
if(st_DeallocatedTail == h)
|
|
{
|
|
st_DeallocatedTail = h->Prev;
|
|
}
|
|
|
|
if(h->Prev)
|
|
{
|
|
st_CheckDeallocatedBlock(h->Prev, file, line);
|
|
h->Prev->Next = h->Next;
|
|
st_MakeHeaderValid(h->Prev);
|
|
}
|
|
|
|
if(h->Next)
|
|
{
|
|
st_CheckDeallocatedBlock(h->Next, file, line);
|
|
h->Next->Prev = h->Prev;
|
|
st_MakeHeaderValid(h->Next);
|
|
}
|
|
|
|
/*
|
|
* Free the label
|
|
*/
|
|
if(h->Label)
|
|
{
|
|
free(h->Label);
|
|
}
|
|
|
|
/*
|
|
* Nuke out all memory that is about to be freed, including the header
|
|
*/
|
|
st_SetFortification((unsigned char*)h, FORTIFY_FILL_ON_DEALLOCATE_VALUE,
|
|
FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE + h->Size + FORTIFY_AFTER_SIZE);
|
|
|
|
/*
|
|
* And do the actual free
|
|
*/
|
|
free(h);
|
|
|
|
/*
|
|
* End critical region
|
|
*/
|
|
FORTIFY_UNLOCK();
|
|
}
|
|
|
|
#endif /* FORTIFY_TRACK_DEALLOCATED_MEMORY */
|
|
|
|
/*
|
|
* st_OutputMemory - Hex and ascii dump the
|
|
* user memory of a block.
|
|
*/
|
|
static void
|
|
st_OutputMemory(struct Header *h)
|
|
{
|
|
st_HexDump((unsigned char*)h + FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE,
|
|
0, h->Size, 1);
|
|
}
|
|
|
|
|
|
/*
|
|
* st_OutputHeader - Output the header
|
|
*/
|
|
static void
|
|
st_OutputHeader(struct Header *h)
|
|
{
|
|
if(h->Label == NULL)
|
|
{
|
|
#ifdef FORTIFY_NO_PERCENT_P
|
|
sprintf(st_Buffer, "0x%08lx %8lu %s.%lu\n",
|
|
#else
|
|
sprintf(st_Buffer, "%10p %8lu %s.%lu\n",
|
|
#endif
|
|
(unsigned char*)h + FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE,
|
|
(unsigned long)h->Size,
|
|
h->File, h->Line);
|
|
}
|
|
else
|
|
{
|
|
#ifdef FORTIFY_NO_PERCENT_P
|
|
sprintf(st_Buffer, "0x%08lx %8lu %s.%lu %s\n",
|
|
#else
|
|
sprintf(st_Buffer, "%10p %8lu %s.%lu %s\n",
|
|
#endif
|
|
(unsigned char*)h + FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE,
|
|
(unsigned long)h->Size,
|
|
h->File, h->Line, h->Label);
|
|
}
|
|
st_Output(st_Buffer);
|
|
}
|
|
|
|
/*
|
|
* st_OutputLastVerifiedPoint - output the last
|
|
* known point where everything was hoopy.
|
|
*/
|
|
static void
|
|
st_OutputLastVerifiedPoint()
|
|
{
|
|
sprintf(st_Buffer, " Memory integrity was last verified at %s.%lu\n",
|
|
st_LastVerifiedFile,
|
|
st_LastVerifiedLine);
|
|
st_Output(st_Buffer);
|
|
}
|
|
|
|
/*
|
|
* st_MemoryBlockString - constructs a string that
|
|
* desribes a memory block. (pointer,size,allocator,label)
|
|
*/
|
|
static const char *
|
|
st_MemoryBlockString(struct Header *h)
|
|
{
|
|
static char st_BlockString[512];
|
|
|
|
if(h->Label == 0)
|
|
{
|
|
#ifdef FORTIFY_NO_PERCENT_P
|
|
sprintf(st_BlockString,"(0x%08lx,%lu,%s.%lu)",
|
|
#else
|
|
sprintf(st_BlockString,"(%p,%lu,%s.%lu)",
|
|
#endif
|
|
(char*)h + FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE,
|
|
(unsigned long)h->Size, h->File, h->Line);
|
|
}
|
|
else
|
|
{
|
|
#ifdef FORTIFY_NO_PERCENT_P
|
|
sprintf(st_BlockString,"(0x%08lx,%lu,%s.%lu,%s)",
|
|
#else
|
|
sprintf(st_BlockString,"(%p,%lu,%s.%lu,%s)",
|
|
#endif
|
|
(char*)h + FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE,
|
|
(unsigned long)h->Size, h->File, h->Line, h->Label);
|
|
}
|
|
|
|
return st_BlockString;
|
|
}
|
|
|
|
#ifdef FORTIFY_TRACK_DEALLOCATED_MEMORY
|
|
#ifdef FORTIFY_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY
|
|
#ifdef FORTIFY_VERBOSE_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY
|
|
/*
|
|
* st_DeallocatedMemoryBlockString - constructs
|
|
* a string that desribes a deallocated memory
|
|
* block. (pointer,size,allocator,deallocator)
|
|
*/
|
|
|
|
static const char *
|
|
st_DeallocatedMemoryBlockString(struct Header *h)
|
|
{
|
|
static char st_BlockString[256];
|
|
|
|
if(h->Label == 0)
|
|
{
|
|
#ifdef FORTIFY_NO_PERCENT_P
|
|
sprintf(st_BlockString,"(0x%08lx,%lu,%s.%lu,%s.%lu)",
|
|
#else
|
|
sprintf(st_BlockString,"(%p,%lu,%s.%lu,%s.%lu)",
|
|
#endif
|
|
(char*)h + FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE,
|
|
(unsigned long)h->Size, h->File, h->Line, h->FreedFile, h->FreedLine);
|
|
}
|
|
else
|
|
{
|
|
#ifdef FORTIFY_NO_PERCENT_P
|
|
sprintf(st_BlockString,"(0x%08lx,%lu,%s.%lu,%s.%lu,%s)",
|
|
#else
|
|
sprintf(st_BlockString,"(%p,%lu,%s.%lu,%s.%lu,%s)",
|
|
#endif
|
|
(char*)h + FORTIFY_HEADER_SIZE + FORTIFY_ALIGNED_BEFORE_SIZE,
|
|
(unsigned long)h->Size, h->File, h->Line, h->FreedFile, h->FreedLine, h->Label);
|
|
}
|
|
|
|
return st_BlockString;
|
|
}
|
|
#endif /* FORTIFY_VERBOSE_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY */
|
|
#endif /* FORTIFY_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY */
|
|
#endif /* FORTIFY_TRACK_DEALLOCATED_MEMORY */
|
|
|
|
|
|
/*
|
|
* st_DefaultOutput - the default output function
|
|
*/
|
|
static void
|
|
st_DefaultOutput(const char *String)
|
|
{
|
|
fprintf(stdout, String);
|
|
fflush(stdout);
|
|
}
|
|
|
|
/*
|
|
* Fortify_malloc - Fortify's replacement malloc()
|
|
*/
|
|
void *FORTIFY_STORAGE
|
|
Fortify_malloc(size_t size, const char *file, unsigned long line)
|
|
{
|
|
return Fortify_Allocate(size, Fortify_Allocator_malloc, file, line);
|
|
}
|
|
|
|
/*
|
|
* Fortify_realloc - Fortify's replacement realloc()
|
|
*/
|
|
void *
|
|
Fortify_realloc(void *uptr, size_t new_size, const char *file, unsigned long line)
|
|
{
|
|
unsigned char *ptr = (unsigned char *)uptr - FORTIFY_HEADER_SIZE - FORTIFY_ALIGNED_BEFORE_SIZE;
|
|
struct Header *h = (struct Header *)ptr;
|
|
void *new_ptr;
|
|
|
|
/*
|
|
* If Fortify is disabled, we gotta do this a little
|
|
* differently.
|
|
*/
|
|
if(!st_Disabled)
|
|
{
|
|
if(!uptr)
|
|
return(Fortify_Allocate(new_size, Fortify_Allocator_realloc, file, line));
|
|
|
|
if(!st_IsOnAllocatedList(h))
|
|
{
|
|
#ifdef FORTIFY_TRACK_DEALLOCATED_MEMORY
|
|
if(st_IsOnDeallocatedList(h))
|
|
{
|
|
sprintf(st_Buffer, "\nFortify: Deallocated memory block passed to \"%s\" at %s.%lu\n",
|
|
st_AllocatorName[Fortify_Allocator_realloc], file, line);
|
|
st_Output(st_Buffer);
|
|
sprintf(st_Buffer, " Memory block %s was deallocated by \"%s\" at %s.%lu\n",
|
|
st_MemoryBlockString(h),
|
|
st_DeallocatorName[h->Deallocator], h->FreedFile, h->FreedLine);
|
|
st_Output(st_Buffer);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
sprintf(st_Buffer,
|
|
#ifdef FORTIFY_NO_PERCENT_P
|
|
"\nFortify: Invalid pointer (0x%08lx) passed to realloc at %s.%lu\n",
|
|
#else
|
|
"\nFortify: Invalid pointer (%p) passed to realloc at %s.%lu\n",
|
|
#endif
|
|
ptr, file, line);
|
|
st_Output(st_Buffer);
|
|
return 0;
|
|
}
|
|
|
|
if(!st_CheckBlock(h, file, line))
|
|
return 0;
|
|
|
|
new_ptr = Fortify_Allocate(new_size, Fortify_Allocator_realloc, file, line);
|
|
if(!new_ptr)
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
if(h->Size < new_size)
|
|
memcpy(new_ptr, uptr, h->Size);
|
|
else
|
|
memcpy(new_ptr, uptr, new_size);
|
|
|
|
Fortify_Deallocate(uptr, Fortify_Deallocator_realloc, file, line);
|
|
return(new_ptr);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If the old block was fortified, we can't use normal realloc.
|
|
*/
|
|
if(st_IsOnAllocatedList(h))
|
|
{
|
|
new_ptr = Fortify_Allocate(new_size, Fortify_Allocator_realloc, file, line);
|
|
if(!new_ptr)
|
|
return(0);
|
|
|
|
if(h->Size < new_size)
|
|
memcpy(new_ptr, uptr, h->Size);
|
|
else
|
|
memcpy(new_ptr, uptr, new_size);
|
|
|
|
Fortify_Deallocate(uptr, Fortify_Deallocator_realloc, file, line);
|
|
return(new_ptr);
|
|
}
|
|
else /* easy */
|
|
{
|
|
return realloc(uptr, new_size);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Fortify_calloc - Fortify's replacement calloc
|
|
*/
|
|
void *
|
|
Fortify_calloc(size_t num, size_t size, const char *file, unsigned long line)
|
|
{
|
|
if(!st_Disabled)
|
|
{
|
|
void *ptr = Fortify_Allocate(size * num, Fortify_Allocator_calloc, file, line);
|
|
if(ptr)
|
|
{
|
|
memset(ptr, 0, size*num);
|
|
}
|
|
return ptr;
|
|
}
|
|
else
|
|
{
|
|
return calloc(num, size);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Fortify_free - Fortify's replacement free
|
|
*/
|
|
void
|
|
Fortify_free(void *uptr, const char *file, unsigned long line)
|
|
{
|
|
/* it is defined to be safe to free(0) */
|
|
if(uptr == 0)
|
|
return;
|
|
|
|
Fortify_Deallocate(uptr, Fortify_Deallocator_free, file, line);
|
|
}
|
|
|
|
/*
|
|
* Fortify_strdup - Fortify's replacement strdup. Since strdup isn't
|
|
* ANSI, it is only provided if FORTIFY_STRDUP is defined.
|
|
*/
|
|
#ifdef FORTIFY_STRDUP
|
|
char *FORTIFY_STORAGE
|
|
Fortify_strdup(const char *oldStr, const char *file, unsigned long line)
|
|
{
|
|
if(!st_Disabled)
|
|
{
|
|
char *newStr = (char*)Fortify_Allocate(strlen(oldStr)+1, Fortify_Allocator_strdup, file, line);
|
|
if(newStr)
|
|
{
|
|
strcpy(newStr, oldStr);
|
|
}
|
|
|
|
return newStr;
|
|
}
|
|
else
|
|
{
|
|
return strdup(oldStr);
|
|
}
|
|
}
|
|
#endif /* FORTIFY_STRDUP */
|
|
|
|
static void
|
|
st_OutputDeleteTrace()
|
|
{
|
|
#ifdef __cplusplus
|
|
if(st_DeleteStackTop > 1)
|
|
{
|
|
sprintf(st_Buffer, "Delete Trace: %s.%lu\n", st_DeleteFile[st_DeleteStackTop-1],
|
|
st_DeleteLine[st_DeleteStackTop-1]);
|
|
st_Output(st_Buffer);
|
|
for(int c = st_DeleteStackTop-2; c >= 0; c--)
|
|
{
|
|
sprintf(st_Buffer, " %s.%lu\n", st_DeleteFile[c],
|
|
st_DeleteLine[c]);
|
|
st_Output(st_Buffer);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
|
|
/*
|
|
* st_NewHandler() - there is no easy way to get
|
|
* the new handler function. And isn't it great
|
|
* how the new handler doesn't take a parameter
|
|
* giving the size of the request that failed.
|
|
* Thanks Bjarne!
|
|
*/
|
|
Fortify_NewHandlerFunc
|
|
st_NewHandler()
|
|
{
|
|
/* get the current handler */
|
|
Fortify_NewHandlerFunc handler = set_new_handler(0);
|
|
|
|
/* and set it back (since we cant
|
|
* get it without changing it)
|
|
*/
|
|
set_new_handler(handler);
|
|
|
|
return handler;
|
|
}
|
|
|
|
/*
|
|
* operator new - Fortify's replacement new,
|
|
* without source-code information.
|
|
*/
|
|
void *FORTIFY_STORAGE
|
|
operator new(size_t size)
|
|
{
|
|
void *p;
|
|
|
|
while((p = Fortify_Allocate(size, Fortify_Allocator_new,
|
|
st_AllocatorName[Fortify_Allocator_new], 0)) == 0)
|
|
{
|
|
if(st_NewHandler())
|
|
(*st_NewHandler())();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
/*
|
|
* operator new - Fortify's replacement new,
|
|
* with source-code information
|
|
*/
|
|
void *FORTIFY_STORAGE
|
|
operator new(size_t size, const char *file, unsigned long line)
|
|
{
|
|
void *p;
|
|
|
|
while((p = Fortify_Allocate(size, Fortify_Allocator_new, file, line)) == 0)
|
|
{
|
|
if(st_NewHandler())
|
|
(*st_NewHandler())();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
#ifdef FORTIFY_PROVIDE_ARRAY_NEW
|
|
|
|
/*
|
|
* operator new[], without source-code info
|
|
*/
|
|
void *FORTIFY_STORAGE
|
|
operator new[](size_t size)
|
|
{
|
|
void *p;
|
|
|
|
while((p = Fortify_Allocate(size, Fortify_Allocator_array_new,
|
|
st_AllocatorName[Fortify_Allocator_array_new], 0)) == 0)
|
|
{
|
|
if(st_NewHandler())
|
|
(*st_NewHandler())();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
/*
|
|
* operator new[], with source-code info
|
|
*/
|
|
void *FORTIFY_STORAGE
|
|
operator new[](size_t size, const char *file, unsigned long line)
|
|
{
|
|
void *p;
|
|
|
|
while((p = Fortify_Allocate(size, Fortify_Allocator_array_new, file, line)) == 0)
|
|
{
|
|
if(st_NewHandler())
|
|
(*st_NewHandler())();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
#endif /* FORTIFY_PROVIDE_ARRAY_NEW */
|
|
|
|
/*
|
|
* Fortify_PreDelete - C++ does not allow overloading
|
|
* of delete, so the delete macro calls Fortify_PreDelete
|
|
* with the source-code info, and then calls delete.
|
|
*/
|
|
void FORTIFY_STORAGE
|
|
Fortify_PreDelete(const char *file, unsigned long line)
|
|
{
|
|
FORTIFY_LOCK();
|
|
|
|
/*
|
|
* Push the source code info for the delete onto the delete stack
|
|
* (if we have enough room, of course)
|
|
*/
|
|
if(st_DeleteStackTop < FORTIFY_DELETE_STACK_SIZE)
|
|
{
|
|
st_DeleteFile[st_DeleteStackTop] = file;
|
|
st_DeleteLine[st_DeleteStackTop] = line;
|
|
}
|
|
|
|
st_DeleteStackTop++;
|
|
}
|
|
|
|
/*
|
|
* Fortify_PostDelete() - Pop the delete source-code info
|
|
* off the source stack.
|
|
*/
|
|
void FORTIFY_STORAGE
|
|
Fortify_PostDelete()
|
|
{
|
|
st_DeleteStackTop--;
|
|
|
|
FORTIFY_UNLOCK();
|
|
}
|
|
|
|
/*
|
|
* operator delete - fortify's replacement delete
|
|
*/
|
|
void FORTIFY_STORAGE
|
|
operator delete(void *uptr)
|
|
{
|
|
const char *file;
|
|
unsigned long line;
|
|
|
|
/*
|
|
* It is defined to be harmless to delete 0
|
|
*/
|
|
if(uptr == 0)
|
|
return;
|
|
|
|
/*
|
|
* find the source-code info
|
|
*/
|
|
if(st_DeleteStackTop)
|
|
{
|
|
if(st_DeleteStackTop < FORTIFY_DELETE_STACK_SIZE)
|
|
{
|
|
file = st_DeleteFile[st_DeleteStackTop-1];
|
|
line = st_DeleteLine[st_DeleteStackTop-1];
|
|
}
|
|
else
|
|
{
|
|
file = st_DeleteFile[FORTIFY_DELETE_STACK_SIZE-1];
|
|
line = st_DeleteLine[FORTIFY_DELETE_STACK_SIZE-1];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
file = st_DeallocatorName[Fortify_Deallocator_delete];
|
|
line = 0;
|
|
}
|
|
|
|
Fortify_Deallocate(uptr, Fortify_Deallocator_delete, file, line);
|
|
}
|
|
|
|
#ifdef FORTIFY_PROVIDE_ARRAY_DELETE
|
|
|
|
/*
|
|
* operator delete[] - fortify's replacement delete[]
|
|
*/
|
|
void FORTIFY_STORAGE
|
|
operator delete[](void *uptr)
|
|
{
|
|
const char *file;
|
|
unsigned long line;
|
|
|
|
/*
|
|
* It is defined to be harmless to delete 0
|
|
*/
|
|
if(uptr == 0)
|
|
return;
|
|
|
|
/*
|
|
* find the source-code info
|
|
*/
|
|
if(st_DeleteStackTop)
|
|
{
|
|
if(st_DeleteStackTop < FORTIFY_DELETE_STACK_SIZE)
|
|
{
|
|
file = st_DeleteFile[st_DeleteStackTop-1];
|
|
line = st_DeleteLine[st_DeleteStackTop-1];
|
|
}
|
|
else
|
|
{
|
|
file = st_DeleteFile[FORTIFY_DELETE_STACK_SIZE-1];
|
|
line = st_DeleteLine[FORTIFY_DELETE_STACK_SIZE-1];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
file = st_DeallocatorName[Fortify_Deallocator_array_delete];
|
|
line = 0;
|
|
}
|
|
|
|
Fortify_Deallocate(uptr, Fortify_Deallocator_array_delete, file, line);
|
|
}
|
|
|
|
#endif /* FORTIFY_PROVIDE_ARRAY_DELETE */
|
|
|
|
#ifdef FORTIFY_AUTOMATIC_LOG_FILE
|
|
/* Automatic log file stuff!
|
|
*
|
|
* AutoLogFile class. There can only ever be ONE of these
|
|
* instantiated! It is a static class, which means that
|
|
* it's constructor will be called at program initialization,
|
|
* and it's destructor will be called at program termination.
|
|
* We don't know if the other static class objects have been
|
|
* constructed/destructed yet, but this pretty much the best
|
|
* we can do with standard C++ language features.
|
|
*/
|
|
class Fortify_AutoLogFile
|
|
{
|
|
static FILE *fp;
|
|
static int written_something;
|
|
static char *init_string, *term_string;
|
|
|
|
public:
|
|
Fortify_AutoLogFile()
|
|
{
|
|
written_something = 0;
|
|
Fortify_SetOutputFunc(Fortify_AutoLogFile::Output);
|
|
Fortify_EnterScope(init_string, 0);
|
|
}
|
|
|
|
static void Output(const char *s)
|
|
{
|
|
if(written_something == 0)
|
|
{
|
|
FORTIFY_FIRST_ERROR_FUNCTION;
|
|
fp = fopen(FORTIFY_LOG_FILENAME, "w");
|
|
if(fp)
|
|
{
|
|
time_t t;
|
|
time(&t);
|
|
fprintf(fp, "Fortify log started at %s\n", ctime(&t));
|
|
written_something = 1;
|
|
}
|
|
}
|
|
|
|
if(fp)
|
|
{
|
|
fputs(s, fp);
|
|
fflush(fp);
|
|
}
|
|
}
|
|
|
|
~Fortify_AutoLogFile()
|
|
{
|
|
Fortify_LeaveScope(term_string, 0);
|
|
Fortify_CheckAllMemory(term_string, 0);
|
|
if(fp)
|
|
{
|
|
time_t t;
|
|
time(&t);
|
|
fprintf(fp, "\nFortify log closed at %s\n", ctime(&t));
|
|
fclose(fp);
|
|
fp = 0;
|
|
}
|
|
}
|
|
};
|
|
|
|
FILE *Fortify_AutoLogFile::fp = 0;
|
|
int Fortify_AutoLogFile::written_something = 0;
|
|
char *Fortify_AutoLogFile::init_string = "Program Initialization";
|
|
char *Fortify_AutoLogFile::term_string = "Program Termination";
|
|
|
|
static Fortify_AutoLogFile Abracadabra;
|
|
|
|
#endif /* FORTIFY_AUTOMATIC_LOG_FILE */
|
|
|
|
#endif /* __cplusplus */
|
|
|
|
#endif /* FORTIFY */
|