Skip to main content
Vulnerability Database/CVE-2026-19595

CVE-2026-19595: RTIO Syscall Verifier Privilege Escalation

CVE-2026-19595 is a privilege escalation flaw in Zephyr RTOS RTIO syscall verifier that allows unprivileged threads to write to protected memory or cause denial of service. This post covers technical details, affected configurations, and mitigation.

Published:

CVE-2026-19595 Overview

CVE-2026-19595 is an out-of-bounds write vulnerability [CWE-787] in the Zephyr RTOS user-mode syscall verifier z_vrfy_rtio_cqe_get_mempool_buffer() located in subsys/rtio/rtio_syscalls.c. The verifier validates its buff and buff_len output parameters using K_SYSCALL_MEMORY_READ() instead of K_SYSCALL_MEMORY_WRITE(). An unprivileged user-mode thread with access to an RTIO context can invoke the syscall with pointers to memory it may read but not write. The kernel then performs supervisor-mode stores through those addresses, either bypassing memory domain write protection or triggering a fatal kernel fault.

Critical Impact

An unprivileged user thread can force kernel-mode writes into memory marked read-only for its memory domain, or reliably halt the device by targeting .text or .rodata in flash.

Affected Products

  • Zephyr RTOS builds with CONFIG_USERSPACE enabled
  • Zephyr RTOS builds with CONFIG_RTIO_SYS_MEM_BLOCKS enabled (RTIO contexts created via RTIO_DEFINE_WITH_MEMPOOL)
  • Architectures using RISC-V PMP or ARM MPU memory protection validators

Discovery Timeline

  • 2026-10-19 - CVE-2026-19595 published to NVD
  • 2026-10-20 - Last updated in NVD database

Technical Details for CVE-2026-19595

Vulnerability Analysis

The z_vrfy_rtio_cqe_get_mempool_buffer() verifier treats two output parameters as if they were inputs. Both buff and buff_len are pure out-parameters: the implementation z_impl_rtio_cqe_get_mempool_buffer() stores the mempool buffer address and its length through them. By checking them with K_SYSCALL_MEMORY_READ(), the verifier only confirms the calling thread can read those addresses.

Architecture validators enforce the read/write distinction. arch_buffer_validate() on RISC-V PMP accepts the global __rom_region and __rodata_region for reads only. ARM MPU validators similarly accept P_RO_U_RO and P_RW_U_RO regions for reads only. A read-validated pointer is therefore not necessarily writable by the calling thread, yet the kernel proceeds to write through it.

Root Cause

The root cause is incorrect use of a syscall memory-access validator macro. The verifier used K_SYSCALL_MEMORY_READ() on parameters that the implementation later writes. This mismatch between declared and actual access intent allows read-only memory regions to pass validation and reach a supervisor-mode store.

Attack Vector

An unprivileged user-mode thread that has been granted access to an RTIO context through rtio_access_grant() invokes rtio_cqe_get_mempool_buffer(). It supplies buff and buff_len pointing at memory it may read but not write. It also supplies a completion-queue entry whose flags field it fully controls, with RTIO_CQE_FLAG_MEMPOOL_BUFFER set in flags[7:0], and flags[19:8] and flags[31:20] providing the block index and count.

Two outcomes follow. If the target lies in a K_MEM_PARTITION_P_RW_U_RO partition, the kernel store succeeds and deposits partly attacker-chosen values into memory the memory domain marked read-only for that thread. If the target lies in globally readable but unwritable memory such as .text or .rodata in flash, the store faults in kernel mode. The fault reaches z_fatal_error() and the default k_sys_fatal_error_handler() halts or reboots the device.

The read-permission check bounds the damage: the address must still be readable by the calling thread, so kernel-private memory cannot be targeted and no confidential data is disclosed.

c
 {
 	K_OOPS(K_SYSCALL_OBJ(r, K_OBJ_RTIO));
 	K_OOPS(K_SYSCALL_MEMORY_READ(cqe, sizeof(struct rtio_cqe)));
-	K_OOPS(K_SYSCALL_MEMORY_READ(buff, sizeof(void *)));
-	K_OOPS(K_SYSCALL_MEMORY_READ(buff_len, sizeof(uint32_t)));
+	K_OOPS(K_SYSCALL_MEMORY_WRITE(buff, sizeof(void *)));
+	K_OOPS(K_SYSCALL_MEMORY_WRITE(buff_len, sizeof(uint32_t)));
 	return z_impl_rtio_cqe_get_mempool_buffer(r, cqe, buff, buff_len);
 }
 #include <zephyr/syscalls/rtio_cqe_get_mempool_buffer_mrsh.c>

Source: Zephyr commit bea9340. The patch replaces both read validators with K_SYSCALL_MEMORY_WRITE(), so a thread lacking write permission is terminated by K_OOPS before the implementation runs. The cqe argument correctly remains read-validated.

Detection Methods for CVE-2026-19595

Indicators of Compromise

  • Unexpected invocations of k_sys_fatal_error_handler() from kernel-mode faults originating in z_impl_rtio_cqe_get_mempool_buffer().
  • Device halts or reboots correlated with user-mode threads holding RTIO access grants.
  • Unexplained modification of data in memory partitions declared K_MEM_PARTITION_P_RW_U_RO.

Detection Strategies

  • Audit Zephyr builds for the combination of CONFIG_USERSPACE=y and CONFIG_RTIO_SYS_MEM_BLOCKS=y, which is the precondition for exploitation.
  • Review all callers of rtio_access_grant() to enumerate user-mode threads capable of reaching the vulnerable syscall.
  • Inspect kernel panic logs for faults with program counters inside the RTIO mempool completion path.

Monitoring Recommendations

  • Instrument fatal error paths to capture faulting thread identity, address, and syscall number for post-incident analysis.
  • Track firmware image hashes across the fleet to confirm all devices run patched Zephyr releases.
  • Monitor device uptime and reboot patterns on fielded embedded targets for denial-of-service symptoms.

How to Mitigate CVE-2026-19595

Immediate Actions Required

  • Apply the upstream Zephyr patch that replaces K_SYSCALL_MEMORY_READ() with K_SYSCALL_MEMORY_WRITE() for the buff and buff_len parameters in z_vrfy_rtio_cqe_get_mempool_buffer().
  • Rebuild and reflash all affected device firmware images.
  • Review and minimize the set of user-mode threads granted RTIO access via rtio_access_grant().

Patch Information

The fix is published in Zephyr commit bea93400138362691403b7f81de1e9048681547d and documented in the GitHub Security Advisory GHSA-7337-mc44-5wjm. Both output parameters are now validated as writable, causing K_OOPS to terminate any calling thread lacking write permission before the implementation runs.

Workarounds

  • If patching is not immediately possible, disable CONFIG_RTIO_SYS_MEM_BLOCKS to make the vulnerable code path return -ENOTSUP without performing writes.
  • Revoke RTIO context access from untrusted user-mode threads where mempool-backed RTIO is not required.
  • Avoid declaring sensitive data in K_MEM_PARTITION_P_RW_U_RO partitions reachable from threads that invoke RTIO syscalls.
bash
# Disable the vulnerable configuration path in prj.conf
CONFIG_RTIO_SYS_MEM_BLOCKS=n

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.