Date: Tue, 3 Jun 2003 10:59:35 -0500 (CDT) From: Dan Nelson <dnelson@allantgroup.com> To: FreeBSD-gnats-submit@FreeBSD.org Cc: phk@FreeBSD.org Subject: bin/52907: [PATCH] more malloc options for debugging programs Message-ID: <200306031559.h53FxZgS089446@dan.emsphone.com> Resent-Message-ID: <200306031600.h53G0SDP035247@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 52907 >Category: bin >Synopsis: [PATCH] more malloc options for debugging programs >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: change-request >Submitter-Id: current-users >Arrival-Date: Tue Jun 03 09:00:28 PDT 2003 >Closed-Date: >Last-Modified: >Originator: Dan Nelson >Release: FreeBSD 5.1-BETA i386 >Organization: The Allant Group >Environment: System: FreeBSD dan.emsphone.com 5.1-BETA FreeBSD 5.1-BETA #271: Thu May 29 16:33:28 CDT 2003 dan@dan.emsphone.com:/usr/src/sys/i386/compile/DANSMP i386 >Description: >How-To-Repeat: >Fix: This patch adds two things to malloc: The ability to mark each allocated and freed area with a unique byte pattern instead of a constant 0xd0, and the ability to die on command at a given malloc/free operation. The two options are collapsed onto the C malloc flag, but could easily enough be separated. Both make use of an internal running counter of how many calls to malloc/free/realloc have been made. To help differentiate the two types of junked data, the patch also alters the J flag to fill mallocs with 0xd0 and frees with 0xd1. "C" all by itself just sticks the counter in the low-order bytes of each 4-byte word when filling with junk. Very useful in conjunction with the next option: "C" with a numeric argument will cause malloc to abort() the Nth time malloc/free/realloc is called. For example, use "C1" to make sure you never malloc or free any data :) These two options are great for tracking down uninitialized pointers, and reuse of free'd memory. For example, say your program seg faulted on a bad pointer lookup (pointer value is 0xd0d0a492). Where's the bug? Run your program again with MALLOC_OPTIONS="CC0xa492", and you will get a coredump right at the malloc() call. Chances are the bug is nearby. I find it useful enough that on my system I have "J" do what "C" does, and C is only used to set the abort counter. Index: malloc.3 =================================================================== RCS file: /home/ncvs/src/lib/libc/stdlib/malloc.3,v retrieving revision 1.60 diff -u -p -r1.60 malloc.3 --- malloc.3 24 Dec 2002 13:41:45 -0000 1.60 +++ malloc.3 3 Jun 2003 15:10:13 -0000 @@ -177,19 +177,45 @@ flags being set) become fatal. The process will call .Xr abort 3 in these cases. +.It C +This option sets the +.Dq J +option, keeps a count of the total number of memory allocation calls, +and when filling memory with junk, initializes the low-order bytes in each +4-byte word with the running count. +For example, the 255th malloc operation will be initialized with 0xd0d0d0ff, +and the next free after that will have its memory set to 0xd1d10100. +Note that there is some ambiguity in interpreting these values; +0xd0d0d0ff could also have been the 53503rd or 13684991th operation. +.It Cn +Keep a count of the total number of memory allocation calls, +and abort at the nth operation. +n can be specified in decimal, or it can be hexidecimal (prefixed with 0x). +If a hex value is used, ensure that no options that may be confused with +hex digits follow this option. +A lowercase +.Dq c +will clear both the +.Dq C +and +.Dq Cn +options. +.Dq C0 +can be used to clear only this option, if needed. .It J Each byte of new memory allocated by .Fn malloc , .Fn realloc or -.Fn reallocf -as well as all memory returned by +.Fn reallocf +will be initialized to 0xd0. +All memory returned by .Fn free , .Fn realloc or .Fn reallocf -will be initialized to 0xd0. -This options also sets the +will be initialized to 0xd1. +This option also sets the .Dq R option. This is intended for debugging and will impact performance negatively. @@ -281,6 +307,12 @@ If the environment variable .Ev MALLOC_OPTIONS is set, the characters it contains will be interpreted as flags to the allocation functions. +.It Ev MALLOC_COUNT +If the environment variable +.Ev MALLOC_COUNT +is set, it will be interpreted as a count value (see the +.Dq Cn +option). This value will override any counts specified in MALLOC_OPTIONS. .El .Sh RETURN VALUES The Index: malloc.c =================================================================== RCS file: /home/ncvs/src/lib/libc/stdlib/malloc.c,v retrieving revision 1.76 diff -u -p -r1.76 malloc.c --- malloc.c 1 Jun 2003 09:16:50 -0000 1.76 +++ malloc.c 3 Jun 2003 15:21:12 -0000 @@ -26,7 +26,8 @@ __FBSDID("$FreeBSD: src/lib/libc/stdlib/ * What to use for Junk. This is the byte value we use to fill with * when the 'J' option is enabled. */ -#define SOME_JUNK 0xd0 /* as in "Duh" :-) */ +#define ALLOC_JUNK 0xd0 /* as in "Duh" :-) */ +#define FREE_JUNK 0xd1 /* as in "Die" :-) */ /* * The basic parameters you can tweak. @@ -248,6 +249,15 @@ static int malloc_zero; /* junk fill ? */ static int malloc_junk = 1; +/* junk fill with a counter ? */ +static int malloc_counter_junk = 0; + +/* keep track of the total number of malloc/realloc/frees done */ +static u_int32_t malloc_counter = 0; + +/* If this is nonzero, die when malloc_counter == malloc_counter_abort */ +static u_int32_t malloc_counter_abort = 0; + #ifdef HAS_UTRACE /* utrace ? */ @@ -288,6 +298,7 @@ static int extend_pgdir(u_long index); static void *imalloc(size_t size); static void ifree(void *ptr); static void *irealloc(void *ptr, size_t size); +static void fillrange(void *ptr, size_t size, u_int32_t value, u_char pad); static void wrtmessage(const char *p1, const char *p2, const char *p3, const char *p4) @@ -445,6 +456,16 @@ malloc_init () case '<': malloc_cache >>= 1; break; case 'a': malloc_abort = 0; break; case 'A': malloc_abort = 1; break; + case 'c': malloc_counter_abort = 0; malloc_counter_junk = 0; break; + case 'C': + if (p[1] >= '0' && p[1] <= '9') { + errnosave = errno; + malloc_counter_abort = strtoul(p+1, &p, 0); + errno = errnosave; + p--; /* decrement to compensate for the for loop */ + } else + malloc_counter_junk = 1; + break; case 'h': malloc_hint = 0; break; case 'H': malloc_hint = 1; break; case 'r': malloc_realloc = 0; break; @@ -471,6 +492,13 @@ malloc_init () } } + p = getenv("MALLOC_COUNT"); + if (p) { + errnosave = errno; + malloc_counter_abort = strtoul(p, NULL, 0); + errno = errnosave; + } + /* * Sensitive processes, somewhat arbitrarily defined here as setuid, * setgid, root and wheel cannot afford to have malloc mistakes. @@ -481,6 +509,13 @@ malloc_init () UTRACE(0, 0, 0); /* + * Filling junk with a running count implies filing with junk in the + * first place. + */ + if (malloc_counter_junk) + malloc_junk=1; + + /* * We want junk in the entire allocation, and zero only in the part * the user asked for. */ @@ -598,7 +633,7 @@ malloc_pages(size_t size) page_dir[index+i] = MALLOC_FOLLOW; if (malloc_junk) - memset(p, SOME_JUNK, size << malloc_pageshift); + fillrange(p, size << malloc_pageshift, malloc_counter, ALLOC_JUNK); } if (delay_free) { @@ -733,7 +768,7 @@ malloc_bytes(size_t size) k <<= bp->shift; if (malloc_junk) - memset((u_char*)bp->page + k, SOME_JUNK, bp->size); + fillrange((u_char*)bp->page + k, bp->size, malloc_counter, ALLOC_JUNK); return (u_char *)bp->page + k; } @@ -891,7 +926,7 @@ free_pages(void *ptr, u_long index, stru l = i << malloc_pageshift; if (malloc_junk) - memset(ptr, SOME_JUNK, l); + fillrange(ptr, l, malloc_counter, FREE_JUNK); if (malloc_hint) madvise(ptr, l, MADV_FREE); @@ -1012,7 +1047,7 @@ free_bytes(void *ptr, u_long index, stru } if (malloc_junk) - memset(ptr, SOME_JUNK, info->size); + fillrange(ptr, info->size, malloc_counter, FREE_JUNK); info->bits[i/MALLOC_BITS] |= 1<<(i%MALLOC_BITS); info->free++; @@ -1093,6 +1128,28 @@ ifree(void *ptr) return; } +static void fillrange(void *ptr, size_t size, u_int32_t value, u_char pad) +{ + int i; + + if (malloc_counter_junk) + { + u_int32_t fillvalue; + if (value < 256) // fits in one byte: D0D0D012 + fillvalue = pad<<24 | pad<<16 | pad<<8 | value; + else if (value < 65536) // fits in two bytes D0D01234 + fillvalue = pad<<24 | pad<<16 | value; + else // D0123456 + fillvalue = pad<<24 | (value & 0x00FFFFFF); + + for(i = 0; i < (size/4)*4 ; i += 4) + ((u_int32_t *)ptr)[i/4] = fillvalue; + } + + for( ; i < size ; i++) + ((u_char *)ptr)[i] = pad; +} + /* * These are the public exported interface routines. */ @@ -1114,6 +1171,9 @@ malloc(size_t size) } if (!malloc_started) malloc_init(); + malloc_counter++; + if (malloc_counter_abort && malloc_counter == malloc_counter_abort) + wrterror("Aborting as requested\n"); if (malloc_sysv && !size) r = 0; else if (!size) @@ -1142,6 +1202,9 @@ free(void *ptr) errno = EDOOFUS; return; } + malloc_counter++; + if (malloc_counter_abort && malloc_counter == malloc_counter_abort) + wrterror("Aborting as requested\n"); if (ptr != ZEROSIZEPTR) ifree(ptr); UTRACE(ptr, 0, 0); @@ -1171,6 +1234,9 @@ realloc(void *ptr, size_t size) } if (!malloc_started) malloc_init(); + malloc_counter++; + if (malloc_counter_abort && malloc_counter == malloc_counter_abort) + wrterror("Aborting as requested\n"); if (ptr == ZEROSIZEPTR) ptr = NULL; if (malloc_sysv && !size) { >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200306031559.h53FxZgS089446>