β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-1845

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_unregister is 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.

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1845 Β· 4 files
FileTypeDescriptionSize
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
VERDICT.md verdict source-confirmation analysis
↓ download 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

fixed
baseline reproduced→ patch + rebuild →patched clean

fix.diff compiled in batch kernel build rc=0 -Werror

fix.diff compiled in batch kernel build rc=0 -Werror
↓ fix.diffcombined build rc=0

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)