제출 #844580: RT-Thread v5.0.2 Out-of-bounds Write정보

제목RT-Thread v5.0.2 Out-of-bounds Write
설명I have already reported this vulnerability in the project's GitHub issue tracker for review by the maintainers. The report is provided below. # ls1c CAN receive path trusts raw DLC and writes past rt_can_msg data ## Describe the bug The Loongson ls1cdev CAN receive path stores the raw hardware DLC value in a global receive message and later uses that value as the copy bound for `struct rt_can_msg.data[8]`. The external input boundary is the CAN controller receive frame. When a frame arrives from the CAN bus, the controller exposes attacker-controlled frame metadata through the `CANx->IDE_RTR_DLC` register. The low 4 bits of this register are used as the DLC. That external DLC value is stored in the global `RxMessage.DLC` and later controls the copy loop in `recvmsg()`. The concrete data flow is: ```text external CAN frame -> CAN controller RX register CANx->IDE_RTR_DLC[DLC] -> CAN_Receive(): global RxMessage.DLC -> ls1c_can0_irqhandler()/ls1c_can1_irqhandler() -> rt_hw_can_isr() -> recvmsg(): pmsg->len and loop bound -> out-of-bounds read from RxMessage.Data[8] -> out-of-bounds write to pmsg->data[8] ``` The RT-Thread classic CAN message type stores only 8 payload bytes when `RT_CAN_USING_CANFD` is not enabled: ```text components/drivers/include/drivers/dev_can.h:510 components/drivers/include/drivers/dev_can.h:526 ``` The ls1c low-level receive routine extracts the low 4 bits of the hardware `IDE_RTR_DLC` register: ```c /* External input: DLC bits from the received CAN frame. */ RxMessage->DLC = (CANx->IDE_RTR_DLC & 0x0F); ``` `CanRxMsg.Data` is only 8 bytes: ```c typedef struct { unsigned long StdId; unsigned long ExtId; unsigned char IDE; unsigned char RTR; unsigned char DLC; unsigned char Data[8]; } CanRxMsg; ``` The interrupt handler stores the received frame in the global `RxMessage` and then calls the generic RT-Thread CAN ISR: ```c /* CAN_Receive stores the external DLC in global RxMessage.DLC. */ CAN_Receive(CANx, &RxMessage); CANx->CMR |= CAN_CMR_RRB; CANx->CMR |= CAN_CMR_CDO; rt_hw_can_isr(&bxcan0, RT_CAN_EVENT_RX_IND); ``` The registered `recvmsg` callback then uses the unchecked global `RxMessage.DLC` as the loop bound: ```c /* RxMessage.DLC is derived from the external CAN DLC. */ pmsg->len = RxMessage.DLC; /* * Overflow trigger: * RxMessage.Data has valid indexes [0..7]. * pmsg->data also has valid indexes [0..7]. * If the external DLC is 9..15, the iteration with i == 8 reads * RxMessage.Data[8] and writes pmsg->data[8]. */ for(i= 0;i< RxMessage.DLC; i++) { pmsg->data[i] = RxMessage.Data[i]; } ``` In the generic CAN ISR, `pmsg` points to a stack `struct rt_can_msg tmpmsg`: ```c /* Stack object inside rt_hw_can_isr(). */ struct rt_can_msg tmpmsg; /* recvmsg() receives &tmpmsg as pmsg. */ ch = can->ops->recvmsg(can, &tmpmsg, no); ... rt_memcpy(&listmsg->data, &tmpmsg, sizeof(struct rt_can_msg)); ``` Therefore, if the controller reports a raw DLC code 9..15, `recvmsg()` reads past `RxMessage.Data[8]` and writes past `tmpmsg.data[8]` before the corrupted stack object is copied into the RT-Thread RX FIFO. Locations: ```text bsp/loongson/ls1cdev/libraries/ls1c_can.h:137 bsp/loongson/ls1cdev/libraries/ls1c_can.h:171 bsp/loongson/ls1cdev/libraries/ls1c_can.h:182 bsp/loongson/ls1cdev/libraries/ls1c_can.h:183 bsp/loongson/ls1cdev/libraries/ls1c_can.c:419 bsp/loongson/ls1cdev/drivers/drv_can.c:28 bsp/loongson/ls1cdev/drivers/drv_can.c:374 bsp/loongson/ls1cdev/drivers/drv_can.c:378 bsp/loongson/ls1cdev/drivers/drv_can.c:388 bsp/loongson/ls1cdev/drivers/drv_can.c:391 bsp/loongson/ls1cdev/drivers/drv_can.c:393 bsp/loongson/ls1cdev/drivers/drv_can.c:420 bsp/loongson/ls1cdev/drivers/drv_can.c:423 bsp/loongson/ls1cdev/drivers/drv_can.c:460 bsp/loongson/ls1cdev/drivers/drv_can.c:463 components/drivers/can/dev_can.c:982 components/drivers/can/dev_can.c:998 components/drivers/can/dev_can.c:1039 components/drivers/include/drivers/dev_can.h:526 ``` ## Steps to reproduce I have not reproduced this on physical ls1cdev hardware yet. I verified the source-level bug with a standalone reduced check that preserves the relevant `recvmsg()` copy semantics. The source-level trigger is: ```text CANx->IDE_RTR_DLC low nibble = 9..15 RT_CAN_USING_CANFD = not enabled USING_BXCAN0 or USING_BXCAN1 = enabled ``` Reduced check: ```c #include <stdint.h> typedef unsigned char rt_uint8_t; typedef unsigned int rt_uint32_t; struct rt_can_msg { rt_uint32_t id; rt_uint8_t len; rt_uint8_t data[8]; }; typedef struct { unsigned long StdId; unsigned long ExtId; unsigned char IDE; unsigned char RTR; unsigned char DLC; unsigned char Data[8]; } CanRxMsg; static CanRxMsg RxMessage; static int recvmsg(void *buf) { struct rt_can_msg *pmsg = (struct rt_can_msg *)buf; int i; /* External input: raw DLC from the received CAN frame. */ pmsg->len = RxMessage.DLC; for (i = 0; i < RxMessage.DLC; i++) { /* OOB when RxMessage.DLC > 8 and i reaches 8. */ pmsg->data[i] = RxMessage.Data[i]; } return 0; } int main(void) { struct rt_can_msg msg = {0}; RxMessage.DLC = 15; /* external DLC value 15 */ recvmsg(&msg); return 0; } ``` Compile it with AddressSanitizer, for example: ```text clang -x c -fsanitize=address -O0 -g repro.c -o repro && ./repro ``` Changing `recvmsg()` to clamp or reject `RxMessage.DLC > 8` avoids the ASan report. ## Relevant log output AddressSanitizer reports a stack buffer overflow on the write into `struct rt_can_msg.data`: ```text ERROR: AddressSanitizer: stack-buffer-overflow WRITE of size 1 #0 recvmsg ... repro.c #1 main ... repro.c This frame has 1 object(s): [..] 'msg' <== Memory access overflows this variable SUMMARY: AddressSanitizer: stack-buffer-overflow in recvmsg ``` ## Impact Potential memory corruption from an externally supplied CAN frame on Loongson ls1cdev boards when a CAN channel is enabled. The attacker-controlled value is the raw DLC field observed by the CAN controller. If the controller reports DLC values 9..15 to software, the driver reads beyond the global `RxMessage.Data[8]` object and writes beyond the generic RT-Thread ISR stack message buffer. This path is reachable from the receive interrupt handlers for CAN0 and CAN1 when the corresponding board options are enabled. ## Environment ```text Initial RT-Thread commit checked: c39e92f4c1 Checked tree description: v5.0.2-2360-gc39e92f4c1-dirty Affected driver: bsp/loongson/ls1cdev/drivers/drv_can.c Affected low-level CAN code: bsp/loongson/ls1cdev/libraries/ls1c_can.c Affected board family: Loongson ls1cdev Target hardware: not tested on board yet Verification: host-side AddressSanitizer semantic check ``` The board Kconfig defines CAN0 and CAN1 channel options: ```text bsp/loongson/ls1cdev/Kconfig:79 config USING_BXCAN0 bsp/loongson/ls1cdev/Kconfig:84 config USING_BXCAN1 ``` The currently checked configuration has the CAN core enabled but disables the two channel options: ```text bsp/loongson/ls1cdev/.config:277 CONFIG_RT_USING_CAN=y bsp/loongson/ls1cdev/.config:279 # CONFIG_RT_CAN_USING_CANFD is not set bsp/loongson/ls1cdev/.config:1524 # CONFIG_USING_BXCAN0 is not set bsp/loongson/ls1cdev/.config:1525 # CONFIG_USING_BXCAN1 is not set bsp/loongson/ls1cdev/rtconfig.h:169 #define RT_USING_CAN ``` ## Additional context A fix should validate the DLC before it is stored as the RT-Thread message length or used as a copy bound. For classic CAN, the code should either reject/drop raw DLC values above 8 or clamp the payload length to 8: ```c rt_uint8_t len = RxMessage.DLC; if (len > 8) { len = 8; } pmsg->len = len; for (i = 0; i < len; i++) { pmsg->data[i] = RxMessage.Data[i]; } ``` The low-level receive code should preserve the same invariant if `DLC` is later exposed to other call sites.
원천⚠️ https://github.com/RT-Thread/rt-thread/issues/11424
사용자
 Zephyr Saxon (UID 80853)
제출2026. 06. 01. AM 08:38 (1 월 ago)
모더레이션2026. 07. 03. PM 03:51 (1 month later)
상태수락
VulDB 항목376113 [RT-Thread 까지 5.0.2 ls1c CAN ls1c_can.h recvmsg 메모리 손상]
포인트들20

Might our Artificial Intelligence support you?

Check our Alexa App!