diff options
Diffstat (limited to 'gnu/usr.sbin/sendmail/libsm/heap.c')
-rw-r--r-- | gnu/usr.sbin/sendmail/libsm/heap.c | 819 |
1 files changed, 819 insertions, 0 deletions
diff --git a/gnu/usr.sbin/sendmail/libsm/heap.c b/gnu/usr.sbin/sendmail/libsm/heap.c new file mode 100644 index 00000000000..27ab6c767e7 --- /dev/null +++ b/gnu/usr.sbin/sendmail/libsm/heap.c @@ -0,0 +1,819 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + */ + +#include <sm/gen.h> +SM_RCSID("@(#)$Sendmail: heap.c,v 1.47 2001/09/04 22:41:27 ca Exp $") + +/* +** debugging memory allocation package +** See heap.html for documentation. +*/ + +#include <string.h> + +#include <sm/assert.h> +#include <sm/debug.h> +#include <sm/exc.h> +#include <sm/heap.h> +#include <sm/io.h> +#include <sm/signal.h> +#include <sm/xtrap.h> + +/* undef all macro versions of the "functions" so they can be specified here */ +#undef sm_malloc +#undef sm_malloc_x +#undef sm_malloc_tagged +#undef sm_malloc_tagged_x +#undef sm_free +#undef sm_free_tagged +#undef sm_realloc +#if SM_HEAP_CHECK +# undef sm_heap_register +# undef sm_heap_checkptr +# undef sm_heap_report +#endif /* SM_HEAP_CHECK */ + +#if SM_HEAP_CHECK +SM_DEBUG_T SmHeapCheck = SM_DEBUG_INITIALIZER("sm_check_heap", + "@(#)$Debug: sm_check_heap - check sm_malloc, sm_realloc, sm_free calls $"); +# define HEAP_CHECK sm_debug_active(&SmHeapCheck, 1) +#endif /* SM_HEAP_CHECK */ + +const SM_EXC_TYPE_T SmHeapOutOfMemoryType = +{ + SmExcTypeMagic, + "F:sm.heap", + "", + sm_etype_printf, + "out of memory", +}; + +SM_EXC_T SmHeapOutOfMemory = SM_EXC_INITIALIZER(&SmHeapOutOfMemoryType, NULL); + + +/* +** The behaviour of malloc with size==0 is platform dependent (it +** says so in the C standard): it can return NULL or non-NULL. We +** don't want sm_malloc_x(0) to raise an exception on some platforms +** but not others, so this case requires special handling. We've got +** two choices: "size = 1" or "return NULL". We use the former in the +** following. +** If we had something like autoconf we could figure out the +** behaviour of the platform and either use this hack or just +** use size. +*/ + +#define MALLOC_SIZE(size) ((size) == 0 ? 1 : (size)) + +/* +** SM_MALLOC_X -- wrapper around malloc(), raises an exception on error. +** +** Parameters: +** size -- size of requested memory. +** +** Returns: +** Pointer to memory region. +** +** Note: +** sm_malloc_x only gets called from source files in which heap +** debugging is disabled at compile time. Otherwise, a call to +** sm_malloc_x is macro expanded to a call to sm_malloc_tagged_x. +** +** Exceptions: +** F:sm_heap -- out of memory +*/ + +void * +sm_malloc_x(size) + size_t size; +{ + void *ptr; + + ENTER_CRITICAL(); + ptr = malloc(MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + if (ptr == NULL) + sm_exc_raise_x(&SmHeapOutOfMemory); + return ptr; +} + +#if !SM_HEAP_CHECK + +/* +** SM_MALLOC -- wrapper around malloc() +** +** Parameters: +** size -- size of requested memory. +** +** Returns: +** Pointer to memory region. +*/ + +void * +sm_malloc(size) + size_t size; +{ + void *ptr; + + ENTER_CRITICAL(); + ptr = malloc(MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + return ptr; +} + +/* +** SM_REALLOC -- wrapper for realloc() +** +** Parameters: +** ptr -- pointer to old memory area. +** size -- size of requested memory. +** +** Returns: +** Pointer to new memory area, NULL on failure. +*/ + +void * +sm_realloc(ptr, size) + void *ptr; + size_t size; +{ + void *newptr; + + ENTER_CRITICAL(); + newptr = realloc(ptr, MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + return newptr; +} + +/* +** SM_REALLOC_X -- wrapper for realloc() +** +** Parameters: +** ptr -- pointer to old memory area. +** size -- size of requested memory. +** +** Returns: +** Pointer to new memory area. +** +** Exceptions: +** F:sm_heap -- out of memory +*/ + +void * +sm_realloc_x(ptr, size) + void *ptr; + size_t size; +{ + void *newptr; + + ENTER_CRITICAL(); + newptr = realloc(ptr, MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + if (newptr == NULL) + sm_exc_raise_x(&SmHeapOutOfMemory); + return newptr; +} +/* +** SM_FREE -- wrapper around free() +** +** Parameters: +** ptr -- pointer to memory region. +** +** Returns: +** none. +*/ + +void +sm_free(ptr) + void *ptr; +{ + if (ptr == NULL) + return; + ENTER_CRITICAL(); + free(ptr); + LEAVE_CRITICAL(); + return; +} + +#else /* !SM_HEAP_CHECK */ + +/* +** Each allocated block is assigned a "group number". +** By default, all blocks are assigned to group #1. +** By convention, group #0 is for memory that is never freed. +** You can use group numbers any way you want, in order to help make +** sense of sm_heap_report output. +*/ + +int SmHeapGroup = 1; +int SmHeapMaxGroup = 1; + +/* +** Total number of bytes allocated. +** This is only maintained if the sm_check_heap debug category is active. +*/ + +size_t SmHeapTotal = 0; + +/* +** High water mark: the most that SmHeapTotal has ever been. +*/ + +size_t SmHeapMaxTotal = 0; + +/* +** Maximum number of bytes that may be allocated at any one time. +** 0 means no limit. +** This is only honoured if sm_check_heap is active. +*/ + +SM_DEBUG_T SmHeapLimit = SM_DEBUG_INITIALIZER("sm_heap_limit", + "@(#)$Debug: sm_heap_limit - max # of bytes permitted in heap $"); + +/* +** This is the data structure that keeps track of all currently +** allocated blocks of memory known to the heap package. +*/ + +typedef struct sm_heap_item SM_HEAP_ITEM_T; +struct sm_heap_item +{ + void *hi_ptr; + size_t hi_size; + char *hi_tag; + int hi_num; + int hi_group; + SM_HEAP_ITEM_T *hi_next; +}; + +#define SM_HEAP_TABLE_SIZE 256 +static SM_HEAP_ITEM_T *SmHeapTable[SM_HEAP_TABLE_SIZE]; + +/* +** This is a randomly generated table +** which contains exactly one occurrence +** of each of the numbers between 0 and 255. +** It is used by ptrhash. +*/ + +static unsigned char hashtab[SM_HEAP_TABLE_SIZE] = +{ + 161, 71, 77,187, 15,229, 9,176,221,119,239, 21, 85,138,203, 86, + 102, 65, 80,199,235, 32,140, 96,224, 78,126,127,144, 0, 11,179, + 64, 30,120, 23,225,226, 33, 50,205,167,130,240,174, 99,206, 73, + 231,210,189,162, 48, 93,246, 54,213,141,135, 39, 41,192,236,193, + 157, 88, 95,104,188, 63,133,177,234,110,158,214,238,131,233, 91, + 125, 82, 94, 79, 66, 92,151, 45,252, 98, 26,183, 7,191,171,106, + 145,154,251,100,113, 5, 74, 62, 76,124, 14,217,200, 75,115,190, + 103, 28,198,196,169,219, 37,118,150, 18,152,175, 49,136, 6,142, + 89, 19,243,254, 47,137, 24,166,180, 10, 40,186,202, 46,184, 67, + 148,108,181, 81, 25,241, 13,139, 58, 38, 84,253,201, 12,116, 17, + 195, 22,112, 69,255, 43,147,222,111, 56,194,216,149,244, 42,173, + 232,220,249,105,207, 51,197,242, 72,211,208, 59,122,230,237,170, + 165, 44, 68,123,129,245,143,101, 8,209,215,247,185, 57,218, 53, + 114,121, 3,128, 4,204,212,146, 2,155, 83,250, 87, 29, 31,159, + 60, 27,107,156,227,182, 1, 61, 36,160,109, 97, 90, 20,168,132, + 223,248, 70,164, 55,172, 34, 52,163,117, 35,153,134, 16,178,228 +}; + +/* +** PTRHASH -- hash a pointer value +** +** Parameters: +** p -- pointer. +** +** Returns: +** hash value. +** +** ptrhash hashes a pointer value to a uniformly distributed random +** number between 0 and 255. +** +** This hash algorithm is based on Peter K. Pearson, +** "Fast Hashing of Variable-Length Text Strings", +** in Communications of the ACM, June 1990, vol 33 no 6. +*/ + +static int +ptrhash(p) + void *p; +{ + int h; + + if (sizeof(void*) == 4 && sizeof(unsigned long) == 4) + { + unsigned long n = (unsigned long)p; + + h = hashtab[n & 0xFF]; + h = hashtab[h ^ ((n >> 8) & 0xFF)]; + h = hashtab[h ^ ((n >> 16) & 0xFF)]; + h = hashtab[h ^ ((n >> 24) & 0xFF)]; + } +# if 0 + else if (sizeof(void*) == 8 && sizeof(unsigned long) == 8) + { + unsigned long n = (unsigned long)p; + + h = hashtab[n & 0xFF]; + h = hashtab[h ^ ((n >> 8) & 0xFF)]; + h = hashtab[h ^ ((n >> 16) & 0xFF)]; + h = hashtab[h ^ ((n >> 24) & 0xFF)]; + h = hashtab[h ^ ((n >> 32) & 0xFF)]; + h = hashtab[h ^ ((n >> 40) & 0xFF)]; + h = hashtab[h ^ ((n >> 48) & 0xFF)]; + h = hashtab[h ^ ((n >> 56) & 0xFF)]; + } +# endif /* 0 */ + else + { + unsigned char *cp = (unsigned char *)&p; + int i; + + h = 0; + for (i = 0; i < sizeof(void*); ++i) + h = hashtab[h ^ cp[i]]; + } + return h; +} + +/* +** SM_MALLOC_TAGGED -- wrapper around malloc(), debugging version. +** +** Parameters: +** size -- size of requested memory. +** tag -- tag for debugging. +** num -- additional value for debugging. +** group -- heap group for debugging. +** +** Returns: +** Pointer to memory region. +*/ + +void * +sm_malloc_tagged(size, tag, num, group) + size_t size; + char *tag; + int num; + int group; +{ + void *ptr; + + if (!HEAP_CHECK) + { + ENTER_CRITICAL(); + ptr = malloc(MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + return ptr; + } + + if (sm_xtrap_check()) + return NULL; + if (sm_debug_active(&SmHeapLimit, 1) + && sm_debug_level(&SmHeapLimit) < SmHeapTotal + size) + return NULL; + ENTER_CRITICAL(); + ptr = malloc(MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + if (ptr != NULL && !sm_heap_register(ptr, size, tag, num, group)) + { + ENTER_CRITICAL(); + free(ptr); + LEAVE_CRITICAL(); + ptr = NULL; + } + SmHeapTotal += size; + if (SmHeapTotal > SmHeapMaxTotal) + SmHeapMaxTotal = SmHeapTotal; + return ptr; +} + +/* +** SM_MALLOC_TAGGED_X -- wrapper around malloc(), debugging version. +** +** Parameters: +** size -- size of requested memory. +** tag -- tag for debugging. +** num -- additional value for debugging. +** group -- heap group for debugging. +** +** Returns: +** Pointer to memory region. +** +** Exceptions: +** F:sm_heap -- out of memory +*/ + +void * +sm_malloc_tagged_x(size, tag, num, group) + size_t size; + char *tag; + int num; + int group; +{ + void *ptr; + + if (!HEAP_CHECK) + { + ENTER_CRITICAL(); + ptr = malloc(MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + if (ptr == NULL) + sm_exc_raise_x(&SmHeapOutOfMemory); + return ptr; + } + + sm_xtrap_raise_x(&SmHeapOutOfMemory); + if (sm_debug_active(&SmHeapLimit, 1) + && sm_debug_level(&SmHeapLimit) < SmHeapTotal + size) + { + sm_exc_raise_x(&SmHeapOutOfMemory); + } + ENTER_CRITICAL(); + ptr = malloc(MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + if (ptr != NULL && !sm_heap_register(ptr, size, tag, num, group)) + { + ENTER_CRITICAL(); + free(ptr); + LEAVE_CRITICAL(); + ptr = NULL; + } + if (ptr == NULL) + sm_exc_raise_x(&SmHeapOutOfMemory); + SmHeapTotal += size; + if (SmHeapTotal > SmHeapMaxTotal) + SmHeapMaxTotal = SmHeapTotal; + return ptr; +} + +/* +** SM_HEAP_REGISTER -- register a pointer into the heap for debugging. +** +** Parameters: +** ptr -- pointer to register. +** size -- size of requested memory. +** tag -- tag for debugging. +** num -- additional value for debugging. +** group -- heap group for debugging. +** +** Returns: +** true iff successfully registered (not yet in table). +*/ + +bool +sm_heap_register(ptr, size, tag, num, group) + void *ptr; + size_t size; + char *tag; + int num; + int group; +{ + int i; + SM_HEAP_ITEM_T *hi; + + if (!HEAP_CHECK) + return true; + SM_REQUIRE(ptr != NULL); + i = ptrhash(ptr); +# if SM_CHECK_REQUIRE + + /* + ** We require that ptr is not already in SmHeapTable. + */ + + for (hi = SmHeapTable[i]; hi != NULL; hi = hi->hi_next) + { + if (hi->hi_ptr == ptr) + sm_abort("sm_heap_register: ptr %p is already registered (%s:%d)", + ptr, hi->hi_tag, hi->hi_num); + } +# endif /* SM_CHECK_REQUIRE */ + ENTER_CRITICAL(); + hi = (SM_HEAP_ITEM_T *) malloc(sizeof(SM_HEAP_ITEM_T)); + LEAVE_CRITICAL(); + if (hi == NULL) + return false; + hi->hi_ptr = ptr; + hi->hi_size = size; + hi->hi_tag = tag; + hi->hi_num = num; + hi->hi_group = group; + hi->hi_next = SmHeapTable[i]; + SmHeapTable[i] = hi; + return true; +} +/* +** SM_REALLOC -- wrapper for realloc(), debugging version. +** +** Parameters: +** ptr -- pointer to old memory area. +** size -- size of requested memory. +** +** Returns: +** Pointer to new memory area, NULL on failure. +*/ + +void * +sm_realloc(ptr, size) + void *ptr; + size_t size; +{ + void *newptr; + SM_HEAP_ITEM_T *hi, **hp; + + if (!HEAP_CHECK) + { + ENTER_CRITICAL(); + newptr = realloc(ptr, MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + return newptr; + } + + if (ptr == NULL) + return sm_malloc_tagged(size, "realloc", 0, SmHeapGroup); + + for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next) + { + if ((**hp).hi_ptr == ptr) + { + if (sm_xtrap_check()) + return NULL; + hi = *hp; + if (sm_debug_active(&SmHeapLimit, 1) + && sm_debug_level(&SmHeapLimit) + < SmHeapTotal - hi->hi_size + size) + { + return NULL; + } + ENTER_CRITICAL(); + newptr = realloc(ptr, MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + if (newptr == NULL) + return NULL; + SmHeapTotal = SmHeapTotal - hi->hi_size + size; + if (SmHeapTotal > SmHeapMaxTotal) + SmHeapMaxTotal = SmHeapTotal; + *hp = hi->hi_next; + hi->hi_ptr = newptr; + hi->hi_size = size; + hp = &SmHeapTable[ptrhash(newptr)]; + hi->hi_next = *hp; + *hp = hi; + return newptr; + } + } + sm_abort("sm_realloc: bad argument (%p)", ptr); + /* NOTREACHED */ + return NULL; /* keep Irix compiler happy */ +} + +/* +** SM_REALLOC_X -- wrapper for realloc(), debugging version. +** +** Parameters: +** ptr -- pointer to old memory area. +** size -- size of requested memory. +** +** Returns: +** Pointer to new memory area. +** +** Exceptions: +** F:sm_heap -- out of memory +*/ + +void * +sm_realloc_x(ptr, size) + void *ptr; + size_t size; +{ + void *newptr; + SM_HEAP_ITEM_T *hi, **hp; + + if (!HEAP_CHECK) + { + ENTER_CRITICAL(); + newptr = realloc(ptr, MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + if (newptr == NULL) + sm_exc_raise_x(&SmHeapOutOfMemory); + return newptr; + } + + if (ptr == NULL) + return sm_malloc_tagged_x(size, "realloc", 0, SmHeapGroup); + + for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next) + { + if ((**hp).hi_ptr == ptr) + { + sm_xtrap_raise_x(&SmHeapOutOfMemory); + hi = *hp; + if (sm_debug_active(&SmHeapLimit, 1) + && sm_debug_level(&SmHeapLimit) + < SmHeapTotal - hi->hi_size + size) + { + sm_exc_raise_x(&SmHeapOutOfMemory); + } + ENTER_CRITICAL(); + newptr = realloc(ptr, MALLOC_SIZE(size)); + LEAVE_CRITICAL(); + if (newptr == NULL) + sm_exc_raise_x(&SmHeapOutOfMemory); + SmHeapTotal = SmHeapTotal - hi->hi_size + size; + if (SmHeapTotal > SmHeapMaxTotal) + SmHeapMaxTotal = SmHeapTotal; + *hp = hi->hi_next; + hi->hi_ptr = newptr; + hi->hi_size = size; + hp = &SmHeapTable[ptrhash(newptr)]; + hi->hi_next = *hp; + *hp = hi; + return newptr; + } + } + sm_abort("sm_realloc_x: bad argument (%p)", ptr); + /* NOTREACHED */ + return NULL; /* keep Irix compiler happy */ +} + +/* +** SM_FREE_TAGGED -- wrapper around free(), debugging version. +** +** Parameters: +** ptr -- pointer to memory region. +** tag -- tag for debugging. +** num -- additional value for debugging. +** +** Returns: +** none. +*/ + +void +sm_free_tagged(ptr, tag, num) + void *ptr; + char *tag; + int num; +{ + SM_HEAP_ITEM_T **hp; + + if (ptr == NULL) + return; + if (!HEAP_CHECK) + { + ENTER_CRITICAL(); + free(ptr); + LEAVE_CRITICAL(); + return; + } + for (hp = &SmHeapTable[ptrhash(ptr)]; *hp != NULL; hp = &(**hp).hi_next) + { + if ((**hp).hi_ptr == ptr) + { + SM_HEAP_ITEM_T *hi = *hp; + + *hp = hi->hi_next; + + /* + ** Fill the block with zeros before freeing. + ** This is intended to catch problems with + ** dangling pointers. The block is filled with + ** zeros, not with some non-zero value, because + ** it is common practice in some C code to store + ** a zero in a structure member before freeing the + ** structure, as a defense against dangling pointers. + */ + + (void) memset(ptr, 0, hi->hi_size); + SmHeapTotal -= hi->hi_size; + ENTER_CRITICAL(); + free(ptr); + free(hi); + LEAVE_CRITICAL(); + return; + } + } + sm_abort("sm_free: bad argument (%p) (%s:%d)", ptr, tag, num); +} + +/* +** SM_HEAP_CHECKPTR_TAGGED -- check whether ptr is a valid argument to sm_free +** +** Parameters: +** ptr -- pointer to memory region. +** tag -- tag for debugging. +** num -- additional value for debugging. +** +** Returns: +** none. +** +** Side Effects: +** aborts if check fails. +*/ + +void +sm_heap_checkptr_tagged(ptr, tag, num) + void *ptr; + char *tag; + int num; +{ + SM_HEAP_ITEM_T *hp; + + if (!HEAP_CHECK) + return; + if (ptr == NULL) + return; + for (hp = SmHeapTable[ptrhash(ptr)]; hp != NULL; hp = hp->hi_next) + { + if (hp->hi_ptr == ptr) + return; + } + sm_abort("sm_heap_checkptr(%p): bad ptr (%s:%d)", ptr, tag, num); +} + +/* +** SM_HEAP_REPORT -- output "map" of used heap. +** +** Parameters: +** stream -- the file pointer to write to. +** verbosity -- how much info? +** +** Returns: +** none. +*/ + +void +sm_heap_report(stream, verbosity) + SM_FILE_T *stream; + int verbosity; +{ + int i; + unsigned long group0total, group1total, otherstotal, grandtotal; + + if (!HEAP_CHECK || verbosity <= 0) + return; + group0total = group1total = otherstotal = grandtotal = 0; + for (i = 0; i < sizeof(SmHeapTable) / sizeof(SmHeapTable[0]); ++i) + { + SM_HEAP_ITEM_T *hi = SmHeapTable[i]; + + while (hi != NULL) + { + if (verbosity > 2 + || (verbosity > 1 && hi->hi_group != 0)) + { + sm_io_fprintf(stream, SM_TIME_DEFAULT, + "%4d %*lx %7lu bytes", + hi->hi_group, + (int) sizeof(void *) * 2, + (long)hi->hi_ptr, + (unsigned long)hi->hi_size); + if (hi->hi_tag != NULL) + { + sm_io_fprintf(stream, SM_TIME_DEFAULT, + " %s", + hi->hi_tag); + if (hi->hi_num) + { + sm_io_fprintf(stream, + SM_TIME_DEFAULT, + ":%d", + hi->hi_num); + } + } + sm_io_fprintf(stream, SM_TIME_DEFAULT, "\n"); + } + switch (hi->hi_group) + { + case 0: + group0total += hi->hi_size; + break; + case 1: + group1total += hi->hi_size; + break; + default: + otherstotal += hi->hi_size; + break; + } + grandtotal += hi->hi_size; + hi = hi->hi_next; + } + } + sm_io_fprintf(stream, SM_TIME_DEFAULT, + "heap max=%lu, total=%lu, ", + (unsigned long) SmHeapMaxTotal, grandtotal); + sm_io_fprintf(stream, SM_TIME_DEFAULT, + "group 0=%lu, group 1=%lu, others=%lu\n", + group0total, group1total, otherstotal); + if (grandtotal != SmHeapTotal) + { + sm_io_fprintf(stream, SM_TIME_DEFAULT, + "BUG => SmHeapTotal: got %lu, expected %lu\n", + (unsigned long) SmHeapTotal, grandtotal); + } +} +#endif /* !SM_HEAP_CHECK */ |