Skip to content

Freelist poisoning

SLUB freelist next-pointer'ını overwrite ederek allocation'ları yeniden yönlendir — ve tam tersine, tam bu overwrite'ı detectable ya da işe yaramaz kılmak için tasarlanan hardening (CONFIG_SLAB_FREELIST_HARDENED) ile debug poison değerleri.

Mechanism

Terimin aynı alanda buluşan iki anlamı vardır. Offensively, freelist'i "poison'lamak", bir free chunk'ın içinde object + s->offset'te saklanan next-pointer'ı overwrite ederek allocator'ın attacker'ın seçtiği bir adresi döndürmesini sağlamak demektir. Defensively, kernel free edilmiş memory'yi "poison'lar": CONFIG_SLAB_FREELIST_HARDENED next-pointer'ı obfuscate eder ve eski SLAB_POISON debug özelliği obje gövdelerini sentinel byte'larla doldurur; böylece bir use-after-free görünür olur.

Note

Hardening invariant'ı: next-pointer açık (clear) saklanmaz. ptr XOR s->random XOR swab(ptr_addr) olarak saklanır; burada s->random, kmem_cache_open()'da set edilen per-cache bir gizli değerdir ve ptr_addr, pointer'ın saklandığı adrestir. Decode için ikisi de gerekir, dolayısıyla kör bir overwrite unmapped bir adrese decode olur (ve saklanan bir 0 artık NULL'a decode olmaz). swab(ptr_addr) byte-swap'i, orijinal 2017 patch'inden sonra 2019'da commit 1ad53d9fa3f6 ("slub: improve bit diffusion for freelist ptr obfuscation", Kees Cook) ile eklenmiştir çünkü ptr ile ptr_addr sayısal olarak yakındır — onsuz naif XOR çoğunlukla sıfır olan high byte'ları leak ediyordu. Güncel mainline mm/slub.c (freelist_ptr_encode/freelist_ptr_decode) swab(ptr_addr)'lı formu kullanır; swab'sız gösterimler pre-2019 kernel'ı yansıtır.

Walkthrough

mm/slub.c'den encode/decode çifti:

/* encode on free */
encoded = (unsigned long)ptr ^ s->random ^ swab(ptr_addr);
/* decode on alloc */
decoded = (void *)(stored ^ s->random ^ swab(ptr_addr));

Orijinal 2017 patch'inde swab() yoktu:

return (void *)((unsigned long)ptr ^ s->random ^ ptr_addr);

include/linux/poison.h'den debug poison sabitleri:

#define SLUB_RED_INACTIVE 0xbb   /* redzone, object free */
#define SLUB_RED_ACTIVE   0xcc   /* redzone, object in use */
#define POISON_INUSE      0x5a   /* in-use object body */
#define POISON_FREE       0x6b   /* freed object body ("kkkk") */
#define POISON_END        0xa5   /* last byte of freed body */

Pratik okuma: slub_debug=P ile, free edilip sonra okunan bir obje 0x6b byte'ları gösterir — bir UAF-read işareti. Hardened bir kernel'da next-pointer'ı overwrite etmesi gereken bir exploit önce s->random'ı ve chunk adresini leak etmelidir; aksi takdirde genellikle aynı slab'ın içinde kalan bir low-byte partial overwrite'a başvurur (in-slab hedefler için decode gerekmez) ya da pointer'a hiç dokunmadan bitişik data alanlarını bozmaya yönelir (örn. komşu bir msg_msg size alanı).

Detection

SLAB_POISON / redzone'lar UAF'i (gövde 0x6b olarak geri okunur), uninitialized-use'u (0x5a) ve OOB write'ları (redzone 0xbb/0xcc'den sapar) tespit eder. Bunlar runtime barrier değil, debug yardımcılarıdır; production kernel'lar genellikle bunları kapalı bırakır.

Mitigation

CONFIG_SLAB_FREELIST_HARDENED (obfuscation + BUG_ON(object == fp)) ve CONFIG_SLAB_FREELIST_RANDOM (karıştırılmış başlangıç sırası) mainline mitigation'lardır. Bilinen bypass sınıfları: s->random'ı leak et ve geçerli bir encoding forge et; partial low-byte in-slab overwrite; ya da komşu objelerin data-only corruption'ına geç.

References