io_uring fixed-buffer OOB physical memory access (CVE-2023-2598)¶
io_uring fixed-buffer registration'ındaki hatalı bir folio-coalescing optimizasyonu, çok kez map'lenen tek bir physical page olan bir buffer'ın fiziksel olarak contiguous gibi ele alınmasına izin verir, böylece
READ_FIXED/WRITE_FIXEDo page'in ötesindeki physical memory'yi okur ve yazar.
Mechanism¶
IORING_REGISTER_BUFFERS, fixed buffer'ları io_sqe_buffer_register() (io_uring/rsrc.c) üzerinden register eder. Bir buffer'ı verimli temsil etmek için function, tüm page'lerinin tek bir folio'ya (compound page) ait olduğu durumu tespit etmeye ve onları tek bir bio_vec'e collapse etmeye çalışır.
Note
Check, page'lerin aynı folio'ya map'lendiğini doğrular ama onların ardışık olduğunu asla doğrulamaz:
Aynı physical page farklı virtual adreslerde çok kez map'lenebilir, dolayısıyla tüm "page'ler" tek bir folio paylaşır ve check geçer. Buffer ardından tek birbio_vec ile tanımlanır; bv_page'i o tek page'e işaret eder ama bv_len'i tam virtual length'e ayarlanır. Sonraki fixed I/O, bv_page'ten başlayan bv_len byte'ı geçerli olarak ele alır — page'in ötesindeki physical memory'yi okur veya yazar.
Kavramsal görünüm (boyutlar/adresler kernel'e göre değişir):
Virtual view (registration sırasında görülen) Physical reality
┌──────────┬──────────┬──────────┬──────────┐ ┌──────────┐
│ vpage 0 │ vpage 1 │ vpage 2 │ vpage 3 │ │ PAGE P │ <- tek physical page
└──────────┴──────────┴──────────┴──────────┘ ├──────────┤
her biri MAP_FIXED ile PAGE P'ye map'li │ ??? (OOB)│ <- ardışık varsayılan,
→ page_folio() hepsi için aynı folio │ ??? (OOB)│ aslında ilgisiz
│ ??? (OOB)│ physical memory
Coalesce sonrası tek bio_vec: └──────────┘
bv_page = PAGE P (tek page)
bv_len = 4 * PAGE_SIZE (tüm virtual span)
│
└─ fixed I/O bv_page'ten itibaren bv_len byte okur/yazar:
ilk PAGE_SIZE byte geçerli, kalanı PAGE P'nin ötesindeki
physical memory'ye OOB read/write.
Warning
v6.3-rc1'de (commit 57bebf807e2a) tanıtıldı, v6.4-rc1'de (commit 776617db78c6, "io_uring/rsrc: check for nonconsecutive pages") düzeltildi; 6.3.2 için backport 14ad317320c7c. Sınıflandırma CWE-787 (OOB write); CVSS 3.1 7.8 (High), AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H. Primitive, physical memory'ye OOB erişimdir, ki bu alışılmadık biçimde güçlüdür.
Walkthrough¶
Reproduction (Yousef Sajjadi / anatomic.rip PoC):
- Bir
memfdoluştur ve içinde tek bir pagefallocateet. - O tek page'i
MAP_FIXEDile ardışık virtual adreslere tekrar tekrarmmap'le. - Tüm region'ı
IORING_REGISTER_BUFFERSile bir fixed buffer olarak register et — folio check kandırılır, böylecebvec.bv_lenvirtual span olur amabv_pagetek bir page'dir. IORING_OP_WRITE_FIXEDbuffer'ı başka bir file'a yazar → physical memory'nin OOB read'i;IORING_OP_READ_FIXEDbuffer'a okur → physical memory'ye OOB write.
Yayınlanan exploit bunu, KASLR'ı yenmek için bir struct sock leak ederek, ioctl'ü call_usermodehelper_exec()'e işaret eden bir struct proto forge ederek ve onu bir root komutu çalıştırmak için çağırarak LPE'ye zincirler.
Detection¶
KASAN physical OOB'yi doğrudan yakalamaz (erişim geçerli bir bio_vec üzerindendir); tanıtan/düzelten commit'ler ve version aralığı güvenilir göstergelerdir. Memfd + tek bir page'in tekrarlı MAP_FIXED'i ardından buffer registration gözlemlenebilir pattern'dir.
Mitigation¶
≥ 6.4-rc1 / backport'lara güncelle; burada registration kodu page contiguity'sini doğrular. Unprivileged io_uring'i kısıtlamak (io_uring_disabled) registration path'ine erişimi kaldırır.