/* 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>


/* the user's options */
#include "ufortify.h"

/* Prototypes and such */
#define __FORTIFY_C__
#include "fortify.h"


/*
 * 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 (void);

#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;
static int st_ErrorsDetected = 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),
#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 *) (void *) ptr;
    st_MakeHeaderValid (st_AllocatedHead);
  }

  /*
   * Initialize and validate the header
   */
  h = (struct Header *) (void *) 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 *) (void *) 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 *) (void *) ptr;
      st_MakeHeaderValid (st_DeallocatedHead);
    }

    h = (struct Header *) (void *) 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 *) (void *) 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 *) (void *) 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 ();
    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);
}

#ifdef UNUSED_CODE
/*
 * Fortify_SetAllocateFailRate(int Percent) -
 * Fortify_Allocate() will "fail" this Percent of
 * the time, even if the memory is ACTIVE.
 * 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);
}
#endif /* UNUSED_CODE */


/*
 * 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);
}

int
Fortify_ErrorsDetected (void)
{
  return st_ErrorsDetected;
}

/* 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 ();
    st_ErrorsDetected = 1;
    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
    st_ErrorsDetected = 1;
  }

  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
    st_ErrorsDetected = 1;
  }

  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 (stderr, "%s", String);
  fflush (stderr);
}

/*
 * 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 *) (void *) uptr - FORTIFY_HEADER_SIZE -
    FORTIFY_ALIGNED_BEFORE_SIZE;
  struct Header *h = (struct Header *) (void *) 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 =
      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 (void)
{
#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 */

#else

extern int fortify_disabled (void);

int fortify_disabled (void);

#endif /* FORTIFY */
