Skip to content

ptmalloc consistency checks

glibc's malloc (ptmalloc) sprinkles integrity assertions through malloc.c — unlink FD/BK validation, size vs. prev_size agreement, double-linked-list checks, tcache key double-free detection and fastbin/top sanity — that call malloc_printerr and abort() when heap metadata is corrupted.

Mechanism

Neden çalışır

ptmalloc, allocator metadata'sını (chunk size'ları, free-list pointer'ları) inline, user data'ya bitişik saklar. Bu yüzden lineer bir overflow ya da bir use-after-free, saldırganın bu metadata'yı forge ederek malloc/free'yi saldırgan-seçimli bir pointer döndürmeye zorlamasına izin verir. Consistency check'ler basit bir invariant'ı savunur: allocator'ın güvenmek üzere olduğu metadata, hâlâ allocator'ın kendi tuttuğu yedek kopyalarla kendi içinde tutarlı olmalıdır.

Her free list, saldırganın tutarlı tutması gereken bir yedeklilik taşır:

  • Doubly-linked bir bin hem fd hem bk'yi tutar. Bir P chunk'ını FD ve BK arasından unlink etmek yalnızca FD->bk == P ve BK->fd == P ise geçerlidir.
  • Her chunk kendi size'ını saklar; next chunk prev_size'ı saklar. Free bir chunk için bunlar uyuşmalı, böylece boundary tag'leri çapraz kontrol eder.
  • tcache entry'leri per-thread random bir key taşır; free'de chunk'a damgalanır ve aynı chunk'ın ikinci bir free'si yakalanır çünkü key zaten mevcuttur — naif tcache double-free'yi etkisiz kılar. (tcache'in kendisi glibc 2.26'da geldi, ama bu double-free key field'ı glibc ≥ 2.29'da eklendi.)

Bunların hiçbiri kurşun geçirmez değildir (yeterince bitişik byte'ı kontrol eden bir saldırgan bunları sağlayabilir), ama sessiz sömürüyü gürültülü bir malloc_printerr abort'una çevirir ve one-shot corruption'lar için çıtayı yükseltir.

Walkthrough

Hangi check'in tetikleneceği hangi yapıyı corrupt ettiğinize bağlıdır. Aşağıdaki tam string'ler ve koşullar glibc malloc/malloc.c'den alınmıştır.

1. unlink check'leri (unlink_chunk). Bir chunk'ı bir bin'den çıkarmadan önce glibc boundary tag'i ve her iki liste link'ini doğrular:

/* glibc malloc/malloc.c, unlink_chunk() */
if (chunksize (p) != prev_size (next_chunk (p)))
    malloc_printerr ("corrupted size vs. prev_size");

if (__glibc_unlikely (fd->bk != p || bk->fd != p))
    malloc_printerr ("corrupted double-linked list");

largebin chunk'lar için nextsize ring'i de kontrol edilir:

if (p->fd_nextsize->bk_nextsize != p || p->bk_nextsize->fd_nextsize != p)
    malloc_printerr ("corrupted double-linked list (not small)");

Yani klasik unsafe-unlink write'ı, fd->bk ve bk->fd'nin her ikisinin de geri P'yi göstereceği şekilde fd/bk'yi forge etmeyi gerektirir — modern unlink saldırılarının bu pointer'ları chunk'ın kendisine bir pointer tutan bir konuma yöneltmesinin nedeni budur.

2. tcache double-free key. tcache'e free etmek bir key damgalar; tekrarlanan bir free yakalanır:

/* tcache double-free guard in _int_free */
if (__glibc_unlikely (e->key == tcache))
    /* walk the bin to confirm, then: */
    malloc_printerr ("free(): double free detected in tcache 2");

Bu tam olarak glibc-tcache-double-free-key mitigation'ıdır; onu bypass etmek önce saklanan key'i corrupt etmek ya da sıfırlamak demektir.

3. Fastbin double-free / entry sanity. Bir fastbin'e push etmek top'u ve size class'ını kontrol eder:

"free(): double free or corruption (fasttop)"   // freed chunk == current fastbin top
"invalid fastbin entry (free)"                   // chunk size != expected fastbin size

fasttop check'i aynı chunk'ın ardışık bir double free'sini engeller; fastbin-dup bunu araya farklı bir chunk free ederek atlatır.

4. Bin-walk ve top check'leri. Allocation'ları servis ederken liste link'lerini ve wilderness'i yeniden doğrular:

"malloc(): smallbin double linked list corrupted"   // bck->fd != victim
"malloc(): unsorted chunks"                          // unsorted-bin link mismatch
"malloc(): corrupted top size"                       // top chunk size implausible
"free(): invalid next size (normal)"                 // next chunk size out of range

5. Birini tetikle. tcache'e basit bir double free abort eder:

$ ./poc            # free(p); free(p);
free(): double free detected in tcache 2
Aborted (core dumped)

Bunlar integrity check'leridir, access control değil

Check'ler yalnızca iç tutarlılığı doğrular. Yeterli bitişik kontrolle bir saldırgan hepsini sağlar — check'ler bir corruption'ın maliyetini yükseltir, heap'i güvenli yapmaz.

Detection

Her başarısızlık malloc_printerr'den geçer; bu, tam tanılamayı stderr'e yazar ve abort() çağırır (SIGABRT, core dump). Yukarıdaki string'ler crash log'larında ve core dosyalarında istikrarlı grep hedefleridir; malloc/free içinde bu mesajlardan biriyle bir abort neredeyse kesin bir heap-corruption sinyalidir. MALLOC_CHECK_ ve mcheck/mtrace debugging için ek enstrümantasyon ekler.

Mitigation

(Artık risk / bypass.) Check'ler yereldir ve yalnızca metadata'ya bakar, bu yüzden metadata'yı kendi içinde tutarlı tutan saldırılara yenik düşerler: fastbin-dup ve fastbin-dup-consolidate fasttop check'inden kaçar; tcache poisoning, singly-linked tcache next'ini yazarak unlink yolundan tamamen kaçınır ve saklanan key'i temizlemek double-free guard'ını etkisiz kılar. Bazı açıkları kapatan hardening arasında glibc-safe-linking (XOR-mangle edilmiş next pointer'ları) ve glibc-tcache-double-free-key guard'ının kendisi vardır; ayrıca bkz. fastbin-reverse-into-tcache.

References