# DF-4997 — VERDICT

**Finding**: `if_ral_pci.c` drives the three child drivers through one union
(`struct ral_pci_softc.u`) and accesses the per-child softc via the
`sc_rt2560` member alias.  `sc_invalid` writes through that alias miss their
target for rt2860-family devices.

## Reproduced? — YES, at the level the bug exists (compiled layout), live on the guest

The bug *is* a layout divergence; it is fully demonstrated by the real
compiled offsets, printed from the actual kernel headers on the audit guest
(module `df4996.c`, `df4997_report_offsets()`, captured in run.log):

| field | rt2560 | rt2661 | rt2860 |
|---|---|---|---|
| sc_st | 7360 | — | 7360 |
| sc_sh | 7368 | — | 7368 |
| watchdog_ch | 7376 | — | 7376 |
| sc_tx_timer | **7400** | — | (absent; at 21224) |
| sc_invalid | **7404** | **7404** | **7400** |
| sc_debug | 7408 | 7408 | **7404** |

Root cause chain:

1. `rt2560var.h:106-122` prefix: `sc_ic, sc_mtx, sc_snd, sc_dev, sc_st,
   sc_sh, watchdog_ch, sc_tx_timer, sc_invalid, sc_debug`.
2. `rt2860var.h:117-132` prefix: `sc_ic, sc_snd, sc_mtx, sc_dev, sc_st,
   sc_sh, watchdog_ch, sc_invalid, sc_debug` — `sc_snd` swapped ahead of
   `sc_mtx` (offset-neutral: sizeof(struct lock)=32, sizeof(struct
   mbufq)=24, both 8-aligned, so all following offsets coincide) **and
   `sc_tx_timer` dropped from the prefix** (it survives at :160), which
   shifts `sc_invalid`/`sc_debug` down by 4 relative to rt2560/rt2661.
   The comment at rt2860var.h:133-135 ("The same in both up to here") is
   therefore false for the rt2860 prefix.
3. `if_ral_pci.c:205` aliases `&psc->u.sc_rt2560`; :251 `sc->sc_invalid=1`,
   :296 `sc->sc_invalid=0`, :308 `sc->sc_invalid=!bus_child_present(dev)`
   all write at offset 7404 — rt2860's **sc_debug**.
4. Dispatch (`if_ral_pci.c:227-239`): default ⇒ rt2860 ops for every
   rt2860-family ID (0x0601/0x0681/0x0701/0x0781/0x3060/…/0x539f and all
   Edimax/MSI/AWT rebrands — the overwhelming majority of `ral_pci_ids`).
   Only 0x0201→rt2560 and 0x0301/0x0302/0x0401→rt2661 use the aligned
   layouts.

### Effects (rt2860 family)

- `rt2860_softc.sc_invalid` is never set by the glue: the card-ejected guard
  `if (sc->sc_invalid) return;` in `rt2860_watchdog` (rt2860.c:2080) can
  never trip after surprise removal (the flag's entire purpose); the
  "device not ready" latch during attach is equally dead.  No *other*
  writer of sc_invalid exists (checked rt2560.c/rt2661.c/rt2860.c).
- `rt2860_softc.sc_debug` receives 1 at pre-attach (transiently enables
  DPRINTF until rt2860.c:242 resets it to 0) and `!bus_child_present()` at
  detach (softc is dying; the RAL_DEBUG sysctl at rt2860.c:369 may print
  debug spam during teardown on an ejected card).
- `sc_st`/`sc_sh` writes land correctly (7360/7368 in both), so the device
  still works — this is why the regression went unnoticed.

### Impact

Robustness/correctness regression with a narrow security edge: dead
surprise-removal guard on CardBus/PCIe hot-unplug of rt2860-family cards
(watchdog keeps driving `rt2860_stop_locked`/`rt2860_init_locked` against
unmapped MMIO after ejection).  Requires physical removal/failure —
Low severity, no unprivileged trigger.

### Fix

Root-cause fix in fix.diff: restore the documented common prefix in
rt2860_softc (move `sc_mtx` ahead of `sc_snd`, move `sc_tx_timer` into the
prefix — private struct, no persistence/ABI impact) **and** add
compile-time assertions in if_ral_pci.c so any future drift of the aliased
fields (`sc_st`, `sc_sh`, `sc_invalid`) fails the build instead of silently
mis-writing.  Not kernel-built on the guest (no ral hardware; a plain
kernel build would only prove it compiles) — layout math and the live
offset table above carry the proof.

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