# DF-3640 — bnx_reset() holds the NVRAM software-arbitration lock forever

**File:** `sys/dev/netif/bnx/if_bnx.c` (pass 2, GLM 5.3)
**Class:** shared-resource lock leak / management-plane interop (CWE-667)
**Severity filed:** Low, bucket `base:dev`, confidence certain (source fact)
**Verdict:** `inconclusive` at runtime — no Broadcom hardware on the audit
guest; the lock state itself is chip-internal and not observable without the
device.

## 1. Root cause (path:line)

`bnx_reset()`:
- `sys/dev/netif/bnx/if_bnx.c:2585` — `CSR_WRITE_4(sc, BGE_NVRAM_SWARB, BGE_NVRAMSWARB_SET1);`
- `sys/dev/netif/bnx/if_bnx.c:2586-2590` — poll up to 8000×20µs (160 ms) for `BGE_NVRAMSWARB_GNT1`
- `sys/dev/netif/bnx/if_bnx.c:2591-2592` — on timeout: print and *continue anyway*

and then performs the GRC core-clock reset, PCI state restore, APE lock/unlock
(`bnx_ape_unlock(sc, BGE_APE_LOCK_GRC)` at 2715) — but **never writes
`BGE_NVRAMSWARB_CLR1`**. Verified: `grep -c NVRAMSWARB_CLR1 if_bnx.c` → 0
(executed against the guest's byte-identical /usr/src copy, see run.log).

Callers of `bnx_reset()`: `bnx_attach()` (2092), `bnx_init()` (3604 — i.e.
every `ifconfig bnx0 up`, MTU change at 3857, watchdog reset at 3941, and the
error-recovery path `bnx_handle_status()` → `bnx_init()` at 2983), and
`bnx_stop()` (4000). The arbitration request/grant is therefore asserted
continuously for the whole lifetime of the driver.

## 2. Why this is a bug (sibling/upstream comparison)

- The code this driver derives from pairs the lock: bge(4)
  `bge_nvram_getbyte()` — `sys/dev/netif/bge/if_bge.c:551-558` acquires
  (SET1/GNT1) and `if_bge.c:588-589` releases (`BGE_NVRAMSWARB_CLR1` +
  read-back).
- Upstream Broadcom tg3 and FreeBSD bge release the NVRAM arbitration when
  they are done resetting the chip (`tg3_chip_reset`: "Release NVRAM
  arbitration"; FreeBSD `bge_reset` similarly) — i.e. the chip reset does NOT
  implicitly return the grant, otherwise those drivers would not bother.
- bce(4) had the same family of defect filed as DF-3605 (arbitration + access
  enable left held on an error path).

Note the bce case (DF-3605) broke the *driver's own* later NVRAM reads. bnx
cannot self-break because `bnx_read_nvram()` is a stub returning 1
(`if_bnx.c:405-409`) — the victim is instead every **other** agent of the
arbiter, principally the APE management firmware (NC-SI/DASH) these chips run
for out-of-band management, and any other driver instance sharing the part.

## 3. Impact

No kernel memory-corruption. The host permanently holds the flash/NVRAM
arbitration on the device:
- APE/management firmware NVRAM access (persistent configuration) fails from
  driver load until power cycle → out-of-band management plane breakage.
- Timeout handling is also wrong (print-and-continue at 2591-2592 proceeds to
  touch NVRAM-adjacent state without the grant), compounding the interop
  hazard on malfunctioning arbiters.

Rated Low: requires the specific hardware; no direct kernel memory-safety
impact; consequence is management-plane availability.

## 4. Guest evidence (why `inconclusive`)

run.log (executed on the guest against its /usr/src):
- `pci/if_bnx` in kernel, `NO_BROADCOM_DEVICE`, `sysctl hw.bnx` unknown oid →
  `bnx_reset()` unreachable at runtime on this guest.
- `md5 if_bnx.c` = `49a18c4b7642b9b2ebd1d02ad11542e4` == audited tree.
- `grep -c NVRAMSWARB_CLR1 if_bnx.c` → **0**; only NVRAMSWARB uses are
  2585/2587 (acquire/poll). bge comparison: 1 CLR1.

The lock is chip-internal state; with no device, nothing further is observable
→ honest `inconclusive` / `reproduced: 0`.

## 5. Fix

`fix.diff` — release the arbitration (CLR1 + read-back, matching bge's idiom)
right after `bnx_ape_unlock(sc, BGE_APE_LOCK_GRC)` in `bnx_reset()`, before
the firmware-handshake poll. Not runtime-validatable on this guest:
`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.
