Skip to content

CAN BCM UAF (CVE-2023-52922)

CAN Broadcast Manager'daki bir teardown-ordering bug'ı, per-op objelerini procfs file'larını unregister etmeden önce free ediyor; bu da bir use-after-free read üzerinden kernel memory'sini sızdırıyor.

Mechanism

net/can/bcm.c içinde, CAN Broadcast Manager her operation için tutulan struct bcm_op objelerini BCM socket struct bcm_sock üzerindeki iki linked list'te (bo->tx_ops ve bo->rx_ops) saklıyor; ayrıca /proc/net/can-bcm/ altında bir procfs entry'si (bo->procname) tutuyor.

Note

Bug aslında bcm_release() içindeki bir lifetime/ordering hatası: socket close sırasında önce op list'leri teardown ediyor — her bcm_op'u bcm_remove_op() (kfree(op)) ile free ederek — ve remove_proc_entry()'yi ancak bundan sonra çağırıyor. Bu window boyunca proc file hâlâ register edilmiş durumda, dolayısıyla eşzamanlı (concurrent) bir reader bcm_proc_show()'u tetikliyor; bu fonksiyon op list'leri list_for_each_entry() ile dolaşıp free edilmiş bcm_op memory'sini dereference ediyor. KASAN bunu bir slab-use-after-free read olarak raporluyor.

Fix (commit 55c3b96074f3, "can: bcm: Fix UAF in bcm_proc_show()", Temmuz 2023) bcm_release()'i yeniden sıralayarak remove_proc_entry()'yi op'lar free edilmeden önce çağırıyor.

Walkthrough

  1. socket(PF_CAN, SOCK_DGRAM, CAN_BCM) aç, ardından bir CAN interface'ine (örn. bir vcan/vxcan device'ı) connect() yap — connect, socket başına procfs file'ını register ediyor.
  2. Op list'leri doldur: bcm_sendmsg() üzerinden, opcode'u TX_SETUP (tx bcm_op) ya da RX_SETUP (rx bcm_op) olan bir struct bcm_msg_head'i sendmsg() ile gönder.
  3. İki thread'i race ettir:
  4. Thread A sürekli olarak /proc/net/can-bcm/<entry>'yi read() ediyor → bcm_proc_show() op list'leri dolaşıyor.
  5. Thread B socket'i close() ediyor → bcm_release(), proc entry'sini unregister etmeden önce bcm_op objelerini free ediyor.

Bu window içinde A, free edilmiş bcm_op memory'sini dereference ediyor → UAF read.

Race-window timeline (kavramsal — sıralama bug'ını gösterir, boyut/adres yok):

  Thread B: bcm_release()        Thread A: bcm_proc_show()
  ---------------------------    --------------------------------
  bcm_remove_op() → kfree(op) ─┐
                               │  list_for_each_entry(op, ...)
        << op artık freed >>   ├─►  deref op->...   ◄── UAF read
                               │     (freed bcm_op)
  remove_proc_entry()  ........┘  (proc entry hâlâ register'lı)

  Fix: remove_proc_entry() önce çağrılır →
       reader, op'lar free edilmeden çıkarılmış olur (window kapanır).
Exploitation primitive

Information disclosure. bcm_proc_show() free edilmiş bcm_op struct'ının field'larını print ediyor; pratik etki (örn. kernel-pointer / KASLR recovery) struct layout'una ve runtime koşullarına bağlıdır ve daha fazla araştırma gerektirir. Unprivileged erişim, user namespace'lere artı virtual CAN'e (CONFIG_CAN_VXCAN) dayanıyor.

Detection

KASAN ile bulundu (bcm_proc_show() içinde slab-UAF read). Runtime'da, unprivileged PF_CAN/CAN_BCM kullanımının, socket close ile race eden hızlı /proc/net/can-bcm/ read'leriyle birleşmesi anormaldir.

Mitigation

Düzeltilmiş stable sürümlere patch'le (4.14.322, 4.19.291, 5.4.251, 5.10.188, 5.15.123, 6.1.42, 6.4.7+). Kullanılmıyorsa CONFIG_CAN_BCM'i devre dışı bırak ve unprivileged user namespace'leri / vxcan oluşturmayı kısıtla.

References