| name | glibc-free-analysis |
| description | Analyze and explain glibc's free() function behavior for heap exploitation. Use this skill whenever the user asks about free(), heap chunk freeing, tcache/fastbin/unsorted bin behavior, double-free detection, safe-linking, tcache poisoning, or any glibc malloc/free internals. This skill explains the complete free() flow from __libc_free through _int_free to bin placement, including all security checks and error messages. Make sure to use this skill when debugging heap issues, analyzing heap exploitation primitives, or understanding why free() triggers specific error messages. |
glibc free() Analysis
This skill explains the complete flow of free() in glibc's heap allocator, including security checks, bin placement logic, and exploitation considerations.
Quick Reference: free() Flow
free(ptr)
└─> __libc_free
├─> NULL check → return
├─> mmaped chunk → munmap → return
└─> _int_free
├─> tcache (if eligible)
├─> fastbin (if eligible)
└─> _int_free_merge_chunk → unsorted bin
Step-by-Step Analysis
1. __libc_free Entry Point
When free(ptr) is called:
| Check | Action |
|---|
ptr == NULL | Return immediately (no-op) |
| Pointer tag mismatch | Double-free detection (if mtag_enabled) |
chunk_is_mmapped(p) | Call munmap_chunk(p) and return |
| Otherwise | Add color, call _int_free(arena, p, 0) |
Key insight: free(NULL) is safe and does nothing.
2. _int_free Validation
Before any bin placement, _int_free performs critical checks:
| Check | Error Message |
|---|
| Pointer not aligned | free(): invalid pointer |
| Size < MINSIZE or not aligned | free(): invalid size |
| Pointer wraps around address space | free(): invalid pointer |
These checks prevent many common heap corruption bugs from silently succeeding.
3. Tcache Placement (if eligible)
Eligibility: Chunk size fits in tcache bins AND tcache is enabled.
Checks before insertion:
| Check | Error Message |
|---|
tcache->counts[tc_idx] >= mp_.tcache_count | free(): too many chunks detected in tcache |
| Entry not aligned | free(): unaligned chunk detected in tcache 2 |
| Entry already in tcache (double-free) | free(): double free detected in tcache 2 |
Double-free detection: The code checks if e->key == tcache_key and walks the bin looking for the same pointer. This is probabilistic but effective.
glibc 2.42+ change: Tcache now accepts much larger chunks via glibc.malloc.tcache_max_bytes tunable. This means more frees go to tcache instead of unsorted bins.
4. Fastbin Placement (if eligible)
Eligibility: size <= get_max_fast() AND (if TRIM_FASTBINS) chunk doesn't border top.
Checks before insertion:
| Check | Error Message |
|---|
| Next chunk size invalid | free(): invalid next size (fast) |
| Chunk already at fastbin top | double free or corruption (fasttop) |
| Fastbin top has different size | invalid fastbin entry (free) |
Safe-linking: Fastbin fd pointers are protected: PROTECT_PTR(pos, ptr) = ((size_t)pos >> 12) ^ (size_t)ptr
5. Unsorted Bin (via _int_free_merge_chunk)
If chunk doesn't fit tcache or fastbin, it goes through consolidation:
Checks during consolidation:
| Check | Error Message |
|---|
| Chunk is top chunk | double free or corruption (top) |
| Next chunk outside arena | double free or corruption (out) |
| Next chunk not marked in-use | double free or corruption (!prev) |
| Next chunk size invalid | free(): invalid next size (normal) |
| Prev size mismatch during consolidation | corrupted size vs. prev_size while consolidating |
Process:
- Check if previous chunk is free → consolidate backward
- Check if next chunk is free → consolidate forward
- Place resulting chunk in unsorted bin
Exploitation Notes
Safe-Linking Bypass
Tcache and fastbin use safe-linking to protect fd pointers:
protected_fd = TARGET ^ (POS >> 12)
Implication: You need a heap leak to perform tcache poisoning. The XOR key is derived from the position of the fd pointer itself.
Tcache Double-Free Detection
The detection works by:
- Checking if
e->key == tcache_key (probabilistic match)
- Walking the entire bin up to
mp_.tcache_count entries
- Aborting if the same pointer is found
Bypass consideration: The key check is probabilistic (1 in 2^size_t chance of false positive), but the pointer walk is deterministic.
Forcing Specific Bin Behavior
For research/testing, use GLIBC_TUNABLES to control tcache:
GLIBC_TUNABLES=glibc.malloc.tcache_count=0 ./program
GLIBC_TUNABLES=glibc.malloc.tcache_max_bytes=0 ./program
Modern Hook Limitations
__malloc_hook and __free_hook overwrites are not viable on glibc ≥ 2.34. Use alternative targets:
- IO_FILE structures
- Exit handlers
- Virtual tables
- Other GOT/PLT entries
Common Error Messages Reference
| Error | Cause |
|---|
free(): invalid pointer | Misaligned or wrapped pointer |
free(): invalid size | Size < MINSIZE or not aligned |
free(): too many chunks detected in tcache | Tcache bin overflow |
free(): unaligned chunk detected in tcache 2 | Tcache entry misalignment |
free(): double free detected in tcache 2 | Same chunk freed twice (tcache) |
free(): invalid next size (fast) | Next chunk size invalid (fastbin) |
double free or corruption (fasttop) | Chunk already at fastbin top |
invalid fastbin entry (free) | Fastbin size mismatch |
double free or corruption (top) | Freeing top chunk |
double free or corruption (out) | Next chunk outside arena |
double free or corruption (!prev) | Next chunk not marked in-use |
free(): invalid next size (normal) | Next chunk size invalid (normal) |
corrupted size vs. prev_size while consolidating | Prev size field mismatch |
Practical Analysis Workflow
When analyzing a heap bug involving free():
- Identify the chunk size - Determines which bin it targets
- Check tcache eligibility - Is tcache enabled? Does size fit?
- Review security checks - Which checks might fail?
- Consider safe-linking - Do you need a heap leak?
- Plan the primitive - Tcache poisoning, fastbin attack, unsorted bin manipulation?
Related Concepts
- Tcache poisoning: Overwrite tcache
fd pointers to control allocation
- Fastbin attack: Similar to tcache but with different constraints
- Unsorted bin attacks: First-fit behavior, chunk consolidation
- Double-free primitives: Modern detection makes this harder
- Use-after-free: Often combined with free() manipulation
References