Skip to content

Refcount imbalance UAF

Bir reference-counting imbalance — eksik bir increment ya da fazladan/gereksiz bir decrement — canlı reference'lar dururken bir objenin count'unu sıfıra düşürür, onu erken free eder ve o reference'ları use-after-free için dangling bırakır.

Mechanism

Note

Reference counting, kernel'in manuel lifetime kontratıdır: paylaşılan bir objeye pointer tutan her taraf bir get (increment) almalı ve işi bitince tam olarak bir put (decrement) bırakmalıdır; obje, count sıfıra ulaştığında free edilir. Invariant katı bir dengedir — counter her an outstanding reference sayısına eşit olmalıdır. Bir imbalance, bu kontratı tehlikeli yönde bozar:

  • eksik bir increment (acquire'da under-counting): count artırılmadan yeni bir reference yaratılır, böylece o reference hâlâ canlıyken obje free edilebilir;
  • gereksiz / aşırı decrement (release'de over-counting): bir error path ya da bir çift put, alınandan fazlasını decrement eder, böylece count bir veya daha fazla reference erken sıfıra çarpar.

Her iki durumda da son put, başka bir subsystem hâlâ geçerli bir pointer'a sahip olduğuna inanırken objeyi free eder. O dangling pointer use-after-free'dir. Bu, bir counter overflow'undan farklıdır: burada count, 2^32 etrafında wrap etmez, yalnızca aritmetik-denge bug'ı ile fazla erken sıfıra sürülür. Wrap-around varyantı için bkz. refcount-overflow-use-after-free.

Walkthrough

Kanonik şekil, hiç başarılı şekilde almadığı bir reference'ı put'layan ya da cleanup sırasında çift-put'layan bir error path'tir:

/* Illustrative over-decrement on an error path */
obj = lookup_get(id);          /* refcount: 1 -> contract: caller owns one put */
if (some_check_fails) {
        put_obj(obj);          /* legitimate release: 1 -> 0, object FREED here */
        goto err;
}
...
err:
        put_obj(obj);          /* BUG: redundant decrement on already-freed obj  */
                               /* second put underflows ownership -> UAF/double-free */

Gerçek dünyadan bir örnek: Linux 6.8 sınıfı kernel'lerdeki AF_UNIX garbage-collector UAF'i; burada legacy GC, 2'lik bir skb refcount'u beklerken kfree_skb() çağırdı, ama upstream değişikliklerden sonra out-of-band skb'ler yalnızca 1'lik bir refcount taşıyordu — dolayısıyla decrement skb'yi bir reference erken free etti ve GC'yi free edilmiş bir obje üzerinde yürür halde bıraktı.

Bir imbalance'ı exploit etmek standart UAF akışını izler:

  1. Imbalance'ı tetikle ki sen hâlâ bir reference tutarken (ya da kernel daha sonra dereference edeceği birini hâlâ tutarken) obje free edilsin.
  2. Reclaim — uyumlu boyutta kontrol edilebilir bir victim obje spray ederek free edilen slot'u geri al (ya da whole-slab durumlarında bir cross-cache reclaim).
  3. Stale reference'ı kullan — kernel artık attacker-controlled olan belleği dereference eder ve kontrollü bir call/read/write verir.
# detecting the bug class during fuzzing / testing:
# KASAN is a compile-time instrumentation (CONFIG_KASAN=y), not a runtime sysfs toggle.
# When the dangling reference is dereferenced it reports "use-after-free"
# with stack traces for both the free and the re-use (re-alloc) sites.
Over- vs under-counting (LinKRID terminolojisi)

Reference-count bug'ları iki sonuca ayrılır: bir eksik decrement (over-counting) bellek leak eder ve yalnızca bir DoS/leak'tir; bir gereksiz decrement (under-counting) — ya da eksik bir increment — erken free'ye ve use-after-free'ye yol açar. Yalnızca under-counting / missing-increment yönü code execution için exploit edilebilir, bu yüzden bu imbalance UAF'leri güvenlik açısından ilgili yarıyı oluşturur.

Detection

KASAN, dangling dereference'ı güvenilir şekilde use-after-free ya da double-free olarak flag'ler ve freeing ile re-use stack'lerini raporlar. Path başına acquire/release dengesini modelleyen static analyzer'lar (örn. LinKRID yaklaşımı ve kernel coccinelle/smatch refcount check'leri) missing-increment / redundant-decrement bug'larını runtime'dan önce yakalar. CONFIG_DEBUG_KOBJECT_RELEASE ve refcount_t underflow uyarıları erken sıfıra düşüşleri yüzeye çıkarır.

Mitigation

Ham atomic_t counter'ları refcount_t'ye çevirmek, overflow'ları önlediği şekilde imbalance UAF'lerini önlemez — ama refcount_t'nin sıfırın altına decrement'te WARN'laması ve sıfır count'u increment etmeyi reddetmesi (ölü bir objede refcount_inc() warn'lar), birçok imbalance'ı sessiz free'ler yerine gürültülü, exploit edilemez başarısızlıklara çevirir. Kalıcı fix doğru hesaplamadır: saklanan her pointer için tam olarak bir reference al ve error path'leri dahil her path'te tam olarak bir tane bırak. İlgili credential-lifetime suistimalleri dirtycred-credential-swap'te görünür.

References