Skip to content

CAN BCM race-condition LPE (CVE-2021-3609)

net/can/bcm.c içindeki bcm_release() ile çalışmakta olan bcm_rx_handler() arasındaki bir race condition, struct bcm_op/struct bcm_sock'u hâlâ kullanımdayken free eder; bu da bir use-after-free ve local root sağlar.

Mechanism

Teardown racing an in-flight RX callback

Linux CAN Broadcast Manager (net/can/bcm.c), bir socket'in receive filter'ları register etmesine izin verir. Eşleşen bir CAN frame geldiğinde, çekirdek CAN katmanı onu işlemek için socket'in register edilmiş callback'i bcm_rx_handler()'ı çağırır. Bu callback soft-IRQ / timer context'inden, sahibi olan process'ten asenkron olarak çalışır.

Doğru bir teardown'ın korumak zorunda olduğu invariant şudur: dokunduğu objeler free edildikten sonra hiçbir callback hâlâ çalışıyor olmamalı. bcm_release() (yani close() path'i), filter başına struct bcm_op objelerini ve nihayetinde struct bcm_sock'u free eder; ancak bunu zaten mid-flight olan bir bcm_rx_handler()'a karşı tam olarak senkronize olmadan yapar. Bir frame, socket'in kapatıldığı anla aynı anda teslim edilirse, handler release path'in eşzamanlı olarak free ettiği belleği dereference eder.

Sonuç, free edilmiş bcm_op/bcm_sock allocation'ları üzerinde birden fazla use-after-free olur. Unprivileged bir kullanıcı bunu tamamen bir user namespace içinde, sanal bir CAN interface üzerinde kurabildiği için, bu güvenilir bir local-privilege-escalation primitive'idir. Bug uzun ömürlüdür: kernel 2.6.25'ten 5.13-rc6'ya kadar mevcuttur.

Walkthrough

Race condition, bir vcan interface üzerinde iki BCM socket ile yeniden üretilir:

// 1. Unprivileged user namespace so an unpriv user reaches the CAN stack
unshare(CLONE_NEWUSER | CLONE_NEWNET);

// 2. Bring up a virtual CAN interface (vcan0)
//    ip link add dev vcan0 type vcan; ip link set vcan0 up

// 3. Open two CAN_BCM sockets, connect both to vcan0
int s1 = socket(AF_CAN, SOCK_DGRAM, CAN_BCM);
int s2 = socket(AF_CAN, SOCK_DGRAM, CAN_BCM);
connect(s1, ...vcan0...);
connect(s2, ...vcan0...);

// 4. On s1: RX_SETUP installs a receiver (allocates a struct bcm_op)
sendmsg(s1, /* bcm_msg_head with opcode = RX_SETUP */, 0);

// 5. On s2: send a frame matching s1's filter -> bcm_rx_handler() fires
sendmsg(s2, /* TX frame to s1's CAN id */, 0);

// 6. Concurrently close(s1) -> bcm_release() frees bcm_op / bcm_sock
//    while bcm_rx_handler() is still running -> UAF
close(s1);

Bu pencere 5.4-rc1 ve sonrasında genişler; burada bf74aa86e111 commit'i BCM timer'larını HRTIMER_MODE_SOFT'a geçirerek asenkron handler'ın release path ile güvenilir biçimde çakışmasını sağlar.

From UAF to root

Free edilmiş bcm_sock/bcm_op slab objesi, saldırganın kontrol ettiği data ile (benzer boyutlu objelerin bir heap spray'i) yeniden ele geçirilir. bcm_rx_handler() ve BCM timer path'leri bu yapıların içindeki function pointer'ları ve list link'leri dereference ettiği için, kontrollü bir reallocation saldırganın control flow'u hijack etmesine / komşu state'i bozmasına ve bir privilege-escalation payload'ına (örneğin credential'ları veya modprobe_path'i overwrite etmek) pivot etmesine olanak tanır. Yeniden ele geçirme stratejileri için bkz. use-after-free-heap-grooming ve cross-cache-attack.

Detection

  • Unprivileged namespace'lerden gelen CAN_BCM socket çalkantısını (hızlı RX_SETUP + close) denetlemek, SocketCAN'i hiç kullanmayan çoğu sistemde anomalidir.
  • KASAN build'leri use-after-free'i doğrudan bcm_rx_handler()'da yakalar.

Mitigation

  • BCM teardown'ı in-flight RX handling'e karşı düzgün biçimde senkronize eden upstream fix'i (5.13'ten önce merge edildi) uygulayın.
  • İhtiyacı olmayan host'larda CAN subsystem'ini (CONFIG_CAN/CONFIG_CAN_BCM=n) devre dışı bırakın veya can-bcm modülünü blacklist'leyin.
  • AF_CAN'a erişimi kesmek için unprivileged user namespace'leri (kernel.unprivileged_userns_clone=0) kısıtlayın.

References