bin/52907: [PATCH] more malloc options for debugging programs
Dan Nelson
dnelson at allantgroup.com
Tue Jun 3 09:00:30 PDT 2003
>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 at 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:
More information about the freebsd-bugs
mailing list