{"id":"CVE-2026-85035","title":"Use-after-free via dynamically allocated message queue retained by the CAN RX filter","summary":"The can_add_rx_filter_msgq() system call installs a CAN RX filter whose callback (can_msgq_put() in drivers/can/can_common.c) is handed the caller's struct k_msgq * as user_data. The controller driver stores that raw pointer in its filte…","severity":"high","cvss":8.8,"cvssVector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","cvssSource":"cna","cwe":["CWE-416"],"vendor":"zephyrproject","product":"zephyr","affected":["zephyr >= 1.14.0 < 4.3.2","zephyr >= 4.4.0 < 4.5.0"],"ssvc":{"exploitation":"none","automatable":"no","technicalImpact":"total","timestamp":"2026-11-03T18:13:02.862531Z"},"published":"2026-11-03","updated":"2026-11-03","sourceUpdated":"2026-11-03T18:13:21.688Z","source":"CVEORG","sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-85035","references":[{"url":"https://github.com/zephyrproject-rtos/zephyr/commit/0114bee266211c72a49f3b6a914d3eaf821dd460","label":"Fix commit"},{"url":"https://github.com/zephyrproject-rtos/zephyr/security/advisories/GHSA-6pxv-r7ww-3g2q","label":"GHSA-6pxv-r7ww-3g2q"}],"tags":["cve.org"],"epss":0.00228,"epssPercentile":0.13678,"ingestedAt":"2026-11-05T20:33:06.345Z","slug":"CVE-2026-85035","body":"## Overview\n\nThe can_add_rx_filter_msgq() system call installs a CAN RX filter whose callback (can_msgq_put() in drivers/can/can_common.c) is handed the caller's struct k_msgq * as user_data. The controller driver stores that raw pointer in its filter table and dereferences it from the RX path every time a matching frame arrives. The system call verifier z_vrfy_can_add_rx_filter_msgq() in drivers/can/can_handlers.c validated the queue only with K_SYSCALL_OBJ(), which is a point-in-time check of type, initialization and caller permission and says nothing about the object's lifetime.\n\nOn a build with CONFIG_USERSPACE=y and CONFIG_DYNAMIC_OBJECTS=y, an unprivileged user-mode thread that has been granted access to a CAN device can obtain a message queue whose lifetime it fully controls: k_object_alloc(K_OBJ_MSGQ) followed by k_msgq_alloc_init(). The lifetime of such an object is driven solely by its permission bitmask, and a pointer retained by the kernel is not represented there. After registering the filter, the thread calls k_object_release() — or simply terminates — and unref_check() frees the struct k_msgq storage while the CAN driver still holds the pointer.\n\nThe next matching CAN frame causes k_msgq_put() to run in supervisor context against freed memory that the attacker can have groomed with another kernel allocation: the frame payload is memcpy()d through the reused block's write_ptr, and the queue's wait_q (and poll_events under CONFIG_POLL) list nodes are manipulated. This yields kernel-heap corruption reachable from a user-mode thread, defeating the isolation CONFIG_USERSPACE is intended to provide; the same dangling pointer also arises benignly when an application thread exits without calling can_remove_rx_filter().\n\nThe fix validates the queue with K_SYSCALL_OBJ_STATIC(), which additionally rejects objects carrying K_OBJ_FLAG_ALLOC, so only statically defined queues (CAN_MSGQ_DEFINE()) may be registered from user mode. Supervisor-mode callers bypass the verifier and must themselves keep the queue valid until the filter is removed.\n\n## Affected\n\n- `zephyr >= 1.14.0 < 4.3.2`\n- `zephyr >= 4.4.0 < 4.5.0`\n\n## Remediation\n\nRefer to the linked advisories for vendor-supplied fixes and affected version ranges.","depth":"twilight","depthScore":48,"depthScoreParts":{"impact":48.4,"likelihood":0,"exploitation":0,"ransomware":0},"changes":[]}