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 birio_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:
IORING_OP_PROVIDE_BUFFERSile bir legacy provided-buffer grubu oluştur, birio_buffer_listallocate edilir.- O buffer grubunu boşalt ki list yeniden kullanıma uygun hale gelsin.
- Aynı group id'yi hedefleyen bir buffer ring'i
IORING_REGISTER_PBUF_RINGile register et; böyleceio_register_pbuf_ring()→io_buffer_get_list()o eski list'i yeniden kullanır veio_alloc_pbuf_ring()onu yeniden type'lar. - 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.