[Python-Dev] Debug entry points for PyMalloc
Michael Hudson
mwh@python.net
21 Mar 2002 13:03:09 +0000
Tim Peters <tim.one@comcast.net> writes:
> The thing I've dreaded most about switching to pymalloc is losing the
> invaluable memory-corruption clues supplied by the Microsoft debug-build
> malloc. On more than one occasion, they've found wild stores, out-of-bounds
> reads, reads of uninitialized memory, and reads of free()ed memory in
> Python. It does this by spraying special bytes all over malloc'ed memory at
> various times, then checking the bytes for sanity at free() and realloc()
> times.
[...]
> Sketch of Debug Mode for PyMalloc
>
> + Three new entry points in obmalloc.c (note: stop #include'ing this;
> hiding code in include files sucks, and naming an include file .c
> compounds the confusion):
+1
> DL_IMPORT(void *) _PyMalloc_DebugMalloc(size_t nbytes);
> DL_IMPORT(void *) _PyMalloc_DebugRealloc(void *p, size_t nbytes);
> DL_IMPORT(void) _PyMalloc_DebugFree(void *p);
>
> + When WITH_PYMALLOC and PYMALLOC_DEBUG are #define'd, these are
> mapped to in the obvious way from _PyMalloc_{MALLOC, REALLOC, FREE}:
>
> #ifdef WITH_PYMALLOC
> DL_IMPORT(void *) _PyMalloc_Malloc(size_t nbytes);
> DL_IMPORT(void *) _PyMalloc_Realloc(void *p, size_t nbytes);
> DL_IMPORT(void) _PyMalloc_Free(void *p);
>
> DL_IMPORT(void *) _PyMalloc_DebugMalloc(size_t nbytes);
> DL_IMPORT(void *) _PyMalloc_DebugRealloc(void *p, size_t nbytes);
> DL_IMPORT(void) _PyMalloc_DebugFree(void *p);
>
> #ifdef PYMALLOC_DEBUG
> #define _PyMalloc_MALLOC _PyMalloc_DebugMalloc
> #define _PyMalloc_REALLOC _PyMalloc_DebugRealloc
> #define _PyMalloc_FREE _PyMalloc_DebugFree
>
> #else /* WITH_PYMALLOC && !PYMALLOC_DEBUG */
> #define _PyMalloc_MALLOC _PyMalloc_Malloc
> #define _PyMalloc_REALLOC _PyMalloc_Realloc
> #define _PyMalloc_FREE _PyMalloc_Free
>
> #endif /* PYMALLOC_DEBUG */
>
> #else /* !WITH_PYMALLOC */
> #define _PyMalloc_MALLOC PyMem_MALLOC
> #define _PyMalloc_REALLOC PyMem_REALLOC
> #define _PyMalloc_FREE PyMem_FREE
> #endif /* WITH_PYMALLOC */
Presuambly it would be possible to do this wrapped around malloc() &
free() too? No real point, I guess.
> + A debug build implies PYMALLOC_DEBUG, but PYMALLOC_DEBUG can
> be forced in a release build.
>
> + No changes to the guts of _PyMalloc_{Malloc, Realloc, Free}. Keep
> them as lean and as clear of #ifdef obscurity as they are now.
+1
> + Define three special bit patterns. In hex, they all end with B
> (for deBug <wink>), and begin with a vaguely mnemonic letter.
> Strings of these are unlikely to be legit memory addresses, ints,
> 7-bit ASCII, or floats:
[snip good stuff]
> + The Debug free first uses the address to find the number of bytes
> originally asked for, then checks the 8 bytes on each end for
> sanity (in particular, that the PYMALLOC_FORBIDDENBYTEs are still
> intact).
> XXX Make this checking a distinct entry point.
Yes. Particularly if you can call it from gdb.
> XXX In case an error is found, print informative stuff, but then what?
> XXX Die or keep going? Fatal error is probably best.
> Then fills the original N bytes with PYMALLOC_DEADBYTE. This is to
> catch references to free()ed memory. The forbidden bytes are left
> intact.
> Then calls _PyMalloc_Free.
Is it worth having an option where you *don't* call _Free? Obviously,
this would chew memory like no tomorrow, but it might just catch more
errors.
> + The Debug realloc first calls _PyMalloc_DebugMalloc with the new
> request size.
> Then copies over the original bytes.
> The calls _PyMalloc_DebugFree on the original bytes.
> XXX This could, and probably should, be optimized to avoid copying
> XXX every time.
Yes.
What are you waiting for? <wink>
Cheers,
M.
--
If comp.lang.lisp *is* what vendors are relying on to make or
break Lisp sales, that's more likely the problem than is the
effect of any one of us on such a flimsy marketing strategy...
-- Kent M Pitman, comp.lang.lisp