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 | 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-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 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: 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.