CVE-2026-87039 · Runtime
NO FIX YET · SEE WHAT TO DO
A user-mode thread calling the I2S configure API with a zero frame clock can pass an unchecked pointer to the kernel, which the LiteX I2S driver later uses as a memory slab. An attacker gets an arbitrary kernel read and write primitive, breaking the user-space isolation boundary.
first public Nov 4, 2026 · 1 source
Is this in your stack? Find out in 2 minutes.
Tell InfraStack what you run. It checks this one, and every next one, against your stack.
Ask from here. InfraStack checks the affected versions against your stack and answers in your inbox.
InfraStack checks the affected versions against your stack and answers in Slack, with the evidence and the fix.
Systems built on Zephyr with CONFIG_USERSPACE enabled that have an I2S device, especially those using the LiteX I2S driver or an out-of-tree I2S driver that retains configuration on unconfigure. Builds without CONFIG_USERSPACE are not affected because the verifier is not used.
Not affected if default-config, no-i2s-device, no-userspace
Who cares embedded, i2s, userspace, zephyr
CONFIG_USERSPACE and includes an I2S driver such as drivers/i2s/i2s_litex.c.struct i2s_config on the unconfigure path.The user-mode syscall verifier z_vrfy_i2s_configure() in drivers/i2s/i2s_handlers.c copies the caller's struct i2s_config in from user memory and then, when cfg->frame_clk_freq is 0 (the API's "unconfigure the stream" request), jumps straight to z_impl_i2s_configure() without performing the K_SYSCALL_OBJ(config.mem_slab, K_OBJ_MEM_SLAB) ownership/type check or the block_size sanity check. The mem_slab field is a struct k_mem_slab *, so on that path a completely unvalidated, user-chosen pointer reaches driver code that is entitled to trust it as a real kernel object.
Any thread running under CONFIG_USERSPACE that has access to an I2S device object can trigger this with a single i2s_configure() call. Most in-tree drivers special-case frame_clk_freq == 0 and never store the caller's structure, but drivers/i2s/i2s_litex.c does not: i2s_litex_configure() only logs a warning for a mismatched frame clock, then memcpy()s the caller's config into stream->cfg and marks the stream I2S_STATE_READY. A subsequent i2s_trigger() with I2S_TRIGGER_START enables the FIFO interrupt, and the interrupt handlers i2s_litex_isr_rx() / i2s_litex_isr_tx() then call k_mem_slab_alloc() and k_mem_slab_free() on the attacker-supplied pointer in supervisor context.
The result is an arbitrary-pointer read/write primitive executed by the kernel on behalf of an unprivileged thread — k_mem_slab_alloc() takes a spinlock at slab->lock and reads and writes slab->free_list and the link word inside the returned "block" — which defeats the CONFIG_USERSPACE isolation boundary entirely. The same missing validation is a hazard for out-of-tree I2S drivers that retain the configuration on the unconfigure path. Builds without CONFIG_USERSPACE are unaffected, since the verifier is not used.
The fix zeroes config.mem_slab and config.block_size on the unconfigure path so the driver can never receive an unvalidated pointer, while still allowing a stream to be unconfigured by a caller that does not own a memory slab, as the API documents.
This page as .md · .json · in your agent: get_signal("CVE-2026-87039") · CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H
NVD data is provided by NIST and is public domain. Posts quoted belong to their authors.