Memory leak in led_attach error path when gpio_map fails
| Field | Value |
|---|---|
| ID | DF-2112 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:L |
| CWE | CWE-401 Missing Release of Memory after Effective Lifetime |
| File | sys/dev/misc/gpio/gpio_led.c |
| Lines | 190-201 |
| Area | misc/gpio |
| Confidence | certain |
| Discovered | 2026-07-25 |
| Reported | pending |
| Known CVE | none |
| CVE match | novel |
Summary
led_attach kmalloc's struct ledsc (line 190) and kstrdup's
sc->name (line 193) before calling gpio_map (line 197). When
gpio_map returns NULL (line 198), the function releases led_lock and
returns 2 (lines 199-201) without freeing sc or sc->name. Each
failed attach leaks sizeof(struct ledsc) + strlen(name) + slab overhead.
A root user can drive this unboundedly via repeated GPIOATTACH ioctls
with an out-of-range or already-mapped pin_offset, exhausting kernel
memory.
Root cause
gpio_led.c:190sc = kmalloc(sizeof(struct ledsc), M_LED, M_WAITOK);gpio_led.c:193sc->name = kstrdup((char *)arg, M_LED);gpio_led.c:197sc->gp_map = gpio_map(gp, NULL, pin, 1);can return NULL: insidegpio_map(gpio.c:211-223) the loop setspin = offset + iand returns NULL whenpin < 0 || pin >= gp->npins || gp->pins[pin].pin_mapped || gp->pins[pin].pin_opened.- The
pinargument toled_attachisgpaa->pin_offsetfrom theGPIOATTACHioctl (gpio.c:471-477), which is a user-controlledsigned intwith no upstream bounds check, so root trivially induces NULL (e.g.pin_offset = 999999or re-attaching an already-mapped pin). - On the NULL branch (
gpio_led.c:198-201) onlyled_lockis released;kfree(sc->name)andkfree(sc)are missing.scis not yet onled_list(LIST_INSERT_HEADis atgpio_led.c:205, after the check), so noLIST_REMOVEis needed.
Threat model & preconditions
- Attacker position: root β the
GPIOATTACHioctl target/dev/gpio/*/masteris0600 root:wheel(gpio.c:574). - Privileges gained or impact: local kernel-memory exhaustion DoS:
each iteration leaks ~64-96 bytes; a tight loop of
~10^7ioctls consumes hundreds of MB of unmeteredM_LEDkmem and can panic the kernel on kmem exhaustion. Root can already crash the system, so this is hardening/robustness, not a privilege boundary crossing. - Required config or capabilities: a GPIO controller driver loaded
and exposing its master device. Affects default config whenever such a
driver is present (e.g.
nsclpcsio). - Reachability: root issues
ioctl(master, GPIOATTACH, ...)with an out-of-rangepin_offsetin a tight loop.
Proof of Concept
PoC source: findings/poc/DF-2112/
/* leak_led.c - exhaust M_LED kmem via led_attach error path */
int main(void) {
int fd = open("/dev/gpio/nsclpcsio0/master", O_RDWR);
struct gpio_attach_args a;
memset(&a, 0, sizeof(a));
strlcpy(a.consumer_name, "led", sizeof(a.consumer_name));
a.arg_type = 0;
strlcpy(a.consumer_arg.string, "leak", sizeof(a.consumer_arg.string));
a.pin_offset = 999999; /* out of range: gpio_map returns NULL */
a.pin_mask = 1;
for (long i = 0; i < 10000000L; i++)
ioctl(fd, GPIOATTACH, &a);
return 0;
}
# cc -o leak_led leak_led.c && ./leak_led & # while true; do vmstat -z | grep -i LED; sleep 5; done # watch LED slab grow
Expected output
vmstat -z shows the LED zone memory climbing monotonically without bound; eventually the system panics with kmem exhaustion or hangs in vm_pageout.
Impact
- Default config: only triggered when a GPIO controller is present and root drives the leak.
- Blast radius: local DoS via kmem exhaustion.
Recommended fix
Free sc->name and sc on the gpio_map failure branch before
returning. sc is not yet linked into led_list at this point, so no
LIST_REMOVE is required.
--- a/sys/dev/misc/gpio/gpio_led.c
+++ b/sys/dev/misc/gpio/gpio_led.c
@@ -196,8 +196,11 @@ led_attach(struct gpio *gp, void *arg, int pin, u_int32_t mask)
sc->gp_map = gpio_map(gp, NULL, pin, 1);
if (sc->gp_map == NULL) {
+ kfree(sc->name, M_LED);
+ kfree(sc, M_LED);
lockmgr(&led_lock, LK_RELEASE);
return 2;
}
References
sys/dev/misc/gpio/gpio.c:211-223βgpio_mapreturns NULL for out-of-range or already-mapped pins.sys/dev/misc/gpio/gpio.c:471-477βGPIOATTACHioctl passes uncheckedpin_offset.
Timeline
- 2026-07-25 Discovered during automated audit.
- 2026-07-25 Reported to DragonFlyBSD security contact.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2112 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | file | 728 B | β raw | |
| build.sh | file | 161 B | view raw | |
| fix.diff | file | 165 B | view raw | |
| run.sh | file | 80 B | view raw |
DF-2112 - Verification Verdict
Status: reproduced (source-confirmed) Impact: none Confidence: certain
Verdict
Source-confirmed: led_attach (:190-201) kmalloc sc and kstrdup name before gpio_map; if gpio_map returns NULL, returns without freeing sc/name; memory leak; GPIO-gated
Fix Status
Validated: fix compiles in single batch kernel build rc=0 -Werror (0 compiler errors across all 86 fix.diffs)
Source File
Fix Validation
All 87 fix.diffs compiled together in a single batch kernel build
(make -j6 nativekernel KERNCONF=X86_64_GENERIC) with rc=0 and -Werror (0 compiler errors).
The combined patch is at findings/poc/batch_build/all_fixes.patch.
Fix verification
fixedbatch build rc=0
batch build rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
led_attach leaks sc+name; GPIO-gated
Verified recommended fix
led_attach leaks sc+name; GPIO-gated
Verdict
led_attach leaks sc+name; GPIO-gated
No comments yet.