CVE-2026-87039: I2S configure syscall handler passes an unvalidated user-supplied kernel object pointer to drivers
The user-mode syscall verifier zvrfyi2sconfigure() in drivers/i2s/i2shandlers.c copies the caller's struct i2sconfig in from user memory and then, when cfg->frameclkfreq is 0 (the API's "unconfigure the stream" request), jumps straight to zimpli2sconfigure() without performing the KSYSCALLOBJ(config.memslab, KOBJMEMSLAB) ownership/type check or the blocksize sanity check. The memslab field is a struct kmemslab , 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 CONFIGUSERSPACE that has access to an I2S device object can trigger this with a single i2sconfigure() call. Most in-tree drivers special-case frameclkfreq == 0 and never store the caller's structure, but drivers/i2s/i2slitex.c does not: i2slitexconfigure() only logs a warning for a mismatched frame clock, then memcpy()s the caller's config into stream->cfg and marks the stream I2SSTATEREADY. A subsequent i2strigger() with I2STRIGGERSTART enables the FIFO interrupt, and the interrupt handlers i2slitexisrrx() / i2slitexisrtx() then call kmemslaballoc() and kmemslabfree() 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 — kmemslaballoc() takes a spinlock at slab->lock and reads and writes slab->freelist and the link word inside the returned "block" — which defeats the CONFIGUSERSPACE 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 CONFIGUSERSPACE are unaffected, since the verifier is not used.
The fix zeroes config.memslab and config.blocksize 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.
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Configuration
Zero config.mem_slab and config.block_size when cfg->frame_clk_freq is 0 before calling z_impl_i2s_configure(), preventing an unvalidated memory-slab pointer from reaching drivers.
I2S configure syscall handler config.mem_slab and config.block_size on the unconfigure path = 0
Event History
Frequently Asked Questions
Which deployments are realistically exposed?
Exposure requires CONFIG_USERSPACE and a user-mode thread that has access to an I2S device object. The described exploitable driver path is in the LiteX I2S driver, which retains the supplied configuration and later uses its mem_slab pointer from interrupt handlers.
What must an attacker be able to do?
An attacker needs only a user-mode thread with access to an I2S device object. They can issue i2s_configure() with frame_clk_freq set to 0 and a chosen mem_slab pointer, then use i2s_trigger() with I2S_TRIGGER_START to reach the LiteX interrupt-driven allocation and free operations.
Are all I2S drivers equally affected?
No. Most in-tree drivers reportedly special-case a frame clock frequency of 0 and do not retain the caller's configuration structure. The LiteX driver is specifically identified because it copies the configuration into stream state despite the unconfigure request.
How can I assess whether a system is exposed?
Check whether CONFIG_USERSPACE is enabled, whether untrusted user-mode threads can access an I2S device object, and whether the LiteX I2S driver is in use. The vulnerable behavior is present when the syscall verifier allows the frame_clk_freq == 0 path to bypass validation of config.mem_slab and block_size.