dgaudet     97/10/14 17:19:36

  Modified:    src/main alloc.c
  Log:
  Add in alloc debugging code which can be used standalone to detect some
  uninitialized read, and use-after-free errors.  It can also be combined with
  debuggers like efence and Purify.  By default nothing should change.
  
  This change introduces one change to the non-debugging code:
  
  -    blok = new_block(0);
  +    blok = new_block(POOL_HDR_BYTES);
  
  This is during make_sub_pool.  Technically speaking, this fixes a bug;
  the bug was that make_sub_pool was assuming that
  BLOCK_MINALLOC > POOL_HDR_BYTES.  Not an unreasonable assumption ... but
  the debugging code sets BLOCK_MINALLOC to 0.
  
  Reviewed by:  Jim Jagielski, Rob Hartill, Martin Kraemer
  
  Revision  Changes    Path
  1.50      +109 -5    apachen/src/main/alloc.c
  
  Index: alloc.c
  ===================================================================
  RCS file: /export/home/cvs/apachen/src/main/alloc.c,v
  retrieving revision 1.49
  retrieving revision 1.50
  diff -u -r1.49 -r1.50
  --- alloc.c   1997/10/05 02:06:36     1.49
  +++ alloc.c   1997/10/15 00:19:35     1.50
  @@ -63,6 +63,37 @@
   
   #include <stdarg.h>
   
  +/* debugging support, define this to enable code which helps detect re-use
  + * of freed memory and other such nonsense.
  + *
  + * The theory is simple.  The FILL_BYTE (0xa5) is written over all malloc'd
  + * memory as we receive it, and is written over everything that we free up
  + * during a clear_pool.  We check that blocks on the free list always
  + * have the FILL_BYTE in them, and we check during palloc() that the bytes
  + * still have FILL_BYTE in them.  If you ever see garbage URLs or whatnot
  + * containing lots of 0xa5s then you know something used data that's been
  + * freed.
  + */
  +/* #define ALLOC_DEBUG */
  +
  +/* debugging support, if defined all allocations will be done with
  + * malloc and free()d appropriately at the end.  This is intended to be
  + * used with something like Electric Fence or Purify to help detect
  + * memory problems.  Note that if you're using efence then you should also
  + * add in ALLOC_DEBUG.  But don't add in ALLOC_DEBUG if you're using Purify
  + * because ALLOC_DEBUG would hide all the uninitialized read errors that
  + * Purify can diagnose.
  + */
  +/* #define ALLOC_USE_MALLOC */
  +
  +#ifdef ALLOC_USE_MALLOC
  +#undef BLOCK_MINFREE
  +#undef BLOCK_MINALLOC
  +#define BLOCK_MINFREE        0
  +#define BLOCK_MINALLOC       0
  +#endif
  +
  +
   /*****************************************************************
    *
    * Managing free storage blocks...
  @@ -98,6 +129,28 @@
   mutex *alloc_mutex = NULL;
   mutex *spawn_mutex = NULL;
   
  +#ifdef ALLOC_DEBUG
  +#define FILL_BYTE    ((char)(0xa5))
  +
  +#define debug_fill(ptr,size) ((void)memset((ptr), FILL_BYTE, (size)))
  +
  +static ap_inline void debug_verify_filled(const char *ptr,
  +    const char *endp, const char *error_msg)
  +{
  +    for (; ptr < endp; ++ptr) {
  +     if (*ptr != FILL_BYTE) {
  +         fputs(error_msg, stderr);
  +         abort();
  +         exit(1);
  +     }
  +    }
  +}
  +
  +#else
  +#define debug_fill(a,b)
  +#define debug_verify_filled(a,b,c)
  +#endif
  +
   
   /* Get a completely new block from the system pool. Note that we rely on
      malloc() to provide aligned memory. */
  @@ -111,6 +164,7 @@
        fprintf(stderr, "Ouch!  malloc failed in malloc_block()\n");
        exit(1);
       }
  +    debug_fill(blok, size + sizeof(union block_hdr));
       blok->h.next = NULL;
       blok->h.first_avail = (char *) (blok + 1);
       blok->h.endp = size + blok->h.first_avail;
  @@ -138,6 +192,14 @@
   
   void free_blocks(union block_hdr *blok)
   {
  +#ifdef ALLOC_USE_MALLOC
  +    union block_hdr *next;
  +
  +    for (; blok; blok = next) {
  +     next = blok->h.next;
  +     free(blok);
  +    }
  +#else
       /* First, put new blocks at the head of the free list ---
        * we'll eventually bash the 'next' pointer of the last block
        * in the chain to point to the free blocks we already had.
  @@ -161,21 +223,22 @@
       while (blok->h.next != NULL) {
        chk_on_blk_list(blok, old_free_list);
        blok->h.first_avail = (char *) (blok + 1);
  +     debug_fill(blok->h.first_avail, blok->h.endp - blok->h.first_avail);
        blok = blok->h.next;
       }
   
       chk_on_blk_list(blok, old_free_list);
       blok->h.first_avail = (char *) (blok + 1);
  +    debug_fill(blok->h.first_avail, blok->h.endp - blok->h.first_avail);
   
       /* Finally, reset next pointer to get the old free blocks back */
   
       blok->h.next = old_free_list;
       (void) release_mutex(alloc_mutex);
  +#endif
   }
   
   
  -
  -
   /* Get a new block, from our own free list if possible, from the system
    * if necessary.  Must be called with alarms blocked.
    */
  @@ -193,6 +256,8 @@
        if (min_size + BLOCK_MINFREE <= blok->h.endp - blok->h.first_avail) {
            *lastptr = blok->h.next;
            blok->h.next = NULL;
  +         debug_verify_filled(blok->h.first_avail, blok->h.endp,
  +             "Ouch!  Someone trounced a block on the free list!\n");
            return blok;
        }
        else {
  @@ -204,11 +269,13 @@
       /* Nope. */
   
       min_size += BLOCK_MINFREE;
  -    return malloc_block((min_size > BLOCK_MINALLOC) ? min_size : 
BLOCK_MINALLOC);
  +    blok = malloc_block((min_size > BLOCK_MINALLOC) ? min_size : 
BLOCK_MINALLOC);
  +    debug_verify_filled(blok->h.first_avail, blok->h.endp,
  +     "Ouch!  Someone trounced a block on the free list!\n");
  +    return blok;
   }
   
   
  -
   /* Accounting */
   
   long bytes_in_block_list(union block_hdr *blok)
  @@ -248,6 +315,9 @@
       struct pool *sub_prev;
       struct pool *parent;
       char *free_first_avail;
  +#ifdef ALLOC_USE_MALLOC
  +    void *allocation_list;
  +#endif
   };
   
   pool *permanent_pool;
  @@ -271,7 +341,7 @@
   
       (void) acquire_mutex(alloc_mutex);
   
  -    blok = new_block(0);
  +    blok = new_block(POOL_HDR_BYTES);
       new_pool = (pool *) blok->h.first_avail;
       blok->h.first_avail += POOL_HDR_BYTES;
   
  @@ -318,6 +388,20 @@
   
       a->last = a->first;
       a->first->h.first_avail = a->free_first_avail;
  +    debug_fill(a->first->h.first_avail,
  +     a->first->h.endp - a->first->h.first_avail);
  +
  +#ifdef ALLOC_USE_MALLOC
  +    {
  +     void *c, *n;
  +
  +     for (c = a->allocation_list; c; c = n) {
  +         n = *(void **)c;
  +         free(c);
  +     }
  +     a->allocation_list = NULL;
  +    }
  +#endif
   
       unblock_alarms();
   }
  @@ -357,6 +441,23 @@
   
   API_EXPORT(void *) palloc(struct pool *a, int reqsize)
   {
  +#ifdef ALLOC_USE_MALLOC
  +    int size = reqsize + CLICK_SZ;
  +    void *ptr;
  +
  +    block_alarms();
  +    ptr = malloc(size);
  +    if (ptr == NULL) {
  +     fputs("Ouch!  Out of memory!\n", stderr);
  +     exit(1);
  +    }
  +    debug_fill(ptr, size); /* might as well get uninitialized protection */
  +    *(void **)ptr = a->allocation_list;
  +    a->allocation_list = ptr;
  +    unblock_alarms();
  +    return (char *)ptr + CLICK_SZ;
  +#else
  +
       /* Round up requested size to an even number of alignment units (core 
clicks)
        */
   
  @@ -377,6 +478,8 @@
       new_first_avail = first_avail + size;
   
       if (new_first_avail <= blok->h.endp) {
  +     debug_verify_filled(first_avail, blok->h.endp,
  +         "Ouch!  Someone trounced past the end of their allocation!\n");
        blok->h.first_avail = new_first_avail;
        return (void *) first_avail;
       }
  @@ -399,6 +502,7 @@
       blok->h.first_avail += size;
   
       return (void *) first_avail;
  +#endif
   }
   
   API_EXPORT(void *) pcalloc(struct pool *a, int size)
  
  
  

Reply via email to