How glibc’s Unlink Mechanism Enables Heap Overflow Exploits (And Why It’s Now Defended)

This article explains the fundamentals of glibc malloc’s unlink mechanism, demonstrates how a heap overflow can be leveraged to overwrite chunk headers and execute arbitrary code, walks through the exploitation steps with code examples, and discusses modern mitigations that render the classic unlink technique ineffective.

ITPUB
ITPUB
ITPUB
How glibc’s Unlink Mechanism Enables Heap Overflow Exploits (And Why It’s Now Defended)

Prerequisite Knowledge

The exploit assumes familiarity with glibc’s malloc implementation, especially the internal malloc_chunk layout, the prev_inuse bit, and the forward/backward consolidation performed during free.

Vulnerable Program

#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
    char *first, *second;
    /*[1]*/ first = malloc(666);
    /*[2]*/ second = malloc(12);
    if (argc != 1)
        /*[3]*/ strcpy(first, argv[1]);
    /*[4]*/ free(first);
    /*[5]*/ free(second);
    /*[6]*/ return 0;
}

If argv[1] exceeds 666 bytes, strcpy overwrites the metadata of the next heap chunk ( second), creating a classic heap‑overflow condition.

Memory Layout

The heap consists of two adjacent chunks ( first and second) followed by the top chunk. The overflow corrupts the header of second.

Heap layout
Heap layout

Unlink Technique Principle

Basic Knowledge

During free, glibc may consolidate the freed chunk with adjacent free chunks. Consolidation uses two steps:

Backward consolidation : merge with the previous free chunk.

Forward consolidation : merge with the next free chunk.

Both steps eventually invoke the unlink macro to remove a free chunk from the doubly‑linked free list.

#define unlink(P, BK, FD) { 
    FD = P->fd; 
    BK = P->bk; 
    FD->bk = BK; 
    BK->fd = FD; 
    ... 
}

After unlinking, the chunk is placed into the unsorted_bins list:

bck = unsorted_chunks(av);
fwd = bck->fd;
...
 p->fd = fwd;
 p->bk = bck;
 bck->fd = p;
 fwd->bk = p;
 set_head(p, size | PREV_INUSE);
 set_foot(p, size);

Attack Walk‑through

The attacker crafts a fake chunk header inside the corrupted second chunk:

Set prev_size to an even value so that PREV_INUSE becomes 0 (previous chunk appears free).

Set size to -4 to break the next‑size check.

Set fd to free@GOT - 12.

Set bk to the address of attacker‑controlled shellcode.

When free(first) runs, forward consolidation sees second as free and calls unlink(second, bk, fd):

FD = second->fd;   // FD = free@GOT - 12
BK = second->bk;   // BK = shellcode address
FD->bk = BK;       // free@GOT - 12 ->bk = shellcode
BK->fd = FD;       // shellcode->fd = free@GOT - 12

This overwrites the GOT entry for free with the shellcode address. The subsequent call to free(second) jumps to the injected shellcode.

Defenses (Why Classic Unlink Is Mostly Defeated)

Double‑Free Detection

glibc aborts if the previous chunk’s prev_inuse bit is cleared, emitting “double free or corruption (!prev)”.

if (__glibc_unlikely (!prev_inuse(nextchunk))) {
    errstr = "double free or corruption (!prev)";
    goto errout;
}

Invalid Next‑Size Check

The allocator verifies that nextsize lies between SIZE_SZ*2 and the arena’s total memory. A crafted -4 size triggers “invalid next size”.

if (__builtin_expect (nextchunk->size <= 2*SIZE_SZ, 0) ||
    __builtin_expect (nextsize >= av->system_mem, 0)) {
    errstr = "free(): invalid next size (normal)";
    goto errout;
}

Double‑Linked List Corruption Check

Before unlinking, glibc checks that the forward and backward pointers of adjacent chunks point back to the chunk being removed.

if (__builtin_expect (FD->bk != P || BK->fd != P, 0))
    malloc_printerr (check_action, "corrupted double-linked list", P);

Other Unlink‑Based Exploits

Research has shown bypasses on Android 4.4, where the allocator is still glibc‑like. Those exploits craft payloads that satisfy the three checks while still achieving arbitrary write.

Conclusion

The classic glibc unlink heap‑overflow technique relies on corrupting a neighboring chunk’s header to manipulate the free‑list and overwrite a GOT entry. Modern glibc includes double‑free detection, next‑size validation, and double‑linked‑list integrity checks that largely mitigate this specific attack, but understanding the internals remains valuable for studying newer heap‑corruption vectors such as fast‑bin attacks.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

memory corruptionheap overflowglibc mallocunlink exploitation
ITPUB
Written by

ITPUB

Official ITPUB account sharing technical insights, community news, and exciting events.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.