Reference count overflow/underflow¶
Bir resource'un reference counter'ı yanlış güncellenir (genişliğinin ötesine over-increment edilir ya da çok erken decrement edilir), böylece object hâlâ canlı reference'lar varken free edilir — use-after-free'ye yol açar; CWE-911.
Mechanism¶
Invariant
Reference counting tek bir invariant'ı korur: counter, bekleyen reference sayısına eşittir ve resource tam olarak bu sayı sıfıra ulaştığında free edilir. Bir reference alan her code path increment etmeli, bir tanesini bırakan her path decrement etmelidir — simetrik olarak. CWE-911 ("Improper Update of Reference Count") bug'ı şöyle tanımlar: bir ürün "bir resource'u yönetmek için bir reference count kullanır, ama reference count'u güncellemez veya yanlış günceller."
İki failure mode invariant'ı kırar, ikisi de dangling bir object'le biter:
- Underflow / premature decrement — bir error path ya da çoğaltılmış bir
putçok erken ya da iki kez decrement eder. Bir reference hâlâ tutulurken count sıfıra iner (ya da sıfırın altına wrap eder); object free edilir; hayatta kalan reference dangling bir pointer olur → use-after-free. - Overflow — counter fixed-width bir integer'dır (Linux kernel'de yaygın olarak 32-bit
atomic_t). Eşleşen bir decrement olmadan increment eden bir leak path, attacker'ın count'u maksimumuna kadar pompalayıp 0'a wrap etmesine izin verir. Kernel o zaman hâlâ referans verilen object'i referanssız sayar ve onu free eder — yine bir UAF, ama erken bir bırakma yerine counter'ı doyurarak (saturate) ulaşılan.
Her iki durumda da tehlikeli an aynıdır: object'e bir pointer hayatta kalırken free() çalışır. O pointer'ı sonra dereference eden her kim olursa, free edilmiş (ve potansiyel olarak yeniden tahsis edilmiş, attacker-controlled) bellek üzerinde çalışır.
Walkthrough¶
Ders kitabı vakası CVE-2016-0728'dir; Linux keyring'inde bir refcount-overflow UAF (Perception Point, 2016). security/keys/process_keys.c içindeki join_session_keyring(), find_keyring_by_name() üzerinden bir reference alır, ama bir process session keyring'ini aynı keyring ile değiştirmeye çalıştığında, eşleşen key_put()'u atlayan bir error label'a atlar — çağrı başına bir reference leak eder. Reference count 32-bit bir atomic_t olduğundan, tekrarlı çağrılar onu sıfıra overflow edebilir.
/* Conceptual shape of the leaking path (per the Perception Point analysis):
* each KEYCTL_JOIN_SESSION_KEYRING on the SAME name leaks one refcount. */
for (i = 0; i < 0xfffffffd; i++) /* ~4.29 billion iterations */
keyctl(KEYCTL_JOIN_SESSION_KEYRING, "leaked-keyring");
/* key->usage (atomic_t, 32-bit) now near overflow; a few more wrap it to 0,
* the keyring is freed while our process still references it -> UAF. */
Loop'un 32-bit bir counter'ı ~2^32 increment boyunca sürmesi gerektiğinden, PoC milyarlarca keyctl syscall çalıştırır (yayınlanan exploit 0xfffffffd'ye kadar döner); asıl overflow-ve-free son birkaç increment'tir.
# On a vulnerable kernel (< 4.4.1) the PoC runs for minutes pumping the refcount:
$ gcc cve-2016-0728.c -o poc -lkeyutils && ./poc
uid=0 ... # after the keyring is freed and reclaimed with a fake key_type,
# KEYCTL_REVOKE invokes an attacker-controlled key_type->revoke
Overflow neden code execution'a dönüşür
key->usage 0'a wrap ettiği anda keyring object'i free edilir ama dangling bir reference kalır. Exploit, free edilen slot'u bir struct key şeklinde attacker-controlled veriyle reclaim etmek için heap object'leri spray eder, böylece gömülü key_type (kendi revoke/destroy function pointer'larıyla) attacker belleğine işaret eder. Sonraki bir KEYCTL_REVOKE, key_type->revoke'u dereference eder ve kernel control flow'unu yönlendirir — klasik commit_creds(prepare_kernel_cred(0)) privilege escalation. Refcount overflow primitive'dir; UAF ve reclaim ardından gelen exploitation'dır.
Genişlik ve timing tuzakları
- Counter genişliği önemlidir: 32-bit bir
atomic_tdakikalar süren syscall'larla erişilebilir; 64-bit'e genişletmek ya darefcount_tkullanmak (wrap etmek yerine saturate eder) overflow path'ini tamamen etkisizleştirir. - Decrement bug'ları overflow bug'larından çok daha ucuzdur — bir error path'teki tek bir eşleşmeyen
putcount'u anında düşürür. Leak edilmiş ya da iki kez bırakılmış bir reference için her early-return/goto err'i denetle.
Detection¶
- Linux
refcount_t:atomic_treference counter'larınırefcount_t'ye çevirmek, kernel'in overflow/underflow'daWARNverip saturate etmesini sağlar ve bug'ı sessizce wrap etmek yerine yüzeye çıkarır. - KASAN/ASan, counting bug'ının kendisi sessiz olsa bile ortaya çıkan use-after-free'yi dereference noktasında yakalar.
- Statik analiz ve kod review: take/put'un tüm path'lerde, özellikle error
gotochain'lerinde dengeli olduğunu kontrol et.
Mitigation¶
- Saturate eden reference tipleri kullan (kernel'de
refcount_t; güvenli dillerde kontrollüRc/Arcsemantiği) ki overflow/underflow wrap edilmek yerine tespit edilsin. - take/put'u simetrik ve merkezi yap; error path'lere dağılmış manuel increment/decrement'ten kaçın.
- Counter'ları genişlet ve/veya işlemleri rate-limit et ki bir attacker bir counter'ı gerçekçi olarak tüm aralığı boyunca süremesin.
References¶
- CWE-911: Improper Update of Reference Count (MITRE)
- Linux Kernel 4.4.1 - REFCOUNT Overflow Use-After-Free in Keyrings (CVE-2016-0728), Exploit-DB 39277
İlgili kernel-kategorisi tekniği: refcount overflow use-after-free.