---
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'
---

## Overview

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 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.

On 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.

The 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().

The 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.

## Affected

- `zephyr >= 1.14.0 < 4.3.2`
- `zephyr >= 4.4.0 < 4.5.0`

## Remediation

Refer to the linked advisories for vendor-supplied fixes and affected version ranges.
