ptmalloc consistency checks¶
glibc's malloc (ptmalloc) sprinkles integrity assertions through
malloc.c— unlink FD/BK validation,sizevs.prev_sizeagreement, double-linked-list checks, tcache key double-free detection and fastbin/top sanity — that callmalloc_printerrandabort()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
fdhembk'yi tutar. BirPchunk'ınıFDveBKarasından unlink etmek yalnızcaFD->bk == PveBK->fd == Pise geçerlidir. - Her chunk kendi
size'ını saklar; next chunkprev_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
keytaşı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-freekeyfield'ı 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:
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¶
- glibc source.
malloc/malloc.c(unlink_chunk, tcache and fastbin checks,malloc_printerrstrings). — https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c - Bootlin Elixir cross-reference of
malloc/malloc.c. — https://elixir.bootlin.com/glibc/latest/source/malloc/malloc.c