isavga_attach() discards both bus_alloc_resource() returns and never unwinds the legacy VGA window claims on attach failure β permanent orphaned rman claims (I/O 0x3c0-0x3df, MEM 0xa0000-0xbffff) on VGA-less BIOS boots, plus leaked cdev under FB_INSTALL_CDEV
Summary
Both legacy-window claims are activated with discarded returns and the attach returns early on vga_attach_unit failure without releasing anything; no detach/shutdown exists at all, so failed-attach orphans the claims permanently. The failure branch is the EXPECTED outcome on VGA-less BIOS hardware (vga_probe returns 0 even after verify_adapter failed) - zombie claims block later kldloaded display drivers with EBUSY (narrow resource-starvation DoS); with FB_INSTALL_CDEV the cdev leaks too. Live baseline captured (devinfo shows both claims); failure path proven by line trace (rig's Bochs VGA always passes). DF-5467 ROM-teardown class ABSENT (nothing mapped, no detach); DF-5469 rid-aliasing ABSENT (fixed rid 0). check+release+destroy_dev fix proposed.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5590 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.3 KB | β raw | |
| VERDICT.md | β | 4.7 KB | β raw | |
| introspect.sh | β | 722 B | view raw | |
| build.sh | β | 105 B | view raw | |
| run.sh | β | 89 B | view raw | |
| run.log | β | 731 B | view raw | |
| env.txt | β | 1.2 KB | view raw | |
| fix.diff | β | 1.3 KB | view raw | |
| manifest.json | β | 1.0 KB | view raw | |
| verdict.json | β | 3.9 KB | view raw |
DF-5590 β isavga_attach() never unwinds the legacy VGA window claims on failure
What this is
Evidence pack for the pass-2 finding on sys/bus/isa/vga_isa.c:
isavga_attach() activates the two legacy VGA resource windows
(SYS_RES_IOPORT 0x3c0-0x3df, SYS_RES_MEMORY 0xa0000-0xbffff) at
sys/bus/isa/vga_isa.c:144-148, discards both bus_alloc_resource()
return values, and then has no error unwinding when
vga_attach_unit() fails (vga_isa.c:150-152) or fb_attach() fails
(vga_isa.c:158-160). There is no detach method at all
(vga_isa.c:66-74), so on a failed attach the claims are permanent.
The failure path is not exotic: isavga_probe() succeeds even when no
VGA hardware is present, because vga_probe() returns 0 unconditionally
after probe_adapters() fails verification (sys/dev/video/fb/vga.c:838-852,
verify_adapter() write/read test at vga.c:3951-... / probe_adapters at
vga.c:616-...). On any legacy-BIOS boot without VGA decode (server with
onboard VGA disabled, -vga none style VM), the expected outcome is:
probe claims success -> attach claims both windows in the ISA rman ->
vga_init() returns ENXIO because V_ADP_PROBED was never set
(vga.c:855-859) -> attach fails -> claims leak forever on a zombie device.
With options FB_INSTALL_CDEV the make_dev()'d cdev also leaks
(vga_isa.c:156-160).
Reproduce
Nothing to compile β this is runtime introspection of the live driver:
./run.sh # from the repo root (uses dfbsd-qemu/vm.sh)
run.sh collects: dmesg attachment lines, the rman window claims
(devinfo -rv), and proof the mechanism exists on the running kernel
(vga0 attached on isa1 with exactly port 0x3c0-0x3df / iomem
0xa0000-0xbffff).
The leak itself requires attach to fail, i.e. a BIOS boot with no
VGA decode (QEMU -vga none, or onboard VGA disabled in BIOS). The
audit rig's Bochs VGA always passes verify_adapter(), so the failure
branch cannot be entered on this guest β see VERDICT.md for the
line-by-line path trace.
Expected output
Successful-attach baseline (what the rig shows):
vga0: <Generic ISA VGA> at port 0x3c0-0x3df iomem 0xa0000-0xbffff on isa1
On a VGA-less BIOS machine the same GENERIC kernel would print nothing
for vga0 (attach fails) yet hold both rman claims β that asymmetry is
the bug; fix.diff unwinds it.
DF-5590 VERDICT β inconclusive at runtime (mechanism code-certain)
Finding: isavga_attach() claims the legacy VGA I/O and memory windows
and never unwinds them when vga_attach_unit() / fb_attach() fail; the
returned struct resource *s are discarded and the driver has no detach
method, so failed-attach leaves permanent rman claims (and, with
FB_INSTALL_CDEV, a leaked cdev).
Runtime status: inconclusive β the defect is certain from source, but
the failure branch could not be entered on this rig because its Bochs VGA
always passes verify_adapter(). This is a hardware-absence trigger, not a
software precondition the guest can express (QEMU args are fixed by
dfbsd-qemu/vm.sh; no loader(8) knob makes verify_adapter() fail while
still creating the vga0 isa child).
Line-by-line path trace (why the mechanism is certain)
device vga0 at isa?βsys/config/X86_64_GENERIC:155creates the isa child; boot enumerates it (dmesg: vga0 ... on isa1).isavga_probe()sys/bus/isa/vga_isa.c:105-130: - PnP guard ok (:112). -vga_probe_unit()->vga_probe()sys/dev/video/fb/vga.c:838-853: returns 0 even whenprobe_adapters()failed (noV_ADP_PROBED), because the return ofprobe_adapters()atvga.c:846is ignored;*adpp = &biosadapteris handed out regardless (:850). - Probe therefore "succeeds" on VGA-less hardware and programs the static windows into the resource list:bus_set_resource()IOPORTIO_VGA(0x3c0)/32, MEMORY0xa0000/0x20000(vga_isa.c:118-121, values from the staticbiosadapteratvga.c:175-179).isavga_attach()vga_isa.c:132-172: -bus_alloc_resource()IOPORT:144-145and MEMORY:147-148withRF_ACTIVE|RF_SHAREABLEβ return values discarded; with start=0/end=~0/count=0 these allocate exactly the ranges set in probe (resource_list_alloc()isdefault path,sys/kern/subr_bus.c:2693-2698). -vga_attach_unit():150->vga_init()vga.c:855-859returns ENXIO when!probe_done(adp)β the normal result on VGA-less hardware. -if (error) return error;:151-152β the two activated rman claims are not released. The newbus child remains in the tree with its resource-list entries holding livestruct resource *s (rle->resset byresource_list_alloc,subr_bus.c:2705-2711). - With FB_INSTALL_CDEV:make_dev()+reference_dev()at:156-157, thenfb_attach()failure at:158-160also returns withoutdestroy_dev()β leaked cdev (LINT-only; GENERIC has no FB_INSTALL_CDEV,sys/conf/options:404).- No unwind ever happens later:
isavga_methodsvga_isa.c:66-74has nodevice_detachand nodevice_shutdownβ nothing can reclaim the claims short of a reboot.
Observed live (baseline, successful attach β the healthy twin of the bug)
$ dmesg | grep vga0
vga0: <Generic ISA VGA> at port 0x3c0-0x3df iomem 0xa0000-0xbffff on isa1
$ devinfo -rv | grep -A5 vga0 # works as UNPRIVILEGED user
vga0
I/O ports:
0x3c0-0x3df
I/O memory addresses:
0xa0000-0xbffff
This proves the claim mechanism (probe bus_set_resource -> attach
bus_alloc_resource -> rman ranges published in devinfo) is exactly as
traced; on a VGA-less BIOS boot the same two ranges are claimed and then
orphaned when attach fails two lines later.
Impact
- No memory corruption. The orphaned claims block any later legitimate claimant of the legacy VGA windows (an isa-attached display driver loaded via kldload would get EBUSY): local resource-starvation DoS with a narrow ceiling, plus a leaked cdev on LINT kernels.
- Trigger: BIOS boot with no VGA decode (or any
vga_attach_unit()failure). No unprivileged-user action is involved in triggering it; the finding is correctness/robustness with a DoS tail.
Why the fix is what it is
Check both bus_alloc_resource() returns, release them when
vga_attach_unit() fails, and destroy_dev() when fb_attach() fails.
fix.diff in this pack implements exactly that against the read-only
sys/ tree. Fix validation was not performed: no memory corruption
reproduced (contract mandates kernel-rebuild validation only then), and
the fixed branch is untriggerable on this rig for the same hardware
reason as above β hence fix_status: not_testable.
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 against the read-only sys/ tree (patch -p1 dry-run OK) implementing the unwind; kernel-rebuild validation not performed: no memory corruption reproduced (contract mandates rebuild validation only then) and the fixed failure branch is untriggerable on this rig for the same hardware-absence reason as the baseline (rig's Bochs VGA always verifies; vm.sh QEMU args are fixed, no -vga none control).
findings/poc/DF-5590/fix.diff (unified diff vs sys/bus/isa/vga_isa.c, dry-run applied clean)
Confirmed kernel references
Detail
Evidence (decisive lines)
findings/poc/DF-5590/run.log (dmesg 'vga0 ... at port 0x3c0-0x3df iomem 0xa0000-0xbffff on isa1' + devinfo -rv claims); VERDICT.md (line-by-line trace: probe success-on-absent-hardware at vga.c:846-852, resource_list_alloc isdefault path subr_bus.c:2693-2698, vga_init ENXIO at vga.c:855-859, no unwind at vga_isa.c:151-152/159-160, no detach at vga_isa.c:66-74); env.txt (GENERIC, no FB_INSTALL_CDEV, no /dev/vga0)
PoC changes
No seeded PoC existed; authored an introspection harness (introspect.sh/run.sh) capturing the live claim mechanism as the baseline; the failure branch is documented by source trace in VERDICT.md since it needs VGA-less BIOS hardware.
Verified recommended fix
Check both bus_alloc_resource() returns and bus_release_resource() them on vga_attach_unit() failure; destroy_dev() on fb_attach() failure (fix.diff).
Verdict
Code-certain defect, runtime-inconclusive on this rig: isavga_attach() (sys/bus/isa/vga_isa.c:144-148) activates the legacy VGA windows (I/O 0x3c0-0x3df, MEM 0xa0000-0xbffff) discarding both bus_alloc_resource() returns, and returns early at :150-152 (vga_attach_unit failure β the EXPECTED outcome on BIOS hardware without VGA decode, since vga_probe() returns 0 even when verify_adapter() failed, vga.c:838-853) or :158-160 (fb_attach failure, leaking the make_dev'd cdev) without releasing them; the method table at vga_isa.c:66-74 has no detach/shutdown, so the claims are permanent. Live baseline on the QEMU guest (vga0 attached on isa1, devinfo -rv shows both claims, run.log) proves the claim mechanism exactly as traced, but the failure branch cannot be entered because the rig's Bochs VGA always passes verify_adapter() and QEMU args are fixed by vm.sh β a hardware-absence trigger, not a guest-expressible software precondition. No memory corruption; ceiling is permanent legacy-window rman starvation (blocks later kldloaded claimants with EBUSY) plus a LINT-only cdev leak.
No comments yet.