DEBUG: pools: replace the link pointer with the caller's address on pool_free()
authorWilly Tarreau <w@1wt.eu>
Wed, 9 Feb 2022 15:49:16 +0000 (16:49 +0100)
committerWilly Tarreau <w@1wt.eu>
Tue, 15 Feb 2022 06:47:01 +0000 (07:47 +0100)
Along recent evolutions of the pools, we've lost the ability to reliably
detect double-frees because while in the past the same pointer was being
used to chain the objects in the cache and to store the pool's address,
since 2.0 they're different so the pool's address is never overwritten on
free() and a double-free will rarely be detected.

This patch sets the caller's return address there. It can never be equal
to a pool's address and will help guess what was the previous call path.
It will not work on exotic architectures nor with very old compilers but
these are not the environments where we're trying to get detailed bug
reports, and this is not done by default anyway so we don't care about
this limitation. Note that depending on the inlining status of the
function, the result may differ but that's no big deal either.

A test by placing a double free of an appctx inside the release handler
itself successfully reported the trouble during appctx_free() and showed
that the return address was in stream_int_shutw_applet() (this one calls
the release handler).

(cherry picked from commit 27c8da1fd5e4f906fd3a9f96a4284c886b559305)
[wt: simplified for 2.5 and older, just use POOL_LINK()]
Signed-off-by: Willy Tarreau <w@1wt.eu>

include/haproxy/pool.h

index 683502f..e9ff4d9 100644 (file)
@@ -355,6 +355,7 @@ static inline void pool_free(struct pool_head *pool, void *ptr)
                /* we'll get late corruption if we refill to the wrong pool or double-free */
                if (*POOL_LINK(pool, ptr) != (void *)pool)
                        ABORT_NOW();
+               *POOL_LINK(pool, ptr) = __builtin_return_address(0);
 #endif
                if (unlikely(mem_poison_byte >= 0))
                        memset(ptr, mem_poison_byte, pool->size);