CVE-2026-19809 · Runtime
NO FIX YET · SEE WHAT TO DO
The Zephyr charger driver's user-mode syscall copies an uninitialized kernel-stack union back to the calling thread. An unprivileged thread with access to a charger device can read leftover supervisor-stack bytes, which may include kernel addresses.
first public Nov 12, 2026 · 1 source
Check it with your coding agent
It reads this page and tells you if your code or app is affected.
Only builds that enable user mode (CONFIG_USERSPACE=y) and grant user-mode threads access to a charger device object are exposed. The leak happens on every call that reaches the handler, whether the driver returns an error or success, so a tree with only the first commit still leaks on successful calls. Systems without a charger driver or without user-mode threads can skip it.
Not affected if no-charger-driver, no-user-threads-with-charger-access, no-userspace
Who cares charger-driver, embedded, user-mode, zephyr
CONFIG_USERSPACE=y and enables a charger driver.5054384e983c and commit c31cac727d02.5054384e983c and c31cac727d02; both are needed. Fixed release version: unknown.The user-mode system call verifier z_vrfy_charger_get_prop() in drivers/charger/charger_handlers.c declares an uninitialized union charger_propval k_val on the kernel (privileged) stack, passes it to the charger driver back end via z_impl_charger_get_prop(), and then copies the full sizeof(union charger_propval) back into the calling user thread's buffer with k_usermode_to_copy(). That discloses kernel stack contents on two paths:
On error. The copy-out ignores the driver's return code. In-tree back ends return an error without writing through the value pointer for properties they do not implement: charger_gpio_get_prop() returns -ENOTSUP unconditionally, and bq24190_get_prop() has a default: return -ENOTSUP; arm that never touches val. The whole union is then stack residue. On success. A back end writes only the one union member the requested property selects (for example sbs_charger.c writes the one-byte val->present for CHARGER_PROP_PRESENT and returns 0), yet the handler still copies the whole union, so every byte beyond that member is uninitialized.
The handler is compiled only when CONFIG_USERSPACE=y and is reachable by any user-mode thread that has been granted access to a charger device object. K_SYSCALL_DRIVER_CHARGER() validates only the device object and its API pointer, not the property identifier, so an unprivileged thread can choose prop freely: an unimplemented property forces the error path, a narrow one the success path, and the call can be repeated at will.
Each call discloses up to the size of the union (12 bytes on a 32-bit target, 16 bytes on 64-bit targets where the function-pointer members widen it) of supervisor-stack memory. The bytes are residue of earlier kernel-side frames on the thread's privileged stack and can contain kernel object addresses and saved return addresses, which an unprivileged thread cannot otherwise observe and which help defeat address-layout assumptions when exploiting a separate memory-safety defect. There is no integrity or availability impact.
Two commits fix it, and both are needed. 5054384e983c skips the copy-out when the back end reports an error. c31cac727d02 zeroes the union before the driver call, so the bytes copied out on success are driver-written data or zeros. A tree carrying only the first still leaks on every successful call.
Are you affected?
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.
InfraStack checks it against your stack and answers on your report, with the evidence and the fix.
This page as .md · .json · CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
NVD data is provided by NIST and is public domain. Posts quoted belong to their authors.