Hi,
I have, for debugging my own mistakes, hacked a bit in apr_pools.c (you know it
would happen sooner or later, right?). I wanted to have easy checks for memory
being overrun inside a pool where address sanitation will not find it. The idea
is to add a guard at the beginning of a cleanup_t struct that is then checked
when a pool is cleared/destroyed.
#define APR_CLEANUP_GUARD 0x55aa55aa55aa55aaL /* active guard */
#define APR_CLEANUP_CLEARED 0x11aa11aa11aa11aaL /* cleanup has run */
#define APR_CLEANUP_STOWED 0x3399339933993399L /* cleanup in free list */
struct cleanup_t {
long guard;
struct cleanup_t *next;
const void *data;
apr_status_t (*plain_cleanup_fn)(void *data);
apr_status_t (*child_cleanup_fn)(void *data);
};
Since cleanup_t is a linked list and allocated from the pool just like any
other memory, it can find itself sitting in various places. This can be used
for checking boundaries of memory allocated just before. Insert a guarded dummy
cleanup after another allocation and on pool destruction, you have the check.
Another advantage is that this causes nearly no runtime overhead while the pool
is in use. That helps with heisenbugs.
Now, there is consistency checking on the cleanup list during APR_POOL_DEBUG
already, so the idea is certainly not new. However that will not help if a
cleanup is NULLed at the start. My hack uses a magic value. And besides, I
needed something that did not drag the whole POOL_DEBUG in at this time.
Besides apr_pool_cleanup_register(), we could in httpd also add a
AP_DEBUG_ADD_GUARD(pool) macro that registers a dummy cleanup here and there in
maintainer mode. In production, this would have no effect.
I am wondering what to do with my changes now. Since my hands are very full
right now, I was wondering if someone here is interested in carrying this
forward?
Cheers,
Stefan