{"id":"CVE-2026-87038","kind":"vulnerability","aliases":["CVE-2026-87038"],"related":[],"title":"Zephyr: I3C syscall race lets a user thread run kernel code","description":"The user-mode verification handler for the i3c_transfer() system call, z_vrfy_i3c_transfer() in drivers/i3c/i3c_handlers.c, validated the caller-supplied struct i3c_device_desc in place instead of snapshotting it. It confirmed only that the descriptor was readable by the calling thread (K_SYSCALL_MEMORY_READ()), read target->bus once to check it with K_SYSCALL_OBJ(), and then passed the caller's live pointer down. The implementation z_impl_i3c_transfer() re-reads target->bus from that user memory and dispatches through it as DEVICE_API_GET(i3c, target->bus)->i3c_xfers(...), so the bus that was validated is not necessarily the bus that is used. DEVICE_API_GET() performs no runtime type check outside CONFIG_DEVICE_API_ASSERT builds.\n\nOn a build with CONFIG_USERSPACE and an I3C controller driver, a user thread that has been granted an I3C device object can exploit this with a second thread in the same memory domain: one thread calls i3c_transfer() while the other rewrites the bus field of the descriptor. The window between the check and the use spans the num_msgs validation, the message-array access check, a stack VLA allocation and a memcpy() of up to 32 struct i3c_msg, so the race is repeatable and can simply be retried until it lands.\n\nWhen the substitution succeeds, the kernel dereferences an attacker-chosen pointer as a struct device, loads a function pointer from the attacker-controlled api table and calls it in supervisor mode with attacker-influenced arguments. This yields kernel-mode code execution from an unprivileged user thread — a complete escape of the userspace memory-protection sandbox; lesser outcomes are an arbitrary two-level kernel read or a kernel fault. Builds without CONFIG_USERSPACE, or without an I3C driver, are unaffected.\n\nThe fix copies the descriptor into a kernel-stack local with k_usermode_from_copy(), validates the bus in that copy, and dispatches on the copy, so the validated value is provably the value used.","summary":"The Zephyr I3C transfer system call checks the caller-supplied device descriptor in user memory, then reads the bus field again when it is used, so a second thread can swap it in between. A user thread can then make the kernel call an attacker-chosen function table in supervisor mode, giving kernel-mode code execution.","severity":"high","cvss":7.0,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"zephyr","fixed":["4.3.2","4.5.0"],"affected":">= 3.2.0, < 4.3.2; >= 4.4.0, < 4.5.0","ecosystem":"vendor"}],"ecosystems":[],"tags":["embedded","i3c","runtime","userspace","zephyr"],"not_affected_if":["no-i3c-driver","no-userspace"],"how_to_check":"- Run your Zephyr version (west list zephyr, or the VERSION file): affected if >= 3.2.0 and < 4.3.2, or >= 4.4.0 and < 4.5.0.\n- Check the build config for CONFIG_USERSPACE=y and an I3C controller driver (CONFIG_I3C); without either you are unaffected.\n- In drivers/i3c/i3c_handlers.c, z_vrfy_i3c_transfer() is fixed if it copies the descriptor with k_usermode_from_copy(); it is vulnerable if it only calls K_SYSCALL_MEMORY_READ(target) and K_SYSCALL_OBJ(target->bus).","fix":"Upgrade to Zephyr 4.3.2 (3.2.0–4.3.1 line) or 4.5.0 (4.4.x line). Backports to v3.7-branch, v4.3-branch and v4.4-branch are merged, but the advisory lists no 3.7.x or 4.4.x patched release; on those lines, build from the branch or cherry-pick commit 550437bc9efd.","status":"open","published_at":"2026-11-04T06:17:06+00:00","first_seen_at":"2026-11-04T06:25:20+00:00","triaged_at":"2026-11-04T06:26:01+00:00","updated_at":"2026-11-04T06:26:41+00:00","category":"runtime","confidence":0.85,"triage":{"at":"2026-11-04T06:26:01Z","model":"claude-haiku-5-5","output":{"fix":"- unknown","reach":"local","title":"Zephyr: I3C syscall race lets a user thread run kernel code","impact":"high","summary":"The Zephyr I3C transfer system call checks the caller-supplied device descriptor in user memory, then reads the bus field again when it is used, so a second thread can swap it in between. A user thread can then make the kernel call an attacker-chosen function table in supervisor mode, giving kernel-mode code execution.","audience":"developers","category":"runtime","nicknames":[],"who_cares":["zephyr","embedded","i3c","userspace"],"confidence":0.7,"fix_status":"unknown","needs_auth":"yes","worst_case":"Kernel-mode code execution from an unprivileged user thread","analyst_note":"Fixed version is not stated in the sources; check the linked patch and advisory for the Zephyr release ranges.","how_to_check":"- Check whether the build sets `CONFIG_USERSPACE` and includes an I3C controller driver; builds without either are unaffected per the description.\n- Look at `drivers/i3c/i3c_handlers.c` for `z_vrfy_i3c_transfer()` and see whether it copies the descriptor with `k_usermode_from_copy()`.\n- Check the Zephyr version in use against the fixed release in the patch link.","plain_action":"","needs_analyst":true,"not_affected_if":["no-userspace","no-i3c-driver"],"who_is_affected":"Only builds with CONFIG_USERSPACE enabled and an I3C controller driver are exposed, and the attacker needs a user thread that has been granted an I3C device object plus a second thread in the same memory domain. Builds without userspace or without an I3C driver are not affected.","user_interaction":"no","needs_special_setup":"yes","severity_if_unknown":"unknown"},"cost_usd":0.000793,"input_tokens":4768,"output_tokens":633,"severity_before":"high"},"analyst_status":"done","analyst_note":"Fixed version is not stated in the sources; check the linked patch and advisory for the Zephyr release ranges.","search":"'-2026':13A,16A '-87038':14A,17A '32':286C 'access':274C 'alloc':279C 'api':173C,195C,205C,328C 'arbitrari':367C 'argument':340C 'array':273C 'assert':206C 'attack':70B,312C,326C,338C 'attacker-chosen':69B,311C 'attacker-control':325C 'attacker-influenc':337C 'build':207C,210C,377C 'bus':43B,134C,162C,177C,182C,190C,252C,406C 'call':29B,67B,94C,126C,244C,331C 'caller':33B,104C,147C 'caller-suppli':32B,103C 'check':30B,137C,201C,261C,275C 'chosen':71B,313C 'code':11A,81B,346C 'complet':354C 'config':203C,212C,379C 'confirm':117C 'control':217C,327C 'copi':390C,403C,409C,414C 'cve':12A,15A 'derefer':309C 'desc':109C 'descriptor':36B,121C,256C,392C 'devic':35B,108C,172C,194C,204C,228C,318C 'dispatch':168C,411C 'domain':241C 'driver':218C,385C 'drivers/i3c/i3c_handlers.c':100C 'embed':19B 'escap':355C 'execut':82B,347C 'exploit':231C 'fault':376C 'field':44B,253C 'fix':389C 'function':72B,321C 'get':174C,196C 'give':77B 'grant':225C 'handler':88C 'i3c':2A,20B,26B,91C,97C,107C,156C,175C,178C,216C,227C,245C,288C,384C 'impl':155C 'implement':153C 'influenc':339C 'instead':112C 'k':128C,140C,400C 'kernel':10A,66B,79B,308C,344C,371C,375C,396C 'kernel-mod':78B,343C 'kernel-stack':395C 'land':302C 'lesser':363C 'let':5A 'level':370C 'live':149C 'load':319C 'local':398C 'make':64B 'memcpi':282C 'memori':39B,130C,166C,240C,360C 'memory-protect':359C 'messag':272C 'message-array':271C 'mode':76B,80B,86C,335C,345C 'msg':289C 'msgs':268C 'necessarili':188C 'num':267C 'obj':142C 'object':229C 'one':242C 'outcom':364C 'outsid':202C 'pass':145C 'perform':197C 'place':111C 'pointer':150C,314C,322C 'protect':361C 'provabl':420C 'race':4A,292C 're':159C 're-read':158C 'read':41B,131C,132C,160C,372C 'readabl':123C 'repeat':294C 'retri':299C 'rewrit':250C 'run':9A 'runtim':21B,199C 'sandbox':362C 'second':52B,235C 'simpli':297C 'snapshot':114C 'span':265C 'stack':277C,397C 'struct':106C,287C,317C 'substitut':305C 'succeed':306C 'supervisor':75B,334C 'suppli':34B,105C 'swap':55B 'syscal':3A,129C,141C 'system':28B,93C 'tabl':73B,329C 'target':133C,161C,176C 'thread':8A,53B,61B,127C,221C,236C,243C,352C 'transfer':27B,92C,98C,157C,246C 'two':369C 'two-level':368C 'type':200C 'unaffect':387C 'unprivileg':350C 'use':49B,193C,264C,423C 'user':7A,38B,60B,85C,165C,220C,351C 'user-mod':84C 'usermod':401C 'userspac':22B,213C,358C,380C 'valid':101C,185C,269C,404C,417C 'valu':418C,422C 'verif':87C 'vla':278C 'vrfi':96C 'window':258C 'without':378C,382C 'xfer':179C 'yield':342C 'z':95C,154C 'zephyr':1A,18A,23B,25B","nicknames":[],"mitigations":[],"who_affected":"Zephyr 3.2.0 to 4.3.1 and 4.4.x builds with CONFIG_USERSPACE and an I3C controller driver. The attacker needs a user thread granted an I3C device object plus a second thread in the same memory domain. Builds without userspace or without an I3C driver can skip it.","tier":"B","verified":{"fix":{"at":"2026-11-04T06:26:41Z","by":"analyst","doc":"https://github.com/zephyrproject-rtos/zephyr/commit/550437bc9efd7b733f13b9be73e77fd98858c8a2","run_id":1638},"products":{"at":"2026-11-04T06:26:41Z","by":"analyst","doc":"https://github.com/zephyrproject-rtos/zephyr/commit/550437bc9efd7b733f13b9be73e77fd98858c8a2","run_id":1638},"how_to_check":{"at":"2026-11-04T06:26:41Z","by":"analyst","doc":"https://github.com/zephyrproject-rtos/zephyr/commit/550437bc9efd7b733f13b9be73e77fd98858c8a2","run_id":1638},"who_affected":{"at":"2026-11-04T06:26:41Z","by":"analyst","doc":"https://github.com/zephyrproject-rtos/zephyr/commit/550437bc9efd7b733f13b9be73e77fd98858c8a2","run_id":1638}},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["zephyr"],"ptokens":["zephyr"],"labels":["embedded","high","i3c","runtime","userspace","zephyr"],"campaign_type":null,"first_public_at":"2026-11-04T05:18:07+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H","cvss_by":"vulnerabilities@zephyrproject.org","poc_count":0,"sources":[{"source":"nvd","external_id":"CVE-2026-87038","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-87038","severity":null,"published_at":"2026-11-04T06:17:06+00:00","modified_at":"2026-11-04T06:17:06+00:00","first_seen_at":"2026-11-04T06:25:20+00:00","fetched_at":"2026-11-04T06:25:20+00:00","note":null},{"source":"analyst","external_id":"CVE-2026-87038:1638","url":null,"severity":null,"published_at":"2026-11-04T06:26:41+00:00","modified_at":"2026-11-04T06:26:41+00:00","first_seen_at":"2026-11-04T06:26:41+00:00","fetched_at":"2026-11-04T06:26:41+00:00","note":"Read Zephyr GHSA-86xj-2q6x-h82w and fix commit 550437bc. Affected >=3.2.0,<4.3.2 and >=4.4.0,<4.5.0; patched 4.3.2, 4.5.0. No exploitation or PoC seen; local, high complexity (CVSS 7.0). Did not search social posts."}],"documents":[{"id":64654,"url":"https://github.com/zephyrproject-rtos/zephyr/commit/550437bc9efd7b733f13b9be73e77fd98858c8a2","title":"Patch: github.com","kind":"other","source":"nvd","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":64655,"url":"https://github.com/zephyrproject-rtos/zephyr/security/advisories/GHSA-86xj-2q6x-h82w","title":"Vendor advisory: github.com","kind":"vendor","source":"nvd","publisher":"vendor","text":null,"published_at":"2026-11-04T05:18:07+00:00","relation":"advisory","confidence":null,"note":"Vendor advisory: github.com"}],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}