Initial revision
This commit is contained in:
917
fortify.c
Normal file
917
fortify.c
Normal file
@ -0,0 +1,917 @@
|
||||
/*
|
||||
* FILE:
|
||||
* fortify.c
|
||||
*
|
||||
* DESCRIPTION:
|
||||
* A fortified shell for malloc, realloc, calloc and free.
|
||||
* To use Fortify, each source file will need to #include "fortify.h". To
|
||||
* enable Fortify, define the symbol FORTIFY. If FORTIFY is not defined, it
|
||||
* will compile away to nothing. If you do not have stdout available, you may
|
||||
* wish to set an alternate output function. See Fortify_SetOutputFunc(),
|
||||
* below.
|
||||
* You will also need to link in fortify.o
|
||||
*
|
||||
* None of the functions in this file should really be called
|
||||
* directly; they really should be called through the macros
|
||||
* defined in fortify.h
|
||||
*
|
||||
*/
|
||||
#ifdef FORTIFY
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#define __FORTIFY_C__ /* So fortify.h knows to not define the fortify macros */
|
||||
#include "fortify.h"
|
||||
|
||||
#include "ufortify.h" /* the user's options */
|
||||
|
||||
struct Header
|
||||
{
|
||||
char *File; /* The sourcefile of the caller */
|
||||
unsigned long Line; /* The sourceline of the caller */
|
||||
size_t Size; /* The size of the malloc'd block */
|
||||
struct Header *Prev, /* List pointers */
|
||||
*Next;
|
||||
int Scope;
|
||||
int Checksum; /* For validating the Header structure; see ChecksumHeader() */
|
||||
};
|
||||
|
||||
static int CheckBlock(struct Header *h, char *file, unsigned long line);
|
||||
static int CheckFortification(unsigned char *ptr, unsigned char value, size_t size);
|
||||
static void SetFortification(unsigned char *ptr, unsigned char value, size_t size);
|
||||
static void OutputFortification(unsigned char *ptr, unsigned char value, size_t size);
|
||||
static int IsHeaderValid(struct Header *h);
|
||||
static void MakeHeaderValid(struct Header *h);
|
||||
static int ChecksumHeader(struct Header *h);
|
||||
static int IsOnList(struct Header *h);
|
||||
static void OutputHeader(struct Header *h);
|
||||
static void OutputMemory(struct Header *h);
|
||||
|
||||
static void st_DefaultOutput(char *String)
|
||||
{
|
||||
printf(String);
|
||||
}
|
||||
|
||||
static struct Header *st_Head = 0; /* Head of alloc'd memory list */
|
||||
static OutputFuncPtr st_Output = st_DefaultOutput; /* Output function for errors */
|
||||
static char st_Buffer[256]; /* Temporary buffer for sprintf's */
|
||||
static int st_Disabled = 0; /* If true, Fortify is inactive */
|
||||
static int st_MallocFailRate = 0; /* % of the time to fail mallocs */
|
||||
|
||||
static char *st_LastVerifiedFile = "unknown";
|
||||
static unsigned long st_LastVerifiedLine = 0;
|
||||
static int st_Scope = 0;
|
||||
static void OutputLastVerifiedPoint(void);
|
||||
|
||||
/*
|
||||
* Fortify_malloc() - Allocates a block of memory, with extra bits for
|
||||
* misuse protection/detection.
|
||||
*
|
||||
* Features:
|
||||
* + Adds the malloc'd memory onto Fortify's own private list.
|
||||
* (With a checksum'd header to detect corruption of the memory list)
|
||||
* + Places sentinals on either side of the user's memory with
|
||||
* known data in them, to detect use outside of the bounds
|
||||
* of the block
|
||||
* + Initializes the malloc'd memory to some "nasty" value, so code
|
||||
* can't rely on it's contents.
|
||||
* + Can check all sentinals on every malloc.
|
||||
* + Can generate a warning message on a malloc fail.
|
||||
* + Can randomly "fail" at a set fail rate
|
||||
*/
|
||||
void *FORTIFY_STORAGE
|
||||
Fortify_malloc(size_t size, char *file, unsigned long line)
|
||||
{
|
||||
unsigned char *ptr;
|
||||
struct Header *h;
|
||||
|
||||
FORTIFY_LOCK();
|
||||
|
||||
if(st_Disabled)
|
||||
{
|
||||
ptr = malloc(size);
|
||||
FORTIFY_UNLOCK();
|
||||
return(ptr);
|
||||
}
|
||||
|
||||
#ifdef CHECK_ALL_MEMORY_ON_MALLOC
|
||||
Fortify_CheckAllMemory(file, line);
|
||||
#endif
|
||||
|
||||
if(size == 0)
|
||||
{
|
||||
#ifdef WARN_ON_ZERO_MALLOC
|
||||
sprintf(st_Buffer,
|
||||
"\nFortify: %s.%ld\n malloc(0) attempted failed\n",
|
||||
file, line);
|
||||
st_Output(st_Buffer);
|
||||
#endif
|
||||
|
||||
FORTIFY_UNLOCK();
|
||||
return(0);
|
||||
}
|
||||
|
||||
if(st_MallocFailRate > 0)
|
||||
{
|
||||
if(rand() % 100 < st_MallocFailRate)
|
||||
{
|
||||
#ifdef WARN_ON_FALSE_FAIL
|
||||
sprintf(st_Buffer,
|
||||
"\nFortify: %s.%ld\n malloc(%ld) \"false\" failed\n",
|
||||
file, line, (unsigned long)size);
|
||||
st_Output(st_Buffer);
|
||||
#endif
|
||||
FORTIFY_UNLOCK();
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* malloc the memory, including the space for the header and fortification
|
||||
* buffers
|
||||
*/
|
||||
#ifdef WARN_ON_SIZE_T_OVERFLOW
|
||||
{
|
||||
size_t private_size = sizeof(struct Header)
|
||||
+ FORTIFY_BEFORE_SIZE + size + FORTIFY_AFTER_SIZE;
|
||||
|
||||
if(private_size < size) /* Check to see if the added baggage is larger than size_t */
|
||||
{
|
||||
sprintf(st_Buffer,
|
||||
"\nFortify: %s.%ld\n malloc(%ld) has overflowed size_t.\n",
|
||||
file, line, (unsigned long)size);
|
||||
st_Output(st_Buffer);
|
||||
FORTIFY_UNLOCK();
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ptr = malloc(sizeof(struct Header) +
|
||||
FORTIFY_BEFORE_SIZE + size + FORTIFY_AFTER_SIZE);
|
||||
if(!ptr)
|
||||
{
|
||||
#ifdef WARN_ON_MALLOC_FAIL
|
||||
sprintf(st_Buffer, "\nFortify: %s.%ld\n malloc(%ld) failed\n",
|
||||
file, line, (unsigned long)size);
|
||||
st_Output(st_Buffer);
|
||||
#endif
|
||||
|
||||
FORTIFY_UNLOCK();
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize and validate the header
|
||||
*/
|
||||
h = (struct Header *)ptr;
|
||||
|
||||
h->Size = size;
|
||||
|
||||
h->File = file;
|
||||
h->Line = line;
|
||||
|
||||
h->Next = st_Head;
|
||||
h->Prev = 0;
|
||||
|
||||
h->Scope = st_Scope;
|
||||
|
||||
if(st_Head)
|
||||
{
|
||||
st_Head->Prev = h;
|
||||
MakeHeaderValid(st_Head);
|
||||
}
|
||||
|
||||
st_Head = h;
|
||||
|
||||
MakeHeaderValid(h);
|
||||
|
||||
|
||||
/*
|
||||
* Initialize the fortifications
|
||||
*/
|
||||
SetFortification(ptr + sizeof(struct Header),
|
||||
FORTIFY_BEFORE_VALUE, FORTIFY_BEFORE_SIZE);
|
||||
SetFortification(ptr + sizeof(struct Header) + FORTIFY_BEFORE_SIZE + size,
|
||||
FORTIFY_AFTER_VALUE, FORTIFY_AFTER_SIZE);
|
||||
|
||||
#ifdef FILL_ON_MALLOC
|
||||
/*
|
||||
* Fill the actual user memory
|
||||
*/
|
||||
SetFortification(ptr + sizeof(struct Header) + FORTIFY_BEFORE_SIZE,
|
||||
FILL_ON_MALLOC_VALUE, size);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We return the address of the user's memory, not the start of the block,
|
||||
* which points to our magic cookies
|
||||
*/
|
||||
|
||||
FORTIFY_UNLOCK();
|
||||
|
||||
return(ptr + sizeof(struct Header) + FORTIFY_BEFORE_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fortify_free() - This free must be used for all memory allocated with
|
||||
* Fortify_malloc().
|
||||
*
|
||||
* Features:
|
||||
* + Pointers are validated before attempting a free - the pointer
|
||||
* must point to a valid malloc'd bit of memory.
|
||||
* + Detects attempts at freeing the same block of memory twice
|
||||
* + Can clear out memory as it is free'd, to prevent code from using
|
||||
* the memory after it's been freed.
|
||||
* + Checks the sentinals of the memory being freed.
|
||||
* + Can check the sentinals of all memory.
|
||||
*/
|
||||
|
||||
void FORTIFY_STORAGE
|
||||
Fortify_free(void *uptr, char *file, unsigned long line)
|
||||
{
|
||||
unsigned char *ptr = (unsigned char *)uptr - sizeof(struct Header) - FORTIFY_BEFORE_SIZE;
|
||||
struct Header *h = (struct Header *)ptr;
|
||||
|
||||
FORTIFY_LOCK();
|
||||
|
||||
if(st_Disabled)
|
||||
{
|
||||
free(uptr);
|
||||
FORTIFY_UNLOCK();
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CHECK_ALL_MEMORY_ON_FREE
|
||||
Fortify_CheckAllMemory(file, line);
|
||||
#endif
|
||||
|
||||
#ifdef PARANOID_FREE
|
||||
if(!IsOnList(h))
|
||||
{
|
||||
sprintf(st_Buffer,
|
||||
"\nFortify: %s.%ld\n Invalid pointer, corrupted header, or possible free twice\n",
|
||||
file, line);
|
||||
st_Output(st_Buffer);
|
||||
OutputLastVerifiedPoint();
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(!CheckBlock(h, file, line))
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* Remove the block from the list
|
||||
*/
|
||||
if(h->Prev)
|
||||
{
|
||||
if(!CheckBlock(h->Prev, file, line))
|
||||
goto fail;
|
||||
|
||||
h->Prev->Next = h->Next;
|
||||
MakeHeaderValid(h->Prev);
|
||||
}
|
||||
else
|
||||
st_Head = h->Next;
|
||||
|
||||
if(h->Next)
|
||||
{
|
||||
if(!CheckBlock(h->Next, file, line))
|
||||
goto fail;
|
||||
|
||||
h->Next->Prev = h->Prev;
|
||||
MakeHeaderValid(h->Next);
|
||||
}
|
||||
|
||||
#ifdef FILL_ON_FREE
|
||||
/*
|
||||
* Nuke out all memory that is about to be freed
|
||||
*/
|
||||
SetFortification(ptr, FILL_ON_FREE_VALUE,
|
||||
sizeof(struct Header) + FORTIFY_BEFORE_SIZE + h->Size + FORTIFY_AFTER_SIZE);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* And do the actual free
|
||||
*/
|
||||
free(ptr);
|
||||
FORTIFY_UNLOCK();
|
||||
return;
|
||||
|
||||
fail:
|
||||
sprintf(st_Buffer, " free(%p) failed\n", uptr);
|
||||
st_Output(st_Buffer);
|
||||
FORTIFY_UNLOCK();
|
||||
}
|
||||
|
||||
/*
|
||||
* Fortify_realloc() - Uses Fortify_malloc() and Fortify_free() to implement
|
||||
* realloc().
|
||||
*
|
||||
* Features:
|
||||
* + The realloc'd block is ALWAYS moved.
|
||||
* + The pointer passed to realloc() is verified in the same way that
|
||||
* Fortify_free() verifies pointers before it frees them.
|
||||
* + All the Fortify_malloc() and Fortify_free() protection
|
||||
*/
|
||||
void *FORTIFY_STORAGE
|
||||
Fortify_realloc(void *ptr, size_t new_size, char *file, unsigned long line)
|
||||
{
|
||||
void *new_ptr;
|
||||
struct Header *h = (struct Header *)
|
||||
((unsigned char *)ptr - sizeof(struct Header) - FORTIFY_BEFORE_SIZE);
|
||||
|
||||
if(st_Disabled)
|
||||
{
|
||||
FORTIFY_LOCK();
|
||||
new_ptr = realloc(ptr, new_size);
|
||||
FORTIFY_UNLOCK();
|
||||
return(new_ptr);
|
||||
}
|
||||
|
||||
if(!ptr)
|
||||
return(Fortify_malloc(new_size, file, line));
|
||||
|
||||
FORTIFY_LOCK();
|
||||
|
||||
if(!IsOnList(h))
|
||||
{
|
||||
sprintf(st_Buffer,
|
||||
"\nFortify: %s.%ld\n Invalid pointer or corrupted header passed to realloc\n",
|
||||
file, line);
|
||||
st_Output(st_Buffer);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if(!CheckBlock(h, file, line))
|
||||
goto fail;
|
||||
|
||||
new_ptr = Fortify_malloc(new_size, file, line);
|
||||
if(!new_ptr)
|
||||
{
|
||||
FORTIFY_UNLOCK();
|
||||
return(0);
|
||||
}
|
||||
|
||||
if(h->Size < new_size)
|
||||
memcpy(new_ptr, ptr, h->Size);
|
||||
else
|
||||
memcpy(new_ptr, ptr, new_size);
|
||||
|
||||
Fortify_free(ptr, file, line);
|
||||
FORTIFY_UNLOCK();
|
||||
return(new_ptr);
|
||||
|
||||
fail:
|
||||
sprintf(st_Buffer, " realloc(%p, %ld) failed\n", ptr, (unsigned long)new_size);
|
||||
st_Output(st_Buffer);
|
||||
FORTIFY_UNLOCK();
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fortifty_calloc() - Uses Fortify_malloc() to implement calloc(). Much
|
||||
* the same protection as Fortify_malloc().
|
||||
*/
|
||||
void *FORTIFY_STORAGE
|
||||
Fortify_calloc(size_t num, size_t size, char *file, unsigned long line)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
ptr = Fortify_malloc(num * size, file, line);
|
||||
|
||||
if(ptr)
|
||||
memset(ptr, 0, num * size);
|
||||
|
||||
return(ptr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fortify_CheckPointer() - Returns true if the uptr points to a valid
|
||||
* piece of Fortify_malloc()'d memory. The memory must be on the malloc'd
|
||||
* list, and it's sentinals must be in tact.
|
||||
* If anything is wrong, an error message is issued.
|
||||
*
|
||||
* (Note - if fortify is disabled, this function always returns true).
|
||||
*/
|
||||
int FORTIFY_STORAGE
|
||||
Fortify_CheckPointer(void *uptr, char *file, unsigned long line)
|
||||
{
|
||||
unsigned char *ptr = (unsigned char *)uptr - sizeof(struct Header) - FORTIFY_BEFORE_SIZE;
|
||||
int r;
|
||||
|
||||
if(st_Disabled)
|
||||
return(1);
|
||||
|
||||
FORTIFY_LOCK();
|
||||
|
||||
if(!IsOnList((struct Header *)ptr))
|
||||
{
|
||||
sprintf(st_Buffer,
|
||||
"\nFortify: %s.%ld\n Invalid pointer or corrupted header detected (%p)\n",
|
||||
file, line, uptr);
|
||||
st_Output(st_Buffer);
|
||||
FORTIFY_UNLOCK();
|
||||
return(0);
|
||||
}
|
||||
|
||||
r = CheckBlock((struct Header *)ptr, file, line);
|
||||
FORTIFY_UNLOCK();
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fortify_SetOutputFunc(OutputFuncPtr Output) - Sets the function used to
|
||||
* output all error and diagnostic messages by fortify. The output function
|
||||
* takes a single unsigned char * argument, and must be able to handle newlines.
|
||||
* The function returns the old pointer.
|
||||
*/
|
||||
Fortify_OutputFuncPtr FORTIFY_STORAGE
|
||||
Fortify_SetOutputFunc(Fortify_OutputFuncPtr Output)
|
||||
{
|
||||
OutputFuncPtr Old = st_Output;
|
||||
|
||||
st_Output = (OutputFuncPtr)Output;
|
||||
|
||||
return((Fortify_OutputFuncPtr)Old);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fortify_SetMallocFailRate(int Percent) - Fortify_malloc() will make the
|
||||
* malloc attempt 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.
|
||||
*/
|
||||
int FORTIFY_STORAGE
|
||||
Fortify_SetMallocFailRate(int Percent)
|
||||
{
|
||||
int Old = st_MallocFailRate;
|
||||
|
||||
st_MallocFailRate = Percent;
|
||||
|
||||
return(Old);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Fortify_CheckAllMemory() - Checks the sentinals of all malloc'd memory.
|
||||
* Returns the number of blocks that failed.
|
||||
*
|
||||
* (If Fortify is disabled, this function always returns 0).
|
||||
*/
|
||||
int FORTIFY_STORAGE
|
||||
Fortify_CheckAllMemory(char *file, unsigned long line)
|
||||
{
|
||||
struct Header *curr = st_Head;
|
||||
int count = 0;
|
||||
|
||||
if(st_Disabled)
|
||||
return(0);
|
||||
|
||||
FORTIFY_LOCK();
|
||||
|
||||
while(curr)
|
||||
{
|
||||
if(!CheckBlock(curr, file, line))
|
||||
count++;
|
||||
|
||||
curr = curr->Next;
|
||||
}
|
||||
|
||||
if(file)
|
||||
{
|
||||
st_LastVerifiedFile = file;
|
||||
st_LastVerifiedLine = line;
|
||||
}
|
||||
|
||||
FORTIFY_UNLOCK();
|
||||
return(count);
|
||||
}
|
||||
|
||||
/* Fortify_EnterScope - enters a new Fortify scope level.
|
||||
* returns the new scope level.
|
||||
*/
|
||||
int FORTIFY_STORAGE
|
||||
Fortify_EnterScope(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.
|
||||
*/
|
||||
int FORTIFY_STORAGE
|
||||
Fortify_LeaveScope(char *file, unsigned long line)
|
||||
{
|
||||
struct Header *curr = st_Head;
|
||||
int count = 0;
|
||||
unsigned long size = 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 Dump at %s.%ld\n", file, line);
|
||||
st_Output(st_Buffer);
|
||||
OutputLastVerifiedPoint();
|
||||
sprintf(st_Buffer, "%11s %8s %s\n", "Address", "Size", "Allocator");
|
||||
st_Output(st_Buffer);
|
||||
}
|
||||
|
||||
OutputHeader(curr);
|
||||
count++;
|
||||
size += curr->Size;
|
||||
}
|
||||
|
||||
curr = curr->Next;
|
||||
}
|
||||
|
||||
if(count)
|
||||
{
|
||||
sprintf(st_Buffer, "%11s %8ld bytes overhead\n", "and",
|
||||
(unsigned long)(count * (sizeof(struct Header) + FORTIFY_BEFORE_SIZE + FORTIFY_AFTER_SIZE)));
|
||||
st_Output(st_Buffer);
|
||||
|
||||
sprintf(st_Buffer,"%11s %8ld bytes in %d blocks\n", "total", size, count);
|
||||
st_Output(st_Buffer);
|
||||
}
|
||||
|
||||
FORTIFY_UNLOCK();
|
||||
return(count);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fortify_OutputAllMemory() - Outputs the entire list of currently
|
||||
* malloc'd memory. For each malloc'd 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.
|
||||
*/
|
||||
int FORTIFY_STORAGE
|
||||
Fortify_OutputAllMemory(char *file, unsigned long line)
|
||||
{
|
||||
struct Header *curr = st_Head;
|
||||
int count = 0;
|
||||
unsigned long size = 0;
|
||||
|
||||
if(st_Disabled)
|
||||
return(0);
|
||||
|
||||
FORTIFY_LOCK();
|
||||
|
||||
if(curr)
|
||||
{
|
||||
sprintf(st_Buffer, "\nFortify: Memory Dump at %s.%ld\n", file, line);
|
||||
st_Output(st_Buffer);
|
||||
OutputLastVerifiedPoint();
|
||||
sprintf(st_Buffer, "%11s %8s %s\n", "Address", "Size", "Allocator");
|
||||
st_Output(st_Buffer);
|
||||
|
||||
while(curr)
|
||||
{
|
||||
OutputHeader(curr);
|
||||
count++;
|
||||
size += curr->Size;
|
||||
curr = curr->Next;
|
||||
}
|
||||
|
||||
sprintf(st_Buffer, "%11s %8ld bytes overhead\n", "and",
|
||||
(unsigned long)(count * (sizeof(struct Header) + FORTIFY_BEFORE_SIZE + FORTIFY_AFTER_SIZE)));
|
||||
st_Output(st_Buffer);
|
||||
|
||||
sprintf(st_Buffer,"%11s %8ld bytes in %d blocks\n", "total", size, count);
|
||||
st_Output(st_Buffer);
|
||||
}
|
||||
|
||||
FORTIFY_UNLOCK();
|
||||
return(count);
|
||||
}
|
||||
|
||||
/* Fortify_DumpAllMemory(Scope) - Outputs the entire list of currently
|
||||
* new'd memory within the specified scope. For each new'd 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.
|
||||
*
|
||||
* It returns the number of blocks on the list, unless Fortify has been
|
||||
* disabled, in which case it always returns 0.
|
||||
*/
|
||||
int FORTIFY_STORAGE
|
||||
Fortify_DumpAllMemory(int scope, char *file, unsigned long line)
|
||||
{
|
||||
struct Header *curr = st_Head;
|
||||
int count = 0;
|
||||
unsigned long size = 0;
|
||||
|
||||
if(st_Disabled)
|
||||
return(0);
|
||||
|
||||
FORTIFY_LOCK();
|
||||
|
||||
while(curr)
|
||||
{
|
||||
if(curr->Scope >= scope)
|
||||
{
|
||||
if(count == 0)
|
||||
{
|
||||
sprintf(st_Buffer, "\nFortify: Memory Dump at %s.%ld\n", file, line);
|
||||
st_Output(st_Buffer);
|
||||
OutputLastVerifiedPoint();
|
||||
sprintf(st_Buffer, "%11s %8s %s\n", "Address", "Size", "Allocator");
|
||||
st_Output(st_Buffer);
|
||||
}
|
||||
|
||||
OutputHeader(curr);
|
||||
OutputMemory(curr);
|
||||
st_Output("\n");
|
||||
count++;
|
||||
size += curr->Size;
|
||||
}
|
||||
|
||||
curr = curr->Next;
|
||||
}
|
||||
|
||||
if(count)
|
||||
{
|
||||
sprintf(st_Buffer, "%11s %8ld bytes overhead\n", "and",
|
||||
(unsigned long)(count * (sizeof(struct Header) + FORTIFY_BEFORE_SIZE + FORTIFY_AFTER_SIZE)));
|
||||
st_Output(st_Buffer);
|
||||
|
||||
sprintf(st_Buffer,"%11s %8ld bytes in %d blocks\n", "total", size, count);
|
||||
st_Output(st_Buffer);
|
||||
}
|
||||
|
||||
FORTIFY_UNLOCK();
|
||||
return(count);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fortify_Disable() - This function provides a mechanism to disable Fortify
|
||||
* without recompiling all the sourcecode. It can only be called, though,
|
||||
* when there is no memory on the Fortify malloc'd list. (Ideally, at the
|
||||
* start of the program before any memory has been allocated). If you
|
||||
* call this function when there IS memory on the Fortify malloc'd list,
|
||||
* it will issue an error, and fortify will not be disabled.
|
||||
*/
|
||||
int FORTIFY_STORAGE
|
||||
Fortify_Disable(char *file, unsigned long line)
|
||||
{
|
||||
int result;
|
||||
FORTIFY_LOCK();
|
||||
|
||||
if(st_Head)
|
||||
{
|
||||
sprintf(st_Buffer, "Fortify: %s.%d\n", file, line);
|
||||
st_Output(st_Buffer);
|
||||
st_Output(" Fortify_Disable failed\n");
|
||||
st_Output(" (because there is memory on the Fortify memory list)\n");
|
||||
|
||||
Fortify_OutputAllMemory(file, line);
|
||||
result = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
st_Disabled = 1;
|
||||
result = 1;
|
||||
}
|
||||
|
||||
FORTIFY_UNLOCK();
|
||||
return(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check a block's header and fortifications.
|
||||
*/
|
||||
static int CheckBlock(struct Header *h, char *file, unsigned long line)
|
||||
{
|
||||
unsigned char *ptr = (unsigned char *)h;
|
||||
int result = 1;
|
||||
|
||||
if(!IsHeaderValid(h))
|
||||
{
|
||||
sprintf(st_Buffer,
|
||||
"\nFortify: %s.%ld\n Invalid pointer or corrupted header detected (%p)\n",
|
||||
file, line, ptr + sizeof(struct Header) + FORTIFY_BEFORE_SIZE);
|
||||
st_Output(st_Buffer);
|
||||
OutputLastVerifiedPoint();
|
||||
return(0);
|
||||
}
|
||||
|
||||
if(!CheckFortification(ptr + sizeof(struct Header),
|
||||
FORTIFY_BEFORE_VALUE, FORTIFY_BEFORE_SIZE))
|
||||
{
|
||||
sprintf(st_Buffer,
|
||||
"\nFortify: %s.%ld\n Memory overrun detected before block\n",
|
||||
file, line);
|
||||
st_Output(st_Buffer);
|
||||
|
||||
sprintf(st_Buffer," (%p,%ld,%s.%ld)\n",
|
||||
ptr + sizeof(struct Header) + FORTIFY_BEFORE_SIZE,
|
||||
(unsigned long)h->Size, h->File, h->Line);
|
||||
st_Output(st_Buffer);
|
||||
|
||||
OutputFortification(ptr + sizeof(struct Header),
|
||||
FORTIFY_BEFORE_VALUE, FORTIFY_BEFORE_SIZE);
|
||||
OutputLastVerifiedPoint();
|
||||
result = 0;
|
||||
}
|
||||
|
||||
if(!CheckFortification(ptr + sizeof(struct Header) + FORTIFY_BEFORE_SIZE + h->Size,
|
||||
FORTIFY_AFTER_VALUE, FORTIFY_AFTER_SIZE))
|
||||
{
|
||||
sprintf(st_Buffer, "\nFortify: %s.%ld\n Memory overrun detected after block\n",
|
||||
file, line);
|
||||
st_Output(st_Buffer);
|
||||
|
||||
sprintf(st_Buffer," (%p,%ld,%s.%ld)\n",
|
||||
ptr + sizeof(struct Header) + FORTIFY_BEFORE_SIZE,
|
||||
(unsigned long)h->Size, h->File, h->Line);
|
||||
st_Output(st_Buffer);
|
||||
|
||||
OutputFortification(ptr + sizeof(struct Header) + FORTIFY_BEFORE_SIZE + h->Size,
|
||||
FORTIFY_AFTER_VALUE, FORTIFY_AFTER_SIZE);
|
||||
OutputLastVerifiedPoint();
|
||||
result = 0;
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if the _size_ bytes from _ptr_ are all set to _value_
|
||||
*/
|
||||
static int CheckFortification(unsigned char *ptr, unsigned char value, size_t size)
|
||||
{
|
||||
while(size--)
|
||||
if(*ptr++ != value)
|
||||
return(0);
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the _size_ bytes from _ptr_ to _value_.
|
||||
*/
|
||||
static void SetFortification(unsigned char *ptr, unsigned char value, size_t size)
|
||||
{
|
||||
memset(ptr, value, size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Output the corrupted section of the fortification
|
||||
*/
|
||||
/* Output the corrupted section of the fortification */
|
||||
static void
|
||||
OutputFortification(unsigned char *ptr, unsigned char value, size_t size)
|
||||
{
|
||||
unsigned long offset, column;
|
||||
char ascii[17];
|
||||
|
||||
st_Output("Address Offset Data");
|
||||
|
||||
offset = 0;
|
||||
column = 0;
|
||||
|
||||
while(offset < size)
|
||||
{
|
||||
if(column == 0)
|
||||
{
|
||||
sprintf(st_Buffer, "\n%8p %8d ", ptr, offset);
|
||||
st_Output(st_Buffer);
|
||||
}
|
||||
|
||||
sprintf(st_Buffer, "%02x ", *ptr);
|
||||
st_Output(st_Buffer);
|
||||
|
||||
ascii[ column ] = isprint( *ptr ) ? (char)(*ptr) : (char)(' ');
|
||||
ascii[ column + 1 ] = '\0';
|
||||
|
||||
ptr++;
|
||||
offset++;
|
||||
column++;
|
||||
|
||||
if(column == 16)
|
||||
{
|
||||
st_Output( " \"" );
|
||||
st_Output( ascii );
|
||||
st_Output( "\"" );
|
||||
column = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ( column != 0 )
|
||||
{
|
||||
while ( column ++ < 16 )
|
||||
{
|
||||
st_Output( " " );
|
||||
}
|
||||
st_Output( " \"" );
|
||||
st_Output( ascii );
|
||||
st_Output( "\"" );
|
||||
}
|
||||
|
||||
st_Output("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the supplied pointer does indeed point to a real Header
|
||||
*/
|
||||
static int IsHeaderValid(struct Header *h)
|
||||
{
|
||||
return(!ChecksumHeader(h));
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates the checksum to make the header valid
|
||||
*/
|
||||
static void MakeHeaderValid(struct Header *h)
|
||||
{
|
||||
h->Checksum = 0;
|
||||
h->Checksum = -ChecksumHeader(h);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate (and return) the checksum of the header. (Including the Checksum
|
||||
* variable itself. If all is well, the checksum returned by this function should
|
||||
* be 0.
|
||||
*/
|
||||
static int ChecksumHeader(struct Header *h)
|
||||
{
|
||||
int c, checksum, *p;
|
||||
|
||||
for(c = 0, checksum = 0, p = (int *)h; c < sizeof(struct Header)/sizeof(int); c++)
|
||||
checksum += *p++;
|
||||
|
||||
return(checksum);
|
||||
}
|
||||
|
||||
/*
|
||||
* Examines the malloc'd list to see if the given header is on it.
|
||||
*/
|
||||
static int IsOnList(struct Header *h)
|
||||
{
|
||||
struct Header *curr;
|
||||
|
||||
curr = st_Head;
|
||||
while(curr)
|
||||
{
|
||||
if(curr == h)
|
||||
return(1);
|
||||
|
||||
curr = curr->Next;
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Hex and ascii dump the memory
|
||||
*/
|
||||
static void
|
||||
OutputMemory(struct Header *h)
|
||||
{
|
||||
OutputFortification((unsigned char*)h + sizeof(struct Header) + FORTIFY_BEFORE_SIZE,
|
||||
0, h->Size);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Output the header...
|
||||
*/
|
||||
static void OutputHeader(struct Header *h)
|
||||
{
|
||||
sprintf(st_Buffer, "%11p %8ld %s.%ld (%d)\n",
|
||||
(unsigned char*)h + sizeof(struct Header) + FORTIFY_BEFORE_SIZE,
|
||||
(unsigned long)h->Size,
|
||||
h->File, h->Line, h->Scope);
|
||||
st_Output(st_Buffer);
|
||||
}
|
||||
|
||||
static void OutputLastVerifiedPoint()
|
||||
{
|
||||
sprintf(st_Buffer, "\nLast Verified point: %s.%ld\n",
|
||||
st_LastVerifiedFile,
|
||||
st_LastVerifiedLine);
|
||||
st_Output(st_Buffer);
|
||||
}
|
||||
|
||||
|
||||
#endif /* FORTIFY */
|
Reference in New Issue
Block a user