Skip to main content

Overview

Safe-Linking is a security mitigation introduced in glibc 2.32 that protects single-linked lists used in the heap allocator. It uses ASLR randomness to obfuscate pointers and enforces alignment checks to prevent pointer hijacking in tcache and fastbin freelists.
Impact on Exploitation: Safe-linking significantly increases the difficulty of tcache poisoning and fastbin attacks by requiring heap leaks and introducing XOR obfuscation.

Background

Before glibc 2.32, tcache and fastbin freelists stored forward pointers in plaintext, making them trivial to hijack:
// Pre-2.32: Simple pointer
tcache->entries[tc_idx] = victim;
victim->next = previous_entry;  // Plaintext pointer!
Attackers could easily overwrite these pointers to make malloc return arbitrary addresses.

The Safe-Linking Mechanism

Commit: a1a486d70e introduced safe-linking in glibc 2.32.
Safe-linking protects pointers using two mechanisms:

1. Pointer Obfuscation (XOR Mangling)

Pointers are XOR’d with the address where they’re stored, shifted right by 12 bits:
#define PROTECT_PTR(pos, ptr) \
  ((__typeof(ptr))((((size_t)(ptr)) >> 12) ^ ((size_t)(pos))))

#define REVEAL_PTR(ptr) \
  PROTECT_PTR(&ptr, ptr)
How it works:
// When storing a pointer:
stored_value = (next_chunk_addr >> 12) ^ current_chunk_addr

// When retrieving:
next_chunk_addr = (stored_value ^ current_chunk_addr) << 12

2. Alignment Check

The protection also verifies that revealed pointers are properly aligned:
if (__glibc_unlikely(!aligned_OK(ptr)))
  malloc_printerr("malloc(): unaligned tcache chunk detected");
Since heap chunks must be 16-byte aligned on x64, the lower 4 bits must be zero.

Example: Safe-Linking in Action

void *a = malloc(0x20);
void *b = malloc(0x20);

free(a);
free(b);

// Without safe-linking (pre-2.32):
// b[0] = a  (plaintext pointer)

// With safe-linking (2.32+):
// b[0] = ((uintptr_t)a >> 12) ^ (uintptr_t)b
printf("Obfuscated pointer: %#lx\n", ((unsigned long *)b)[0]);

Security Properties

  1. Direct Pointer Overwrites: Attackers can’t simply write an arbitrary address into a freelist
  2. Information Leaks: Forward pointers no longer directly reveal heap addresses
  3. Basic Tcache Poisoning: The classic tcache_poisoning.c technique requires adaptation
  4. Fastbin Corruption: Similar protections apply to fastbin freelists
  1. Double-Free: Still exploitable with additional steps
  2. Heap Layout Attacks: Techniques like House of Botcake still work
  3. Metadata Corruption: Direct tcache_perthread_struct corruption bypasses this
  4. Known-Address Linking: If you know both the source and target addresses, you can compute the mangled value

Bypass Techniques

Several techniques have been developed to bypass or work around safe-linking:

1. Decrypt Safe-Linking

Exploit the mathematical properties of the XOR obfuscation to recover the original pointer.

Decrypt Safe-Linking

Learn how to recover original pointers from obfuscated values using the 12-bit sliding property.
Protect a pointer twice with the same key to effectively cancel out the obfuscation.

Safe-Link Double Protect

Bypass safe-linking by applying the PROTECT_PTR operation twice to revert to the original value.

3. Tcache Metadata Manipulation

Directly corrupt the tcache_perthread_struct to bypass the freelist entirely.

House of Water

Gain leakless control of tcache metadata to bypass safe-linking completely.

Impact on Classic Techniques

Tcache Poisoning

Before Safe-Linking:
victim[0] = target_address;  // Direct overwrite
After Safe-Linking:
// Need heap leak to compute mangled pointer
victim[0] = ((target >> 12) ^ victim_addr);

Fastbin Dup

Before Safe-Linking:
free(a);
free(a);  // Double-free
// fastbin: a -> a
After Safe-Linking:
// Still possible but requires:
// 1. Heap leak to decrypt pointers
// 2. Additional steps to bypass checks

Implementation Details

Affected Structures

Safe-linking protects:
  • Tcache bins (sizes 0x20 to 0x410)
  • Fastbins (sizes 0x20 to 0xb0)
It does NOT protect:
  • Unsorted bin (uses double-linked list)
  • Small bins (uses double-linked list)
  • Large bins (uses double-linked list)

Performance Impact

The XOR operation and alignment check add minimal overhead:
  • ~2-3 CPU cycles per malloc/free
  • No memory overhead
  • No impact on allocation patterns

Code Reference

The safe-linking implementation in glibc:
// malloc/malloc.c
#define PROTECT_PTR(pos, ptr)                           \
  ((__typeof (ptr)) ((((size_t) (ptr)) >> 12) ^         \
                     ((size_t) (pos))))

#define REVEAL_PTR(ptr) PROTECT_PTR (&ptr, ptr)

// In tcache_put:
e->next = PROTECT_PTR (&e->next, tcache->entries[tc_idx]);
tcache->entries[tc_idx] = e;

// In tcache_get:
tcache_entry *e = tcache->entries[tc_idx];
e = REVEAL_PTR (e);
if (__glibc_unlikely (!aligned_OK (e)))
  malloc_printerr ("malloc(): unaligned tcache chunk detected");

Practical Exploitation

To exploit heap vulnerabilities on glibc 2.32+, you typically need:
  1. Heap Address Leak: To compute mangled pointers
  2. Known Chunk Layout: To predict pointer positions
  3. Bypass Strategy: One of the techniques listed above
1

Obtain Heap Leak

Use information disclosure bugs to leak a heap address. This can come from:
  • Uninitialized memory
  • Use-after-free reads
  • Format string vulnerabilities
2

Calculate Obfuscated Value

Use the leaked address to compute the mangled pointer value:
mangled = (target >> 12) ^ source_addr
3

Overwrite Freelist

Write the mangled value into the tcache/fastbin freelist:
victim->next = mangled_pointer;
4

Trigger Allocation

Call malloc to retrieve your target address from the poisoned freelist.

Comparison with Other Mitigations

MitigationGlibc VersionProtectsBypassable
Safe-Linking2.32+Single-linked listsYes (with heap leak)
Tcache Double-Free Check2.26-2.28Tcache double-freeYes (House of Botcake)
Tcache Key Check2.29+Tcache double-freeYes (overwrite key)
Safe-Unlinking2.3.4+Unlink attacksDifficult

Historical Context

Safe-linking was added in response to widespread exploitation of tcache poisoning:
  • 2017: Tcache introduced in glibc 2.26 with no protection
  • 2018-2019: Tcache poisoning becomes the most common heap technique
  • 2020: Safe-linking added in glibc 2.32
  • 2020+: New bypass techniques developed (decrypt, double-protect, etc.)

Further Reading

References

Build docs developers (and LLMs) love