β¬’ 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 704 B ↓ raw
build.sh build-script N/A (source-only) 61 B view raw
run.sh run-script N/A (source-only) 87 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 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)