CVE-2026-19595 in Zephyr
Summary
by MITRE • 10/20/2026
The user-mode syscall verifier z_vrfy_rtio_cqe_get_mempool_buffer() in subsys/rtio/rtio_syscalls.c validated its two output parameters, buff and buff_len, with K_SYSCALL_MEMORY_READ() rather than K_SYSCALL_MEMORY_WRITE(). Both are pure out-parameters: z_impl_rtio_cqe_get_mempool_buffer() stores the mempool buffer address and its length through them. The distinction is enforced by every architecture validator — arch_buffer_validate() on RISC-V PMP explicitly accepts the global __rom_region/__rodata_region for reads only, and the ARM MPU validators accept P_RO_U_RO and P_RW_U_RO regions for reads only — so a read-validated pointer is not necessarily writable by the calling thread.
An unprivileged user-mode thread that has been granted access to an RTIO context (rtio_access_grant()) can invoke the rtio_cqe_get_mempool_buffer() syscall with buff/buff_len pointing at memory it may read but not write, and with a completion-queue entry whose flags field it fully controls (only RTIO_CQE_FLAG_MEMPOOL_BUFFER in flags[7:0] is required; flags[19:8] and flags[31:20] supply the block index and count). The kernel then performs a pointer-sized and a 4-byte store through those addresses while running in supervisor mode. Reaching the store requires CONFIG_USERSPACE together with CONFIG_RTIO_SYS_MEM_BLOCKS, the configuration behind RTIO_DEFINE_WITH_MEMPOOL; without the latter the implementation returns -ENOTSUP and writes nothing.
Two outcomes follow. If the target lies in a memory-domain partition declared K_MEM_PARTITION_P_RW_U_RO (privileged read-write, user read-only), the kernel-mode store succeeds and deposits partly attacker-chosen values into memory the memory domain had marked read-only for that thread, bypassing the partition's write protection. If the target lies in globally readable but unwritable memory such as .text/.rodata in flash, the store faults in kernel mode; the fault is delivered to z_fatal_error() and the default k_sys_fatal_error_handler() halts or reboots the device, giving an unprivileged thread a reliable full denial of service.
The read-permission check does bound 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. The fix replaces both checks 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.
If you want to get best quality of vulnerability data, you may have to visit VulDB.
Analysis
by VulDB Data Team • 10/20/2026
The vulnerability identified in the Zephyr real-time operating system involves a critical misconfiguration of syscall memory validation within the RTIO subsystem, specifically affecting the z_vrfy_rtio_cqe_get_mempool_buffer function located in subsys/rtio/rtio_syscalls.c. This flaw stems from an incorrect use of the K_SYSCALL_MEMORY_READ macro to validate two output parameters, buff and buff_len, which are intended solely for writing data back to user space. Because these pointers serve as pure out-parameters where the kernel implementation z_impl_rtio_cqe_get_mempool_buffer stores a mempool buffer address and its length, they must be validated with K_SYSCALL_MEMORY_WRITE. The distinction is enforced by architecture-specific validators such as arch_buffer_validate on RISC-V PMP systems, which explicitly restricts global read-only regions like __rom_region to read operations only, and ARM MPU validators that accept specific permission bits for reads but not writes. Consequently, a pointer validated merely for reading may reside in memory segments where the calling thread lacks write permissions, creating a security gap when the kernel subsequently attempts to write through these pointers while operating in supervisor mode.
An unprivileged user-mode thread can exploit this vulnerability if it has been granted access to an RTIO context via rtio_access_grant and operates within a configuration that includes CONFIG_USERSPACE and CONFIG_RTIO_SYS_MEM_BLOCKS, which is typically enabled by the RTIO_DEFINE_WITH_MEMPOOL macro. The attacker constructs a completion-queue entry where they fully control the flags field, specifically setting only the RTIO_CQE_FLAG_MEMPOOL_BUFFER bit in flags[7:0], while using flags[19:8] and flags[31:20] to supply block index and count values. By invoking rtio_cqe_get_mempool_buffer with buff or buff_len pointing at memory that is readable but not writable by the user thread, the kernel proceeds to perform pointer-sized and four-byte stores through these addresses in supervisor mode. This bypasses the intended write protection mechanisms because the initial validation only confirmed read access, allowing the kernel to interact with memory regions it should have rejected for writing purposes.
The operational impact of this vulnerability manifests in two distinct scenarios depending on the target memory region. If the target address lies within a memory-domain partition declared as K_MEM_PARTITION_P_RW_U_RO, meaning privileged read-write and user read-only, the kernel-mode store succeeds despite the user thread lacking write permissions. This allows an attacker to deposit partly controlled values into memory that was explicitly marked as read-only for their context, effectively bypassing domain-based access controls and compromising data integrity within those protected regions. Alternatively, if the target lies in globally readable but unwritable memory such as .text or .rodata sections in flash storage, the kernel-mode store triggers a fault. This fault is delivered to z_fatal_error(), invoking the default k_sys_fatal_error_handler which typically halts or reboots the device. In this scenario, an unprivileged thread achieves a reliable full denial of service against the entire system without requiring any escalation of privileges beyond what was already granted for RTIO access.
The scope of potential damage is partially bounded by the requirement that the target address must still be readable by the calling thread. This constraint prevents attackers from targeting kernel-private memory regions or disclosing confidential data, as they cannot write to addresses they do not have read permission for. However, this does not mitigate the risk of integrity violation within user-accessible domains or the denial-of-service vector via system crashes. The remediation involves replacing both K_SYSCALL_MEMORY_READ checks with K_SYSCALL_MEMORY_WRITE in the validation logic. This ensures that any thread lacking write permission is terminated by K_OOPS before the implementation code executes, thereby enforcing correct access control semantics. The cqe argument correctly remains read-validated to ensure safe inspection of completion queue entries while preventing unauthorized writes through output parameters.
From a classification perspective, this vulnerability aligns with CWE-284 Improper Access Control and CWE-755: Improper Handling of Exceptional Conditions in the context of memory validation failures. It also maps to MITRE ATT&CK techniques related to privilege escalation via improper access control mechanisms within operating system kernels. The incident highlights the critical importance of matching syscall parameter directionality with appropriate memory permission checks, particularly when dealing with output parameters that require write access by the kernel on behalf of user-space applications. Proper validation ensures that security boundaries defined by memory domains and MPU configurations are respected even during privileged operations initiated by untrusted userspace threads.