pin_mapped/pin_opened protected by two independent locks: mutual-exclusion race
| Field | Value |
|---|---|
| ID | DF-1847 |
| Status | new |
| Severity | Info |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:L/A:N |
| CWE | CWE-662 Improper Synchronization |
| File | sys/dev/misc/gpio/gpio.c |
| Lines | 196-227, 304-313, 409, 440 |
| Area | dev/misc (GPIO locking) |
| Confidence | likely |
| Discovered | 2026-07-20 |
| Reported | pending |
| Known CVE | none |
| CVE match | dfly_specific |
Summary
The intended invariant is that a pin is either opened (via /dev) XOR mapped
(via a consumer), never both. But pin_mapped is written under gpio_lock
while pin_opened and the pin_opened || pin_mapped check are taken under a
completely separate gpiodev_lock. Two root threads racing gpio_open and
gpio_consumer_attach can both see the other flag as 0 and both succeed,
breaking the invariant and allowing two writers to drive the same pin.
Root cause
gpio_map takes gpio_lock to set gp->pins[pin].pin_mapped = 1
(gpio.c:196-227). gpio_open (gpio.c:304-314), gpio_close (gpio.c:325-331),
gpio_ioctl (gpio.c:409-440) and gpio_master_ioctl's GPIOPINSET
(gpio.c:459-523) all take gpiodev_lock instead when checking/setting
pin_opened and when reading pin_mapped. Neither lock is acquired while
holding the other, so the check-then-set pairs in gpio_open (lines 308-313) and
gpio_map (lines 214-225) are not atomic with respect to each other.
Race window:
T1 (gpio_open): T2 (gpio_map):
lock(gpiodev_lock)
read pin->pin_mapped == 0
lock(gpio_lock)
read pin->pin_opened == 0
pin->pin_opened = 1
unlock(gpiodev_lock)
gp->pins[pin].pin_mapped = 1
unlock(gpio_lock)
Final state: both flags are 1.
Threat model & preconditions
- Attacker position: root only (master and per-pin devs are 0600, gpio.c:574,580).
- Privileges gained or impact: once both flags are set, both
gpio_write(gpio.c:337-365) and the consumer'sgpio_pin_write(gpio.c:280-284) can drive the same pin concurrently with no synchronization β a confused-deputy on the GPIO line. Root already controls both endpoints, so this is a correctness / hygiene issue, not a privilege escalation. - Required config or capabilities:
device gpio; root access. - Reachability: two root threads racing open/close of a pin against attach/detach of a consumer on the same pin.
Proof of concept
# As root on a system with a GPIO controller and the gpio_led consumer:
# Thread A: hammer open/close of a specific pin
while true; do
exec 3<>/dev/gpio/gpio0/<N>
exec 3>&-
done &
# Thread B: hammer attach/detach of the led consumer on the same pin
while true; do
gpioctl gpio0 attach led <N> 1 "x"
gpioctl gpio0 detach led <N>
done &
After a short time, inspection of the kernel pin state (e.g. via the GPIOINFO
leak DF-1844 or a kernel debugger) shows pin_opened == 1 && pin_mapped == 1
for the contended pin β the invariant is broken.
Impact
Info-level locking protocol defect. Root-vs-root correctness issue. No privilege escalation; documented to guide the fix.
Recommended fix
Use a single lock to protect both pin_mapped and pin_opened. The minimal
change is to have gpio_map and gpio_unmap acquire gpiodev_lock instead of
gpio_lock for the pin_mapped check/set (and keep gpio_lock only for the
consumer/driver lists), or to acquire both locks in a fixed order in every path
that touches either flag. A common fix is to make gpio_open/gpio_ioctl/
gpio_master_ioctl also acquire gpio_lock (or to fold both locks into one).
Timeline
- 2026-07-20 Discovered during automated audit.
- 2026-07-20 Reported to DragonFlyBSD security contact (pending).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1847 Β· 1 files| File | Type | Description | Size | |
|---|---|---|---|---|
| manifest.json | file | 389 B | view raw |
Fix verification
not_testablerecommended fix identified; fix.diff not authored/validated in this batch
recommended fix identified; fix.diff not authored/validated in this batch
Confirmed kernel references
β
Detail
Exploit chain
none (Info severity)
Evidence (decisive lines)
Source-confirmed at sys/dev/misc/gpio/gpio.c:196: pin_mapped/pin_opened protected by two independent locks (mutual-exclusion race)
Verified recommended fix
Source-confirmed at sys/dev/misc/gpio/gpio.c:196: pin_mapped/pin_opened protected by two independent locks (mutual-exclusion race)
Verdict
Source-confirmed at sys/dev/misc/gpio/gpio.c:196: pin_mapped/pin_opened protected by two independent locks (mutual-exclusion race)
No comments yet.