race-condition UAF LPE (CVE-2020-27786)¶
ALSA rawmidi ioctl buffer-resize path'indeki bir race, eşzamanlı bir read/write hâlâ kullanırken runtime buffer'ı free eder; local root'a yükselen bir use-after-free doğurur.
Mechanism¶
Note
İhlal edilen invariant şudur: "başka bir thread ona canlı bir pointer tutarken bir buffer free edilmemelidir." ALSA rawmidi core'u, user space'in runtime->buffer'ı ioctl (SNDRV_RAWMIDI_IOCTL_PARAMS) üzerinden resize etmesine izin verir. Bu sırada snd_rawmidi_kernel_read1() / snd_rawmidi_kernel_write1(), user space'e/'den kopyalamak zorundadır ve bunu yapmak için copy_from_user/copy_to_user çevresinde runtime spinlock'u geçici olarak bırakırlar. O unlocked aralık race window'udur: ikinci bir thread buffer'ı resize edip eski runtime->buffer'ı free edebilir, birinci thread ise devam edip artık free edilmiş pointer'ı dereference eder — bir use-after-free write/read.
Bug, ders kitabı niteliğinde bir race-condition-uaf-lpe desenidir: user memory'ye güvenli dokunmak için bir lock bırakılır, ama koruduğu shared object o boşlukta mutate edilip free edilebilir. User hem kopya zamanlamasını (page fault'lar yoluyla) hem de resize'ı kontrol ettiği için, window deterministik biçimde genişletilebilir.
Walkthrough¶
Public write-up ve oss-security ifşasına göre race, iki thread artı kopyayı durdurmak için userfaultfd ile sürülür.
Kavramsal race yapısı
// Thread A: issue a write whose source page is backed by userfaultfd.
// copy_from_user() blocks mid-copy with the runtime lock released,
// while A still holds a pointer into runtime->buffer.
write(midi_fd, uffd_backed_page, len);
// Thread B: resize the runtime buffer, which frees the old buffer.
struct snd_rawmidi_params p = { .buffer_size = new_size, ... };
ioctl(midi_fd, SNDRV_RAWMIDI_IOCTL_PARAMS, &p);
// Thread A resumes the copy -> writes into freed memory (UAF).
Mantıksal adımlar:
- Bir rawmidi cihazı aç ve user buffer'ı fault veren (userfaultfd) bir read ya da write başlat, böylece kernel'i lock bırakılmış hâlde kopya ortasında askıya al.
- Başka bir thread'den runtime buffer'ı resize et, böylece eski allocation
kvfree()edilir. - Durdurulmuş kopyayı devam ettir; kernel artık free edilmiş slab memory'sini okur/yazar.
- UAF'i kontrollü bir write'a çevirmek için free edilmiş object'i saldırgan kontrolündeki bir allocation ile (heap grooming) reclaim et, sonra yükselt. Bkz. slab-grooming ve arbitrary-write-primitive.
Detection¶
- Audio rolü olmayan process'lerin
/dev/snd/midi*/ rawmidi node'larınıopen()etmesini, özellikleuserfaultfdkullanımıyla birlikte denetleyin. - Güvenilmeyen process'lerden gelen
userfaultfdsyscall'ları birçok kernel UAF race'i için güçlü bir exploitation sinyalidir; workload'un ihtiyaç duymadığı yerlerde bunları logla/alarm ver. - KASAN-açık kernel'ler "use-after-free in snd_rawmidi_kernel_write1/read1" splat'ları yayar — test/CI fleet'lerinde paha biçilmez.
- Aynı fd üzerinde read/write ile iç içe geçmiş
SNDRV_RAWMIDI_IOCTL_PARAMS'ı zorlayan multi-threaded process'leri izleyin.
Mitigation¶
Warning
snd_rawmidi_buffer_ref() / snd_rawmidi_buffer_unref() reference counting'i ekleyen c13f1463d84b86bedb664e509838bef37e6ea317 commit'ini içeren bir kernel'e yamalayın; resize_runtime_buffer() ardından runtime->buffer_ref sıfır olmadığı sürece free etmeyi reddeder (bir hata döndürür) ve window'u kapatır.
- Distribution kernel'leri (SUSE, Debian, Ubuntu vb.) düzeltmeleri 2021 başında dağıttı — vendor güncellemelerini uygulayın.
- UAF exploitation'ını genel olarak körelten hardening:
vm.unprivileged_userfaultfd=0(userfaultfd'yi privileged user'lara kısıtla), slab-freelist-hardening ve freelist randomization exploitation maliyetini yükseltir. - rawmidi gerekmediğinde
/dev/snd/cihaz node'larına erişimi kısıtlayın.