ichwd_attach fail path dereferences ZERO_LENGTH_PTR softc, panicking on attach failure
| Field | Value |
|---|---|
| ID | DF-1831 |
| 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-822 Untrusted Pointer Dereference |
| File | sys/dev/misc/ichwd/ichwd.c |
| Lines | 603-604 |
| Area | dev/misc (watchdog driver attach) |
| Confidence | certain |
| Discovered | 2026-07-20 |
| Reported | pending |
| Known CVE | none |
| CVE match | dfly_specific |
Summary
ichwd_attach() uses a file-scope static softc (sc = &ichwd_sc; at ichwd.c:524)
because the driver table declares softc size 0 (ichwd.c:661-665). The error path
at ichwd.c:603-604 reassigns sc = device_get_softc(dev), which for a size-0
driver returns the sentinel ZERO_LENGTH_PTR = (void*)-8 from kmalloc(0)
(kern_slaballoc.c:888-891). The very next read (sc->tco_res at ichwd.c:605)
wraps to address 0x30 in the unmapped NULL guard page and the kernel takes an
unconditional page fault β panic. The resources actually allocated into
&ichwd_sc are leaked. Any of several realistic attach-failure conditions
(missing PMBASE, I/O range busy, NO_REBOOT bit hardwired by BIOS) triggers this.
Root cause
ichwd.c:234 declares static struct ichwd_softc ichwd_sc; as a file-scope
global because the DragonFlyBSD port dropped the dynamic softc but kept the
FreeBSD-style device_get_softc(dev) call in the error path. ichwd_attach()
correctly sets the working pointer at line 524:
sc = &ichwd_sc;
The driver table at ichwd.c:661-665 sets softc size to 0:
static driver_t ichwd_driver = {
"ichwd",
ichwd_methods,
0
};
Therefore subr_bus.c:1952-1954 executes
dev->softc = kmalloc(0, M_BUS, M_INTWAIT | M_ZERO), and
kern_slaballoc.c:888-891 returns the sentinel ZERO_LENGTH_PTR = (void*)-8
for any size==0 allocation. device_get_softc() (subr_bus.c:1782-1785)
returns dev->softc unchanged, so it hands back (void*)-8.
In the fail path (ichwd.c:603-616):
fail:
sc = device_get_softc(dev); /* sc = (struct ichwd_softc*)-8 */
if (sc->tco_res != NULL) /* reads addr 0x30 -> page fault */
bus_release_resource(dev, SYS_RES_IOPORT,
sc->tco_rid, sc->tco_res);
if (sc->smi_res != NULL) /* reads addr 0x20 */
bus_release_resource(dev, SYS_RES_IOPORT,
sc->smi_rid, sc->smi_res);
if (sc->gcs_res != NULL) /* reads addr 0x40 */
bus_release_resource(ich, SYS_RES_MEMORY,
sc->gcs_rid, sc->gcs_res);
With amd64 struct layout (device_t=intptr_t): offsets are device@0, ich@8,
ich_version@16, active@20, timeout@24, smi_enabled@28, smi_rid@32,
smi_res@40, tco_rid@48, tco_res@56, gcs_rid@64, gcs_res@72. Adding these
offsets to (u_long)-8 wraps modulo 2^64 into addresses 0x00..0x40, all
inside the unmapped NULL page (0..PAGE_SIZE-1). The first execution of
sc->tco_res at line 605 reads address 0x30 and the kernel takes a page
fault in supervisor mode β panic.
Reachability of goto fail:
- ichwd.c:528-531 (no ICH LPC bridge)
- ichwd.c:537-540 (PMBASE==0)
- ichwd.c:547-549 (smi_res alloc fails)
- ichwd.c:556-558 (tco_res alloc fails)
- ichwd.c:565-567 (gcs_res alloc fails for ICH6+)
- ichwd.c:571-572 (ichwd_clear_noreboot returns EIO because NO_REBOOT is
hardwired by BIOS β common on server boards; driver itself prints
"ICH WDT present but disabled in BIOS or hardware" at ichwd.c:419-420)
Every one of these branches trips the panic; the failure is not limited to the late resource-allocated cases.
Threat model & preconditions
- Attacker position: requires local administrator capable of loading
the module, OR a system builder/BIOS that has
device ichwdcompiled in or auto-loaded at boot. The bug fires during bus attach, not from a user syscall. - Privileges gained or impact: unconditional kernel panic at module load / boot (denial of service). On systems where ichwd is built in or auto-loaded, the machine cannot boot.
- Required config or capabilities:
device ichwd(sys/conf/files:2121 marks it optional), Intel ICH LPC bridge present (any of ~160 PCI IDs in ichwd.c:74-229), plus any of the realistic failure preconditions listed above. - Reachability: bus attach sequence for the LPC bridge child device
created by
ichwd_identify(ichwd.c:246).
Proof of concept
No userspace PoC is meaningful β the bug is in bus attach. The trigger is
simply running kldload ichwd (or booting) on a system matching the
preconditions.
Build & run
# In a DFly kernel build with `device ichwd` enabled: kldload ichwd # or boot a kernel that auto-loads ichwd on a system whose BIOS has set # the NO_REBOOT latch, or whose PMBASE is unset.
Expected output
ichwd0: <watchdog timer on ICH6 or later> port 0x..-0x.. irq 9 on acpi0 ichwd0: ICH WDT present but disabled in BIOS or hardware. Fatal trap 12: page fault while in kernel mode fault virtual address = 0x30 fault code = supervisor read, data not present instruction pointer = 0x..:0x..<ichwd_attach+...> ... panic: from debugger
Impact
Driver load or kernel boot panics on any system that has the Intel ICH LPC bridge and any of several realistic attach-failure preconditions. No confidentiality or integrity impact; pure availability (DoS). Most practically affects embedded/server boards whose BIOS hardwires NO_REBOOT, and development boxes where the TCO I/O range is already claimed by another driver.
Recommended fix
The local variable sc is already correctly initialized to &ichwd_sc
at line 524. Remove the incorrect device_get_softc(dev) reassignment
in the fail path so the fail block reads the real resource pointers. The
cleaner long-term fix additionally sets each *_res field to NULL after
its bus_release_resource to make the path idempotent, and gates the GCS
release on sc->ich_version >= 6 (matching the allocation guard at
ichwd.c:562).
--- a/sys/dev/misc/ichwd/ichwd.c
+++ b/sys/dev/misc/ichwd/ichwd.c
@@ -601,7 +601,6 @@ ichwd_attach(device_t dev)
return (0);
fail:
- sc = device_get_softc(dev);
if (sc->tco_res != NULL)
bus_release_resource(dev, SYS_RES_IOPORT,
sc->tco_rid, sc->tco_res);
References
- DFly
kmalloc(0)returningZERO_LENGTH_PTR = (void*)-8: kern_slaballoc.c:888-891. - subr_bus.c:1952-1954
dev->softc = kmalloc(0, ...)for size-0 drivers. - FreeBSD counterpart uses a dynamically allocated softc and never hits this path; the bug was introduced during the DFly port that replaced the dynamic softc with the static global.
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-1831 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | git-apply-able fix for the cited bug | 315 B | view raw |
| VERDICT.md | verdict | source-confirmation analysis | 706 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 |
DF-1831 VERDICT
Verdict: REPRODUCED (source-confirmed)
Impact: Low (driver-level NULL deref / OOB / leak / DoS β hardware-gated)
Mechanism: ichwd_attach uses static global softc sc=&ichwd_sc but fail path reassigns sc=device_get_softc(dev) which for size-0 driver returns ZERO_LENGTH_PTR=(void*)-8 from kmalloc(0); first read sc->tco_res wr
Citation: sys/dev/misc/ichwd/ichwd.c:603-604
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
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: fail path sc=device_get_softc(dev) returns ZERO_LENGTH_PTR (ichwd.c:603-604)
Verified recommended fix
Source-confirmed: fail path sc=device_get_softc(dev) returns ZERO_LENGTH_PTR (ichwd.c:603-604)
Verdict
Source-confirmed: fail path sc=device_get_softc(dev) returns ZERO_LENGTH_PTR (ichwd.c:603-604)
No comments yet.