CVE-2026-19575 Zephyr device_deinit 类型混淆漏洞
影响用户态线程可越权调用任意函数指针,实现提权或代码执行
Zephyr 内核中 device_deinit() 系统调用的用户态校验处理函数 z_vrfy_device_deinit() 使用 K_SYSCALL_OBJ_INIT(dev, K_OBJ_ANY) 校验参数,而 K_OBJ_ANY 会使 k_object_validate() 跳过对象类型比较,同时该宏也跳过初始化状态检查,导致校验形同虚设。攻击者因此可传入任意自己拥有权限的内核对象(如线程栈对象),其内存可被用户态写入,从而被当作 struct device 解析。
影响范围
受影响版本范围暂无公开信息,涉及 Zephyr 内核 kernel/device.c 中的 z_vrfy_device_deinit() 实现;同文件的 z_vrfy_device_init() 与 z_vrfy_device_is_ready() 使用 K_OBJ_DRIVER_ANY,不受影响。
漏洞详情
漏洞类型为类型混淆/校验绕过。成因是 K_OBJ_ANY 使对象类型比较被短路,且 K_SYSCALL_OBJ_INIT 不检查对象初始化状态,使校验退化为仅确认指针是调用线程被授权的某个内核对象基址。利用方式是用户态线程传入可写的线程栈对象,z_impl_device_deinit() 将其字节解释为 struct device,解引用其中的 state 指针并调用 ops.deinit 函数指针,成功时还会再次通过 state 写入,从而劫持控制流。
利用条件与风险
利用前提是攻击者能在用户态运行并持有某个可写内核对象(如通过 k_thread_stack_alloc() 获取的线程栈)的权限;实战中可导致内核态任意函数调用、权限提升或代码执行,风险较高。
修复建议
官方修复方案暂无公开信息,建议关注 Zephyr 官方安全公告并升级至修复版本;临时缓解措施包括限制不可信用户态线程的创建与对象授权,或在补丁可用前避免向不可信线程授予可写内核对象权限。
The user-mode verification handler for the device_deinit() system call, z_vrfy_device_deinit() in kernel/device.c, validated its dev argument with K_SYSCALL_OBJ_INIT(dev, K_OBJ_ANY). k_object_validate() short-circuits its type comparison when the requested type is K_OBJ_ANY, so the check reduced to “this pointer is the base address of some kernel object the calling thread has been granted” — the object’s actual type was never compared, and K_SYSCALL_OBJ_INIT also skips the initialization-state check. The sibling handlers z_vrfy_device_init() and z_vrfy_device_is_ready() already used K_OBJ_DRIVER_ANY and were unaffected.
A thread running in user mode can therefore pass any kernel object it holds permission on — most usefully a thread stack object obtained from the k_thread_stack_alloc() syscall or a statically defined K_THREAD_STACK it was granted in order to spawn a child user thread — whose backing memory is writable from user mode. z_impl_device_deinit() then interprets those attacker-written bytes as a struct device: it dereferences the state pointer read out of the object, calls the function pointer read out of ops.deinit, and on success writes through state again. The result is an indirect call to an arbitrary address executed in supervisor mode, plus an arbitrary kernel read and a single-byte kernel write.
Exploitation gives a local unprivileged thread full kernel code execution, defeating the CONFIG_USERSPACE isolation boundary entirely; a less precise attempt yields a supervisor-mode fault and a system crash. The defect is only reachable in builds that enable both CONFIG_USERSPACE and CONFIG_DEVICE_DEINIT_SUPPORT — with de-initialization support disabled, z_impl_device_deinit() returns -ENOTSUP without ever dereferencing the pointer. In v4.2.x and v4.3.x, CONFIG_DEVICE_DEINIT_SUPPORT defaulted to y, so every CONFIG_USERSPACE build of those releases is exposed unless the option was explicitly turned off. From v4.4.0 the option is opt-in (no default, and not selected by any in-tree subsystem), so a v4.4.x build is exposed only if it enables the option explicitly. The v4.2 line is no longer maintained and receives no backport.
The fix changes the object check to K_OBJ_DRIVER_ANY, which constrains the argument to the build-generated driver object type range (K_OBJ_DRIVER_FIRST..K_OBJ_DRIVER_LAST) — the real struct device instances placed by the linker — so the state and ops.deinit fields are once again kernel-controlled.