# 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)

1. `device vga0 at isa?` — `sys/config/X86_64_GENERIC:155` creates the isa
   child; boot enumerates it (`dmesg: vga0 ... on isa1`).
2. `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 when `probe_adapters()` failed** (no `V_ADP_PROBED`),
     because the return of `probe_adapters()` at `vga.c:846` is ignored;
     `*adpp = &biosadapter` is handed out regardless (`:850`).
   - Probe therefore "succeeds" on VGA-less hardware and programs the
     static windows into the resource list: `bus_set_resource()` IOPORT
     `IO_VGA(0x3c0)/32`, MEMORY `0xa0000/0x20000` (`vga_isa.c:118-121`,
     values from the static `biosadapter` at `vga.c:175-179`).
3. `isavga_attach()` `vga_isa.c:132-172`:
   - `bus_alloc_resource()` IOPORT `:144-145` and MEMORY `:147-148` with
     `RF_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-859` returns 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 live `struct resource *`s
     (`rle->res` set by `resource_list_alloc`, `subr_bus.c:2705-2711`).
   - With FB_INSTALL_CDEV: `make_dev()`+`reference_dev()` at `:156-157`,
     then `fb_attach()` failure at `:158-160` also returns without
     `destroy_dev()` — leaked cdev (LINT-only; GENERIC has no
     FB_INSTALL_CDEV, `sys/conf/options:404`).
4. No unwind ever happens later: `isavga_methods` `vga_isa.c:66-74` has **no
   `device_detach` and no `device_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.
