Answer to the Exact Question
glibc’s free() performs a series of consistency checks on the chunk being freed. The routine __libc_free looks at the chunk’s size field, the prev_size of the preceding chunk, and the links that the chunk has in the tcache or fastbin lists. If the chunk is already present in one of those lists, the allocator treats it as a double‑free and prints the message double free or corruption before calling abort().
When the checks fail because the size or boundary fields no longer match the expected values (for example, an overrun has overwritten the metadata of a neighboring chunk), glibc also prints the same message and aborts. In both cases the message is produced by the same helper __malloc_printerr, but the trigger is different: a list‑membership test for double‑free versus a consistency test for corruption.
Distinguishing Between a Genuine Double‑Free and General Corruption
- Double‑free detection – the chunk’s
fd or bk pointer is already part of a tcache or fastbin list. The allocator checks this by walking the list pointers before inserting the chunk back.
- Corruption detection – the allocator validates that the
size of the current and previous chunks match the prev_size field, that the prev_in_use flag is consistent, and that the chunk’s boundaries lie within the heap. If any of these invariants fail, the same abort routine is invoked.
Environment Variables and Tunables for Granular Control
MALLOC_CHECK_ (0‑3) – controls the response to a detected error.
- 0: ignore the error, continue execution (dangerous).
- 1: print a diagnostic message, then continue.
- 2: print a diagnostic message and
abort() immediately.
- 3: print a diagnostic message, then
abort() (same as 2 in current glibc).
MALLOC_PERTURB_ – fills freed memory with a byte pattern (default 0xAA). Useful for detecting overruns after a free but does not change the abort behavior.
GLIBC_TUNABLES – a semicolon‑separated list of tunable settings. Examples:
- glibc.malloc.mxfast=0 – disables tcache, forcing all frees to go through the fastbin path.
- glibc.malloc.trim_threshold=0 – disables heap trimming after each free.
- LD_PRELOAD hooks – you can preload a shared library that registers
malloc_hook and free_hook to add custom logging or to replace the default abort with a delayed or conditional one.
Practical Verification Steps
Compile a minimal test program that frees the same pointer twice.
Run it with MALLOC_CHECK_=2:
export MALLOC_CHECK_=2
./double_free_test
You should see the diagnostic message and the process terminate with SIGABRT.
Set MALLOC_CHECK_=0 and run again to observe that the process continues (undefined behavior).
To explore tcache behavior, set GLIBC_TUNABLES=glibc.malloc.mxfast=0 and repeat the test. The double‑free will now be caught by the fastbin path.
For custom logging, create a small shared library that implements free_hook and preload it with LD_PRELOAD=./myhooks.so.
What We Still Need To Know
To tailor the advice further, it would help to know which glibc version you are targeting (e.g., 2.27, 2.34, or newer). The tcache implementation was introduced in glibc 2.26, so the exact checks and tunables differ between releases.