CVE-2026-15923 Overview
CVE-2026-15923 is an infinite loop vulnerability [CWE-835] in the Zephyr real-time operating system's Secure Digital Input Output (SDIO) subsystem. The flaw resides in sdio_io_rw_extended_helper() within subsys/sd/sdio.c. When an SDIO card advertises a max_blk_size value of zero in its Card Information Structure (CIS) FUNCE tuple, the byte-I/O transfer loop never makes progress and spins forever. The calling thread holds the per-card mutex func->card->lock for the duration, permanently denying service to the SDIO peripheral and any dependent subsystem such as Wi-Fi. Exploitation requires physical access to insert a crafted or malfunctioning SDIO card.
Critical Impact
A single malicious SDIO card can permanently hang the calling thread and lock out the SDIO peripheral until device reset, disrupting availability of Wi-Fi and other SDIO-dependent subsystems.
Affected Products
- Zephyr RTOS SDIO subsystem (subsys/sd/sdio.c)
- Boards exposing removable SDIO or combo card slots
- Downstream subsystems dependent on SDIO transport, including Wi-Fi drivers
Discovery Timeline
- 2026-09-14 - CVE-2026-15923 published to the National Vulnerability Database
- 2026-09-14 - Last updated in NVD database
Technical Details for CVE-2026-15923
Vulnerability Analysis
The defect exists in the final byte-I/O phase of sdio_io_rw_extended_helper(). After block-aligned transfers complete, the helper drains the remaining bytes using a while (remaining > 0) loop. Each iteration computes size = MIN(remaining, func->cis.max_blk_size) to determine how many bytes to move. When func->cis.max_blk_size equals zero, size becomes zero on every iteration. The value of remaining never decreases, and the loop runs indefinitely.
The loop is reachable from the public SDIO client API, including sdio_read_fifo(), sdio_write_fifo(), and the incrementing register read and write helpers. Every entry point acquires the per-card mutex func->card->lock before entering the loop. A thread that enters the loop with max_blk_size == 0 blocks forever without releasing the mutex. Any subsequent SDIO operation on the same card queues indefinitely, and dependent subsystems lose service until the device is reset.
Root Cause
The value func->cis.max_blk_size is decoded directly from the SDIO card's CIS FUNCE tuple in sdio_decode_cis() and is never validated. The subsystem trusts card-supplied metadata as an upper bound for byte transfers without rejecting a zero value. The MIN() computation produces zero when either operand is zero, which makes forward progress impossible in the byte-drain loop.
Attack Vector
The malicious value must originate from the SDIO card itself. On boards with a soldered SDIO peripheral, the value is not attacker-influenceable and the defect cannot be triggered. On boards exposing a removable SDIO or combo card slot, an attacker with physical access can insert a crafted or malfunctioning card advertising max_blk_size == 0. The first non-block-aligned transfer triggers the hang. There is no memory-safety, confidentiality, or integrity impact; only availability is affected.
}
}
/* Remaining data must be written using byte I/O */
+ if (func->cis.max_blk_size == 0U) {
+ /* A zero max_blk_size would make MIN(remaining, 0) == 0 and
+ * the loop below spin forever without making progress.
+ */
+ return -EIO;
+ }
while (remaining > 0) {
size = MIN(remaining, func->cis.max_blk_size);
// Source: https://github.com/zephyrproject-rtos/zephyr/commit/3b2f7aaee1f2ca52904c7e7f028951ba39456abd
The patch rejects a zero max_blk_size with -EIO before the loop is entered, preventing the infinite spin.
Detection Methods for CVE-2026-15923
Indicators of Compromise
- Threads blocked indefinitely inside sdio_io_rw_extended_helper() in subsys/sd/sdio.c.
- SDIO peripheral becoming unresponsive immediately after insertion of an unknown card.
- Wi-Fi or other SDIO-dependent subsystems failing to initialize or hanging after a card change event.
- Watchdog resets triggered on the thread that owns func->card->lock.
Detection Strategies
- Instrument sdio_decode_cis() to log CIS FUNCE tuple contents and flag any card reporting max_blk_size == 0.
- Add runtime assertions or metrics around the byte-I/O loop in sdio_io_rw_extended_helper() to detect loops exceeding an expected iteration count.
- Correlate SDIO card insertion events with subsequent thread hangs and mutex acquisition timeouts.
Monitoring Recommendations
- Enable watchdog and stack monitoring on threads that call the SDIO client API.
- Track mutex hold times on func->card->lock and alert when they exceed transfer-size expectations.
- Log firmware version and SDIO subsystem revision alongside device boot telemetry to identify unpatched deployments.
How to Mitigate CVE-2026-15923
Immediate Actions Required
- Apply the upstream Zephyr fix that returns -EIO when func->cis.max_blk_size is zero. See the Zephyr commit 3b2f7aa.
- Review the Zephyr Security Advisory GHSA-4pvm-wrcp-jjf5 for the affected version ranges and downstream backports.
- Inventory deployed devices to identify those with exposed removable SDIO or combo card slots.
Patch Information
The fix is committed to the Zephyr Project as 3b2f7aaee1f2ca52904c7e7f028951ba39456abd. It introduces an explicit check that returns -EIO when func->cis.max_blk_size == 0U before entering the byte-I/O loop in sdio_io_rw_extended_helper(). Integrators should rebuild firmware against a Zephyr release that includes this commit and redeploy to affected devices.
Workarounds
- Restrict physical access to devices that expose removable SDIO slots until firmware is patched.
- Where feasible, disable or physically block unused SDIO or combo card slots on production hardware.
- For high-assurance deployments, prefer boards with soldered SDIO peripherals where the CIS tuple cannot be attacker-supplied.
- Add a downstream validation shim in sdio_decode_cis() to clamp or reject a zero max_blk_size if upstream patching is delayed.
# Fetch and apply the upstream fix in a Zephyr workspace
cd zephyr
git fetch origin
git cherry-pick 3b2f7aaee1f2ca52904c7e7f028951ba39456abd
west build -b <your_board> <your_app> --pristine
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

