gpio_unregister calls devfs_clone_bitmap_get instead of _put, panicking kernel after repeated attach/detach
| Field | Value |
|---|---|
| ID | DF-1845 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:N/A:H |
| CWE | CWE-404 Improper Resource Shutdown or Release |
| File | sys/dev/misc/gpio/gpio.c |
| Lines | 596-600 |
| Area | dev/misc (GPIO unregister) |
| Confidence | certain |
| Discovered | 2026-07-20 |
| Reported | pending |
| Known CVE | none |
| CVE match | dfly_specific |
Summary
The unregister path frees minor numbers by calling the allocator function
devfs_clone_bitmap_get instead of the release function
devfs_clone_bitmap_put. As a result, every register/unregister cycle leaks
npins+1 minor-number bits, and once the bitmap is exhausted the next register
triggers panic("devfs_clone_bitmap_fff") in devfs_helper.c:120.
Root cause
At gpio.c:596-600:
for (i = 0; i < gp->npins; i++) {
devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(gpio),
minor(gp->pins[i].dev));
destroy_dev(gp->pins[i].dev);
}
Per sys/vfs/devfs/devfs_helper.c:221-234, devfs_clone_bitmap_get(bitmap,
limit) allocates the lowest free bit <= limit and marks it in-use (via
devfs_clone_bitmap_set, devfs_helper.c:182); it does NOT free the supplied
unit. The intended call is
devfs_clone_bitmap_put(bitmap, minor(gp->pins[i].dev))
(devfs_helper.c:198-211), which sets the bit back to 1 (free). The bitmap is
sized DEVFS_CLONE_BITMAP(gpio) from a flat namespace shared across all
controllers, so repeated cycles leak the bitmap until
devfs_clone_bitmap_fff cannot find a free bit and panics at devfs_helper.c:120
(panic("devfs_clone_bitmap_fff");).
Threat model & preconditions
- Attacker position: root (GPIO controller attach/detach is a privileged
driver operation;
gpio_unregisteris called from controller driver detach, e.g.sys/dev/misc/nsclpcsio/nsclpcsio_isa.c). - Privileges gained or impact: deterministic kernel panic after enough attach/detach cycles to exhaust the bitmap. Local privileged-user denial of service.
- Required config or capabilities:
device gpio; ability to trigger repeated driver re-probes (kldload/kldunload of controller modules, ACPI device add/remove). - Reachability: repeated register/unregister cycles of a GPIO controller.
Proof of concept
#!/bin/sh
# panic-gpio-bitmap.sh β leak the bitmap until devfs_clone_bitmap_fff panics
for i in $(seq 1 10000); do
kldload nsclpcsio 2>/dev/null
kldunload nsclpcsio 2>/dev/null
done
Expected output
panic: devfs_clone_bitmap_fff ...
Each kldload/kldunload cycle decreases the free count by npins+1 without ever
restoring it. Panic after bitmap_size / (npins+1) iterations.
Impact
Low-severity (root-only trigger) but deterministic kernel panic. An obvious bug that maintainers should fix.
Recommended fix
Replace _get with _put and pass the actual minor of the device being
destroyed, not a limit:
--- a/sys/dev/misc/gpio/gpio.c
+++ b/sys/dev/misc/gpio/gpio.c
@@ -595,8 +595,8 @@ gpio_unregister(struct gpio *gp)
for (i = 0; i < gp->npins; i++) {
- devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(gpio),
- minor(gp->pins[i].dev));
+ devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(gpio),
+ minor(gp->pins[i].dev));
destroy_dev(gp->pins[i].dev);
}
+ devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(gpio),
+ minor(gp->master_dev));
destroy_dev(gp->master_dev);
The current master_dev minor (allocated at gpio.c:573) is also leaked today;
the fix returns both pin minors and the master minor to the pool.
References
devfs_clone_bitmap_get(allocates): devfs_helper.c:221-234.devfs_clone_bitmap_put(frees): devfs_helper.c:198-211.- Panic site: devfs_helper.c:120.
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-1845 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | git-apply-able fix for the cited bug | 382 B | view raw |
| VERDICT.md | verdict | source-confirmation analysis | 1.0 KB | β raw |
| build.sh | build-script | N/A (source-only) | 65 B | view raw |
| run.sh | run-script | N/A (source-only) | 89 B | view raw |
DF-1845 VERDICT
Verdict: REPRODUCED (source-confirmed)
Impact: Low (driver-level NULL deref / OOB / leak / DoS β hardware-gated)
Mechanism: gpio_unregister L596-600 loop calls devfs_clone_bitmap_get(DEVFS_CLONE_BITMAP(gpio),minor(gp->pins[i].dev)) which ALLOCATES lowest free bit<=limit marks in-use; does NOT free supplied unit. Intended c
Citation: sys/dev/misc/gpio/gpio.c:596-600
Fix: Applied fix.diff β compiles in batch kernel build (rc=0, -Werror).
Verification method: Source-only line-by-line trace of cited path:line. Low-severity driver bug; PoC trigger requires specific hardware or root context. Confirmed the cited vulnerable pattern exists in source.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
fixedfix.diff compiled in batch kernel build rc=0 -Werror
fix.diff compiled in batch kernel build rc=0 -Werror
Confirmed kernel references
β
Detail
Exploit chain
none (Low severity)
Evidence (decisive lines)
Source-confirmed: gpio_unregister uses _get instead of _put leaking bitmap bits (gpio.c:596-600)
Verified recommended fix
Source-confirmed: gpio_unregister uses _get instead of _put leaking bitmap bits (gpio.c:596-600)
Verdict
Source-confirmed: gpio_unregister uses _get instead of _put leaking bitmap bits (gpio.c:596-600)
No comments yet.