CVE-2026-82961: Unvalidated pin index in GPIO syscall handlers allows out-of-bounds kernel writes from user mode
The GPIO syscall handlers in drivers/gpio/gpiohandlers.c did not validate the pin argument supplied by a user-mode caller. zvrfygpiopinconfigure(), zvrfygpiopingetconfig() and zvrfygpiopininterruptconfigure() verified only the device object (KSYSCALLDRIVERGPIO() / KSYSCALLOBJ()) and then passed the caller-supplied gpiopint straight through to zimplgpiopin() and on to the driver. The only pin check in include/zephyr/drivers/gpio.h was ASSERT(gpioportpinissupported(cfg->portpinmask, pin), ...), which compiles to nothing unless CONFIGASSERT is enabled, so in a production build no check existed at all.
GPIO drivers use pin as a shift count and as an array index on the documented assumption that it is a pin the controller supports. On STM32, for example, gpiostm32pininterruptconfigure() turns pin into an EXTI line and stm32gpiointcsetirqcallback() writes data->cb[linenum].cb and data->cb[linenum].data into a NUMEXTILINES-entry array (default 16) in drivers/interruptcontroller/intcgpiostm32.c, guarded only by a compiled-out ASSERTNOMSG(); the same function then reads extiirqtable[linenum] and calls irqenable() on the result. Other controllers index fixed-size per-pin configuration tables the same way — drivers/gpio/gpioiteit8xxx2v2.c reads an MMIO base pointer out of wucbase[8] with the caller's pin and performs a read-modify-write through it.
The issue is reachable only in builds with CONFIGUSERSPACE=y (the file is compiled under that symbol), by a user-mode thread that has been granted access to a GPIO device object. Such a grant is intended to authorize control of that port's pins; the missing validation instead lets the thread drive a supervisor-mode out-of-bounds write of a function pointer and an associated void past a kernel .bss array, and out-of-bounds reads that feed irqenable() and MMIO accesses — an escape from the user-mode sandbox that can lead to kernel memory corruption, arbitrary code execution, or a crash.
The fix validates pin against the controller's gpiodriverconfig.portpinmask with gpioportpinissupported() in every pin-taking zvrfy handler and returns -EINVAL otherwise, complemented by an unconditional pin >= GPIOMAXPINSPERPORT range check added to the shared zimplgpiopin() inlines.
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Compensating control
Validate the pin argument in every pin-taking z_vrfy_ GPIO handler using gpio_port_pin_is_supported() against the controller's gpio_driver_config.port_pin_mask, returning -EINVAL when unsupported; also add an unconditional pin >= GPIO_MAX_PINS_PER_PORT check to the shared z_impl_gpio_pin_*() inlines.
Event History
Frequently Asked Questions
Who is exposed to this issue?
Systems that expose the affected GPIO syscalls to user-mode code are exposed, particularly production builds where CONFIG_ASSERT is not enabled. The caller must have permission to invoke the GPIO syscall on a valid GPIO device object.
What does an attacker need to exploit it?
An attacker needs local user-mode code execution with access to an affected GPIO device through the syscall object/driver permission checks. They can then supply an unsupported pin value to the affected GPIO configuration, configuration-query, or interrupt-configuration syscall handlers.
What is the impact on STM32-based systems?
On STM32, an invalid pin used for interrupt configuration can cause writes beyond the NUM_EXTI_LINES callback array, which defaults to 16 entries. The same path can also read an out-of-range interrupt table entry and pass it to irq_enable().
Does enabling assertions mitigate the issue?
CONFIG_ASSERT causes the GPIO pin-support assertion to be present, preventing the unchecked path described for production builds. Assertions are not enabled in the production-build scenario described, so this should not be treated as a substitute for applying the available fixes.
How can I identify potentially affected code paths?
Review whether untrusted user-mode code can call gpio_pin_configure, gpio_pin_get_config, or gpio_pin_interrupt_configure on GPIO devices it is authorized to use. GPIO interrupt configuration on STM32 is especially relevant because it uses the supplied pin as an EXTI line and callback-array index.