atkbdattach() ignores IRQ resource allocation failure: NULL resource reaches nexus_setup_intr() which panics by design (boot-time kernel panic; BUS_SETUP_INTR result also unchecked, resource leaked)
Summary
No hint.atkbd.N.irq -> ivar -1 -> the rman range [(u_long)-1,(u_long)-1] fails -> NULL -> nexus panic 'NULL irq resource!'. GENERIC pins irq 1 so boot-config-gated; the psm sibling on the same bus does both checks. NULL-check + unwind fix (apply-verified).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5179 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.6 KB | β raw | |
| VERDICT.md | β | 2.7 KB | β raw | |
| trigger.txt | β | 1.3 KB | view raw | |
| fix.diff | β | 825 B | view raw | |
| build.sh | β | 148 B | view raw | |
| run.sh | β | 160 B | view raw | |
| manifest.json | β | 1.2 KB | view raw | |
| verdict.json | β | 2.8 KB | view raw |
DF-5179 β atkbd(4) ISA attach: unchecked IRQ resource allocation β boot-time kernel panic
- File:
sys/dev/misc/atkbd/atkbd_isa.c - Severity: Low (boot-time DoS; trigger requires privileged config / resource exhaustion)
- Confidence: certain (source-proven panic path)
- Verification: source-confirmed only β trigger is boot-time and gated on
kernel-config/loader hints (root-only) or ISA IRQ rman exhaustion; there is
no unprivileged runtime entry point to exercise in the QEMU guest, so no
guest run was attempted (
verdict.jsonstatusuntested, attempts 0).
Root cause
atkbdattach() (sys/dev/misc/atkbd/atkbd_isa.c:118-124) allocates the
legacy IRQ resource and hands it straight to BUS_SETUP_INTR() without any
NULL check:
rid = 0;
sc->intr = bus_alloc_legacy_irq_resource(dev, &rid, irq, RF_ACTIVE);
BUS_SETUP_INTR(device_get_parent(dev), dev, sc->intr, INTR_MPSAFE,
atkbd_isa_intr, kbd, &sc->ih, NULL, NULL);
Three things go wrong when the allocation fails:
sc->intr == NULLis forwarded down the bus tree (atkbdc βbus_genericβ isa β nexus) andnexus_setup_intr()panics on NULL by design βsys/platform/pc64/x86_64/nexus.c:505-506:
c
/* somebody tried to setup an irq that failed to allocate! */
if (irq == NULL)
panic("%s: NULL irq resource!", __func__);
A driver-caused NULL irq is supposed to be a driver bug caught before this point; atkbd_isa is exactly such an unchecked driver.
-
Even for the non-NULL late-failure path (
register_int()returns NULL βEINVAL), theBUS_SETUP_INTR()return value is ignored, the irq resource is leaked, andatkbdattach()stillreturn 0β the driver reports a healthy attach with no interrupt plumbed. -
atkbdresume()(atkbd_isa.c:141) later callsrman_get_start(sc->intr)which dereferences the resource pointer ((r)->r_start) β a second NULL-deref site if the attach bug is ever fixed by ignoring the error instead of failing the attach.
Trigger path
irq comes from the parent atkbdc ivar. When no hint.atkbd.N.irq hint
exists, atkbdc_add_device() initializes it to -1
(sys/dev/misc/atkbdc_layer/atkbdc_isa.c:221-224), which is passed as
(u_long)-1 to bus_alloc_legacy_irq_resource() β
BUS_ALLOC_RESOURCE(..., start = ULONG_MAX, end = ULONG_MAX, ...) β the ISA
IRQ rman cannot satisfy the range β NULL β panic above. IRQ rman
exhaustion/conflict at boot is an alternative route to the same NULL.
Default configs pin the irq (sys/config/X86_64_GENERIC:152:
device atkbd0 at atkbdc? irq 1), so the panic needs a custom kernel
config / /boot/loader.conf hints (root-only) or IRQ pressure β hence Low.
In-tree reference for the correct pattern
The sibling driver on the same parent does it right
(sys/dev/misc/psm/psm.c:2014-2022):
sc->intr = bus_alloc_legacy_irq_resource(dev, &rid, irq, RF_ACTIVE);
if (sc->intr == NULL) {
return (ENXIO);
}
error = BUS_SETUP_INTR(device_get_parent(dev), dev, sc->intr,
INTR_NOPOLL, psmintr, sc, &sc->ih, NULL, NULL);
if (error)
goto out;
Fix
fix.diff in this directory β NULL-check the allocation (return ENXIO),
check the BUS_SETUP_INTR() result, unwind the resource on failure.
Not applied to sys/ (audit tree is read-only); not guest-validated
(fix_status: not_testable β trigger is boot-config-gated).
Artifacts
VERDICT.mdβ this narrativetrigger.txtβ the privileged config that triggers the panicfix.diffβ the verified-by-inspection fixmanifest.json,verdict.json
DF-5179 β Verification Verdict
Status: untested (source-confirmed; boot-config-gated β no unprivileged or guest-runnable trigger exists) Impact: panic (boot-time kernel panic when triggered) Confidence: certain Attempts: 0 (no runtime attempt β see below)
Why the code path is proven without a guest run
Every link in the chain is visible in the tree:
atkbdattach()passes the parent ivarirqtobus_alloc_legacy_irq_resource()and forwards the unchecked result toBUS_SETUP_INTR()βsys/dev/misc/atkbd/atkbd_isa.c:119-122.- With no
hint.atkbd.N.irq, the parent initializes the ivar to-1(sys/dev/misc/atkbdc_layer/atkbdc_isa.c:221-224), which becomes(u_long)-1inBUS_ALLOC_RESOURCE(..., start=ULONG_MAX, end=ULONG_MAX, ...)(sys/kern/subr_bus.c:3303-3310) β an ISA IRQ rman range that can never be satisfied β returns NULL. BUS_SETUP_INTRwalks atkbdc (bus_generic) β isa (sys/bus/isa/x86_64/isa.c:143-149) β nexus, andnexus_setup_intr()executespanic("%s: NULL irq resource!", __func__)on the NULL resource βsys/platform/pc64/x86_64/nexus.c:505-506.- Secondary NULL-deref site:
atkbdresume()dereferencesrman_get_start(sc->intr)(sys/dev/misc/atkbd/atkbd_isa.c:141). - Sibling driver on the same bus checks both conditions
(
sys/dev/misc/psm/psm.c:2014-2022) β atkbd_isa.c is the outlier.
Why no guest run
The trigger is boot-time and requires either a kernel config/loader hint
without irq 1 (root-only file edits + reboot, risking an unbootable
console-less guest) or ISA IRQ rman exhaustion. There is no runtime syscall,
ioctl, or device node that reaches this path after boot completes, so a QEMU
PoC would be testing the guest's own boot configuration rather than an
attacker-reachable primitive. Marked untested rather than not_reproduced
because the proof is static, per the family precedent (DF-2177/DF-2178/DF-2385
were likewise source-confirmed).
Fix validation
fix.diff (in this directory) adds the NULL check (ENXIO) and the
BUS_SETUP_INTR error check with resource unwind. Verified to apply cleanly
(patch -p1 --dry-run OK against a pristine copy of the file). Kernel
build/boot validation not performed (Low severity, boot-gated trigger):
fix_status: not_testable.
Source File
sys/dev/misc/atkbd/atkbd_isa.c
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
not_testablefix.diff authored after source verification; verified to apply cleanly to a pristine copy (patch -p1 --dry-run OK). Kernel build/boot validation skipped: Low-severity boot-gated trigger would require editing the guest's loader config and rebooting into a possibly console-less system.
findings/poc/DF-5179/fix.diff (sha256 15fb54e0d7c0150cb4c53f7f843f8a8b3d7c4add384f5bf8e3d2bcebefafb019); dry-run apply check in VERDICT.md
Confirmed kernel references
Detail
Evidence (decisive lines)
findings/poc/DF-5179/VERDICT.md (static chain, 5 links); trigger.txt (privileged config); fix.diff (applies cleanly, patch -p1 --dry-run OK)
PoC changes
n/a (no seed PoC; trigger documented as privileged boot configuration in trigger.txt)
Verified recommended fix
Check bus_alloc_legacy_irq_resource() for NULL (return ENXIO) and the BUS_SETUP_INTR() result (release the resource and return the error), mirroring psm.c:2014-2022.
Verdict
Source-confirmed boot-time kernel panic: atkbdattach() (atkbd_isa.c:119-122) forwards the unchecked result of bus_alloc_legacy_irq_resource() to BUS_SETUP_INTR(); when the allocation fails (missing hint.atkbd.N.irq defaults the ivar to -1 per atkbdc_isa.c:221-224, making the rman range [ULONG_MAX,ULONG_MAX] unsatisfiable, or under ISA IRQ exhaustion) the NULL resource reaches nexus_setup_intr() which panics by design (nexus.c:505-506). The BUS_SETUP_INTR return value is also ignored and the resource leaks on late failure, and atkbdresume() would dereference rman_get_start(NULL) (atkbd_isa.c:141). The sibling driver psm.c:2014-2022 performs both checks. No guest run: the trigger is privileged boot configuration (kernel config/loader hints) with no post-boot entry point, so no attacker-reachable runtime PoC exists; static proof chain is complete.
No comments yet.