> For the complete documentation index, see [llms.txt](https://alham-rizvi.gitbook.io/alhamrizvi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://alham-rizvi.gitbook.io/alhamrizvi/binary-exp/17.-understanding-heap.md).

# 0x16 — Understanding Heap: Malloc, Free, and Tcache

## Overview

The **heap** is a dynamic memory region managed by `malloc`, `free`, and related functions. Understanding how the heap allocator works internally is essential for exploiting heap vulnerabilities like Use-After-Free, Double Free, and Heap Overflow.

This guide focuses on **glibc's ptmalloc2** allocator, specifically the **tcache** (thread cache) introduced in glibc 2.26.

***

## Memory Regions

| Region | Size        | Purpose                          |
| ------ | ----------- | -------------------------------- |
| Stack  | Fixed/small | Local variables, function frames |
| Heap   | Dynamic     | `malloc`/`free` managed memory   |
| BSS    | Fixed       | Uninitialized global variables   |
| Data   | Fixed       | Initialized global variables     |

The heap grows **upward** (toward higher addresses). `brk()` and `mmap()` expand it.

***

## Chunk Structure

Every allocated or freed region on the heap is a **chunk**. Each chunk has a header:

```
+---------------------------+
| prev_size (8 bytes)       |  ← size of previous chunk if prev is free
+---------------------------+
| size      (8 bytes)       |  ← size of this chunk | flags
+---------------------------+  ← user pointer (returned by malloc) starts here
| user data                 |
| ...                       |
+---------------------------+
```

### Flags in the `size` field (lowest 3 bits)

* **P (0x1)**: Previous chunk in use (PREV\_INUSE)
* **M (0x2)**: Chunk was mmap'd
* **A (0x4)**: Non-main arena

Actual size is `size & ~0x7` (mask out flags).

```c
// Example: size = 0x21 → actual size = 0x20, PREV_INUSE set
```

***

## How `malloc` Works

```c
void *ptr = malloc(size);
```

1. Rounds up `size` to nearest alignment (16 bytes on 64-bit)
2. Checks **tcache** for a matching free chunk → returns it immediately if found (fast path)
3. Checks **fastbins** → returns if found
4. Checks **smallbins**, **largebins**, **unsorted bin**
5. Calls `brk()` or `mmap()` to get more memory from OS if needed

***

## How `free` Works

```c
free(ptr);
```

1. Validates the chunk header
2. If size fits in **tcache** (< 0x408): places it in the tcache singly-linked list
3. Otherwise: consolidates with adjacent free chunks, places in appropriate bin

***

## Bins Overview

| Bin       | Chunk Size    | Structure          | Notes                  |
| --------- | ------------- | ------------------ | ---------------------- |
| Tcache    | ≤ 0x408 bytes | Singly-linked list | Per-thread, 7 max/size |
| Fastbins  | ≤ 0x80 bytes  | LIFO singly-linked | No coalescing          |
| Unsorted  | Any size      | Doubly-linked      | Temporary holding bin  |
| Smallbins | < 0x400 bytes | Doubly-linked FIFO | Sorted by size         |
| Largebins | ≥ 0x400 bytes | Doubly-linked      | Ranges of sizes        |

***

## Tcache (Thread Cache) — Deep Dive

Introduced in glibc 2.26. Each thread has a `tcache_perthread_struct` with:

* **64 bins** for sizes 0x20 to 0x410 (in 0x10 increments)
* **Each bin** holds up to **7 entries** (default)
* **LIFO** order (last freed = first returned by malloc)

### Tcache chunk freed structure:

```
+------------------+
| prev_size        |
+------------------+
| size             |
+------------------+  ← user data starts here (now free)
| fd (next ptr)    |  ← points to next chunk in tcache bin
+------------------+
| key              |  ← glibc 2.29+: contains heap address for double-free detection
+------------------+
```

### Tcache in memory:

```
tcache_perthread_struct {
    uint16_t counts[64];     ← how many entries in each bin
    tcache_entry *entries[64]; ← head pointers to each bin's linked list
}
```

***

## Visualizing malloc and free

```c
// Allocate three chunks
char *a = malloc(0x20);   // chunk A
char *b = malloc(0x20);   // chunk B
char *c = malloc(0x20);   // chunk C

// Free A and B
free(a);   // tcache bin[0x20]: A → NULL
free(b);   // tcache bin[0x20]: B → A → NULL

// Next malloc(0x20) returns B (LIFO)
char *d = malloc(0x20);   // d == b
// tcache bin[0x20]: A → NULL
```

***

## Heap in GDB (pwndbg)

```bash
gdb ./vuln
> heap          # show all heap chunks
> bins          # show all bin contents
> tcache        # show tcache state
> vis_heap_chunks  # visual heap layout
> malloc_chunk 0x602010   # inspect a specific chunk
```

***

## Simple Heap Demo (`demo.c`)

```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    printf("malloc(0x20) returns: ");
    void *a = malloc(0x20);
    printf("%p\n", a);

    printf("malloc(0x20) returns: ");
    void *b = malloc(0x20);
    printf("%p\n", b);

    printf("Freeing a...\n");
    free(a);

    printf("malloc(0x20) returns: ");
    void *c = malloc(0x20);
    printf("%p\n", c);
    // c == a (same address, reused from tcache)

    return 0;
}
```

```bash
gcc -o demo demo.c
./demo
# malloc(0x20) returns: 0x602260
# malloc(0x20) returns: 0x602290
# Freeing a...
# malloc(0x20) returns: 0x602260  ← reused!
```

***

## Heap Exploitation Primitive

Most heap attacks exploit the **free list (tcache/fastbin) pointers** stored in freed chunks:

```
Freed chunk layout:
[ prev_size ][ size ][ fd pointer ][ key/bk ][ user data... ]
```

If an attacker can:

* **Write into a freed chunk** (Use-After-Free / Heap Overflow) → corrupt `fd`
* **Double-free** a chunk → insert the chunk into tcache twice

Then subsequent `malloc` calls can return an **arbitrary address** → write-what-where.

***

## Key Addresses & Structures

```bash
# Check glibc version (affects tcache details)
ldd ./vuln | grep libc
strings /lib/x86_64-linux-gnu/libc.so.6 | grep "GNU C"

# In GDB
p &main_arena      # main allocator arena
p tcache           # current thread's tcache
```

***

## Key Takeaways

* Heap chunks have a **header** (prev\_size + size) before the user data pointer
* **Tcache** is the fastest allocation path — a per-thread LIFO singly-linked list
* Freed tcache chunks store a **forward pointer** (`fd`) in their user data area
* All heap exploitation techniques ultimately manipulate these free list pointers
* Understanding chunk layout and bin structure is essential before studying UAF, Double Free, etc.

***

## Useful pwntools Heap Functions

```python
from pwn import *

elf = ELF('./vuln')

# Helper for pwntools heap analysis
p = process('./vuln')
gdb.attach(p, '''
    heap
    bins
    tcache
    continue
''')
```
