Skip to content

io_uring memory-sharing bypass (CVE-2025-21836)

IORING_REGISTER_PBUF_RING, legacy provided buffer'lar için oluşturulup sonradan boşaltılmış mevcut bir io_buffer_list'i yeniden kullanır; bu da "yayınlandıktan sonra alanları stabil kalır" varsayımını ihlal eder — paylaşılan buffer-list yapısı üzerinde bir type confusion / UAF.

Mechanism

io_uring'in provided-buffer mekanizmasının aynı struct io_buffer_list ile desteklenen iki çeşidi var: legacy/klasik buffer'lar (IORING_OP_PROVIDE_BUFFERS ile eklenir, bl->buf_list üzerinden zincirlenir) ve ring buffer'lar (IORING_REGISTER_PBUF_RING ile register edilir, bl->buf_ring ile tanımlanır). Bu iki görünüm bir union paylaşır, yani aynı bellek her modda farklı bir anlama gelir.

Note

IORING_REGISTER_PBUF_RING mevcut, artık boş bir io_buffer_list (başlangıçta legacy buffer'lar için oluşturulmuş) bulduğunda onu yeniden kullanır ve io_alloc_pbuf_ring() onu ring moduna çevirir (bl->is_mmap = 1, ring page'leri allocate eder). Ama bu buffer list'e hâlâ eşzamanlı olarak erişilebilir — mmap validation path'i (io_uring_validate_mmap_request() / io_pbuf_get_bl()) allocation'dan sonra ama refcount sıfırlanmadan önce bl->refs'i artırabilir, yani "reference count'un hatalı güncellenmesi." buf_list/buf_ring union örtüşmesiyle birleşince, free edilmiş/yeniden kullanılmış io_buffer_list tutarsız alanlarla xarray üzerinden erişilebilir kalır — bir use-after-free / type confusion.

Warning

İhlal edilen invariant şu: "io_buffer_list'in çoğu alanı publish'ten sonra stabil kalmalı." Yayınlanmış bir yapıyı yeniden kullanıp concurrency altında yeniden type'lamak kök neden; fix ise legacy olanı yeniden kullanmak yerine her zaman taze bir io_buffer_list reallocate etmek.

Walkthrough

"Déjà Vu in Linux io_uring: Breaking Memory Sharing Again After Generations of Fixes" (Pumpkin Chang / u1f383) sunumunda anlatıldı. Kavramsal yol:

  1. IORING_OP_PROVIDE_BUFFERS ile bir legacy provided-buffer grubu oluştur, bir io_buffer_list allocate edilir.
  2. O buffer grubunu boşalt ki list yeniden kullanıma uygun hale gelsin.
  3. Aynı group id'yi hedefleyen bir buffer ring'i IORING_REGISTER_PBUF_RING ile register et; böylece io_register_pbuf_ring()io_buffer_get_list() o eski list'i yeniden kullanır ve io_alloc_pbuf_ring() onu yeniden type'lar.
  4. mmap-validation refcount path'iyle race et; yeniden kullanılan list'i tutarsız bir state ve bozuk bir refcount ile bırak, io_buffer_list üzerinde UAF/type confusion elde et.

Detection

KASAN, io_buffer_list üzerinde use-after-free / refcount underflow raporlar. Tetikleme dizisi (legacy provide-buffers → boşalt → eşzamanlı mmap ile aynı grupta pbuf ring register et) gözlemlenebilir davranıştır.

Mitigation

io_register_pbuf_ring()'in legacy olanı yeniden kullanmak yerine her zaman yeni bir io_buffer_list allocate ettiği bir kernel'a güncelle. Unprivileged io_uring'i kısıtlamak bu surface'i ortadan kaldırır.

References