Skip to content

Scheduler-based race exploitation

Linux scheduler'ın kendi primitive'lerini — scheduling class, priority ve CPU affinity — kullanarak thread interleaving'i kontrol et; böylece dar bir kernel race condition window'u deterministik olarak vurulabilir.

Mechanism

Bir kernel race condition, ancak attacker iki operation'ı paylaşılan bir object'in tam da tutarsız durumda olduğu anda interleave edebiliyorsa exploit edilebilir. Window genelde sadece birkaç instruction genişliğindedir, dolayısıyla brute-force güvenilir değildir. Scheduler, bir process'in kendi thread'lerinin ne zaman çalışacağını şekillendirmesine izin veren unprivileged ayar düğmeleri sunar.

Note

Yarışan thread'leri sched_setaffinity() ile aynı CPU'ya pin'lemek onları tek bir core için çekişmeye zorlar; böylece aynı anda sadece biri çalışır ve sıralamayı parallelism değil, preemption belirler. Bir thread'e SCHED_NORMAL, victim thread'e ise sched_setscheduler() ile SCHED_IDLE atamak, busy-loop içinde dönen bir SCHED_NORMAL thread'in SCHED_IDLE thread'i aç bırakmasını (starve) sağlar; bu da victim'i syscall'un ortasında etkili biçimde duraklatır. Busy loop'u bırakmak victim'i seçilen bir anda devam ettirir ve birkaç instruction'lık window'u attacker'ın kontrol ettiği bir window'a genişletir.

Bu, herhangi bir özel privilege gerektirmeden olasılıksal bir race'i neredeyse deterministik hale çevirir.

Walkthrough

CPU 0'a pin'lenmiş, karşıt scheduling class'lara sahip iki thread kur:

#include <sched.h>

void pin_cpu0(void){
    cpu_set_t s; CPU_ZERO(&s); CPU_SET(0,&s);
    sched_setaffinity(0, sizeof(s), &s);          // same core => serialized
}

void make_idle(void){
    struct sched_param p = { .sched_priority = 0 };
    sched_setscheduler(0, SCHED_IDLE, &p);        // victim: lowest priority
}

void make_normal(void){
    struct sched_param p = { .sched_priority = 0 };
    sched_setscheduler(0, SCHED_NORMAL, &p);
}

Sonra: (1) victim thread (SCHED_IDLE) açık veren syscall'a girer ve race noktasında preempt edilir; (2) bir SCHED_NORMAL "destroy" thread'i busy loop içinde döner, victim'i aç bırakır ve window içinde dondurur; (3) attacker çakışan operation'ı gerçekleştirir (ör. free/remap); (4) busy loop'u durdur, victim devam eder ve artık eskimiş (stale) object üzerinde işlem yapar.

Gerçek dünya uygulaması

GitHub Security Lab tam olarak bu pattern'i msm-5.4 üzerindeki Qualcomm kgsl GPU driver'ında (CVE-2022-22057) bir UAF kazanmak için uyguladı: bir DESTROY_CPU busy-loop thread'i, kgsl_timeline race window'unu güvenilir biçimde genişletmek için bir SCHED_IDLE task'ı park eder.

Detection

sched_setscheduler(SCHED_IDLE)/sched_setaffinity()'nin anormal kullanımının hemen ardından yoğun CPU çekişmesi gelmesi ya da tek bir core'a pin'lenmiş birçok thread'in tek bir syscall'u yarıştırması, runtime monitor'lar tarafından gözlemlenebilir (bu syscall'ların seccomp/eBPF ile denetlenmesi).

References