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:
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ç.