A suggestion for dmalloc -
If check-blank is set (and probably even if it isn't),
_chunk_check() should be called during cleanup, or better yet
in _chunk_stats().
The reasoning is to verify if any freed memory has been trashed (by
write-after-free bugs). This is particularly useful in conjunction with
(larger values of) FREED_POINTER_DELAY or with DEBUG_NEVER_REUSE, but is
useful even without them to verify everything is in good shape when
the program exits.
I added it to my local copy to help in tracking down random longword-0
writes into structures (due to their being reallocated over freed
structures which are being written to long after free).
BTW, I use MUCH larger values of FREED_POINTER_DELAY than the default
(10). The idea behind FREED_POINTER_DELAY (which came from a suggestion
of mine) was that it would be more likely to trap either write-after-free
bugs or read-after-free bugs (since reallocation often leaves the data
as 0's or valid pointers, while free_blank sets it to 0xC5C5C5C5).
With larger values, it might make sense to not delay freeing larger
allocations (larger than some settings.dist value), such as say 20K+.
This avoids the problem of programs that constantly alloc/free large data
buffers eating vast amount of memory due to the delay, while still
providing good protection to most structures (which are more likely to
be involved in these sorts of bugs). In my old private memory debugger,
I had a "don't delay free()'s over this size" value, and for added
fun a "don't delay more than X MB of freed memory" value. Also,
FREED_POINTER_DELAY would be nice to have as modifiable without rebuilding
dmalloc.
NOTE:
To better try to track down where a chunk of freed memory was _last_
allocated from, I removed pn_file from the db_pnt union. This seriously
confuses dmalloc. Trying to separate in_bblock from in_nums causes even
more problems. The issue is that dmalloc has tests (such as
/* check out dblock entry to see if it is not free */
/*
* XXX: this test might think it was a free'd slot if db_file
* (unioned with db_next) points to a return address in the
* heap.
*/
if ((dblock_p->db_next == NULL || IS_IN_HEAP(dblock_p->db_next))
&& IS_IN_HEAP(dblock_p->db_bblock))
to try to figure out if a dblock_p is allocated or not. I was forced to
add a db_flags field instead to track allocation.
I suspect there are other hidden assumptions that various
values are clobbered by other members of the union combined with
IS_IN_HEAP(). I view IS_IN_HEAP() as inherently dangerous at best -
I'd prefer to increase the overhead-per-allocation/chunk and get away from
the hidden assumptions that make it hard to find real memory errors,
and very hard to understand/modify to suite the circumstances, and hard
to debug freed-memory problems once you break in a debugger.
When you're trying to debug write-after-free problems, having
access to the file/line the freed memory was allocated from is critical.
In some cases it might be nice to know where it was freed from as well,
but that's not a big issue.
--
Randell Jesup, Worldgate Communications, ex-Scala, ex-Amiga OS team ('88-94)
rjesup@...