# DF-5277 — `pci_freecfg()` panics on VPD-capable devices (kfree(NULL) / NULL deref via stale counts)

## What

`sys/bus/pci/pci.c:2146-2153` — the VPD teardown in `pci_freecfg()`:

```c
if (dinfo->cfg.vpd.vpd_reg) {
        kfree(dinfo->cfg.vpd.vpd_ident, M_DEVBUF);          /* NULL if never parsed / after cleanup */
        for (i = 0; i < dinfo->cfg.vpd.vpd_rocnt; i++)
                kfree(dinfo->cfg.vpd.vpd_ros[i].value, M_DEVBUF);   /* vpd_ros may be NULL */
        kfree(dinfo->cfg.vpd.vpd_ros, M_DEVBUF);            /* NULL after cleanup */
        for (i = 0; i < dinfo->cfg.vpd.vpd_wcnt; i++)
                kfree(dinfo->cfg.vpd.vpd_w[i].value, M_DEVBUF);
        kfree(dinfo->cfg.vpd.vpd_w, M_DEVBUF);
}
```

Two DragonFly-specific landmines (upstream FreeBSD tolerates these because
`free(NULL)` is a no-op there — DragonFly's `kfree(NULL, ...)` panics,
`sys/kern/kern_slaballoc.c:1406`):

* **Variant A (never parsed):** any device whose VPD capability was found
  at enumeration (`vpd_reg != 0`) but whose VPD was never parsed has
  `vpd_ident == vpd_ros == vpd_w == NULL` → first `kfree(NULL)` panics.
* **Variant B (failed parse):** `pci_read_vpd()`'s error cleanup
  (pci.c:1297-1320) frees and NULLs `vpd_ros`/`vpd_w`/`vpd_ident` but
  **does not reset `vpd_rocnt` / `vpd_wcnt`**, which were already set by a
  *completed* section → `vpd_ros[0].value` dereferences `*(NULL+8)` →
  page-fault panic. The same stale-count state also NULL-derefs
  `pci_get_vpd_readonly_method()` (pci.c:1354-1357).

Reachability: `pci_freecfg()` is called from the CardBus hotplug paths
(`sys/dev/pccard/cardbus/cardbus.c:191,246` — card insertion failure, card
removal, and `cardbus_suspend()`). A CardBus card with a VPD capability
(very common for PCMCIA network cards) whose VPD is malformed — or simply
never queried — panics the system on removal/suspend.

## Reproduce

The userspace harness (see ../DF-5276) mode `t2` first proves the *parser*
produces the crash precondition (`vpd_ros == NULL`, `vpd_rocnt == 1`) via
its own error-cleanup path. Then a kernel module calls the REAL exported
`pci_freecfg()` on a devinfo carrying exactly that state:

```
cd kmod && make
kldload ./freecfgcrash.ko                  # variant A: kfree(NULL) panic
# (reboot) 
kenv DF5277_VARIANT=B && kldload ./freecfgcrash.ko   # variant B: *(NULL+8)
```

Expected (vulnerable kernel): immediate panic —
`panic: trying to free NULL pointer` (A) or `Fatal trap 12: page fault
while in kernel mode, fault VA 0x8` (B). Serial console captured in
`panic-A.txt` / `panic-B.txt`.

Expected (kernel patched with ../fix.diff): module loads, prints
`DF5277: pci_freecfg() returned without crashing (fixed kernel)`, unloads.

## Fix

`fix.diff`: NULL-guard every VPD kfree in `pci_freecfg()`, reset
`vpd_rocnt`/`vpd_wcnt` in the parse error cleanup, NULL-guard
`pci_get_vpd_readonly_method()`.
