CAN BCM race-condition LPE (CVE-2021-3609)¶
net/can/bcm.ciçindekibcm_release()ile çalışmakta olanbcm_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 veyacan-bcmmodü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.