Skip to content

io_uring reference counting bug (CVE-2021-20226)

Erken bir io_uring, caller'ın files_struct'ını bir worker thread'le reference almadan paylaşır; böylece fdget()'in single-threaded fast path'i per-file refcount'u atlar ve eşzamanlı bir IORING_OP_CLOSE hâlâ kullanımda olan bir struct file'ı free eder — LPE'ye götüren bir refcount-underflow use-after-free.

Mechanism

io_uring işi bir kernel worker'ına devrettiğinde, worker submitter'ın open-file table'ına ihtiyaç duyar. io_grab_files() bunu request üzerine kaydeder. Bug, table'ı fazladan kullanıcıyı hesaba katmadan paylaşmasıdır.

Note

Burada iki reference-count invariant'ı birleşir. (1) io_grab_files(), files_struct refcount'unu artırmadan req->work.files = current->files; yapar; böylece artık table'ı bir worker tutsa da files->count yapay olarak düşük kalır. (2) fdget()'in bir optimizasyonu var: atomic_read(&files->count) == 1 olduğunda struct file'ı per-file refcount'u artırmadan döndürür ("fd table paylaşılmıyorsa refcnt artırma yok"). Worker table'ı paylaşırken files->count hâlâ 1 okuduğundan, fdget() table'ın paylaşılmadığını sanır ve koruyucu reference olmadan bir file pointer'ı verir. Sahip task sonra worker hâlâ onu kullanırken o file'ı (örneğin IORING_OP_CLOSE ile) free edebilir — struct file üzerinde use-after-free üreten fiili bir refcount underflow.

Warning

Free edilen object bir struct file'dır (filp cache). Dangling worker erişiminden önce slot'u reclaim etmek — worker'ın privileged bir files_struct devralması için setuid bir binary'nin execve()'si, root-sahipli fd'leri okumak için IORING_OP_FILES_UPDATE ile birleştirilerek — UAF'ı arbitrary read/write'a ve root'a çevirir.

Walkthrough

GMO Flatt Security (Ryota Shiga) ve ZDI analizlerinden (ZDI-21-001):

  1. io_uring işi submit et ki bir worker current->filesio_grab_files() üzerinden ek reference olmadan tutsun.
  2. files_struct.count == 1 tut ki fdget() worker'ın erişimlerinde per-file refcount'u atlasın.
  3. Sahip task'a IORING_OP_CLOSE çalıştırt ki worker hâlâ korumasız path üzerinden ona reference verirken struct file free edilsin.
  4. Worker'ı operation ortasında durdurup race penceresini genişletmek için userfaultfd kullan; free edilmiş filp slot'unu kontrollü bir object'le reclaim et ve LPE için dangling reference üzerinde işlem yap.
Etkilenen sürümler ve fix

Zafiyetli: Linux 5.6'dan 5.7'ye fs/io_uring.c (open/close-tarzı file op'ları io_uring'e eklendikten sonra). Commit 0f2122045b946241a9e549c2a76cea54fa58a7ff ile fix edildi; bu commit files_struct'ı worker'lara verirken düzgün bir reference alır, böylece files->count > 1 olur ve fdget() artık refcounting'i atlamaz.

Detection

KASAN, IORING_OP_CLOSE/IORING_OP_FILES_UPDATE civarında bir io_uring worker'ından ulaşılan bir struct file üzerinde use-after-free raporlar. 5.6–5.7 sürüm aralığı güvenilir göstergedir.

Mitigation

Fix'li bir kernel'a güncelle (fix'leyen commit / 5.7+ stable backport'ları). Sonraki kernel'lar io_uring'in file-table işleyişini yeniden tasarladı; unprivileged io_uring'i devre dışı bırakmak path'i ortadan kaldırır.

References