bge_nvram_getbyte leaks the NVRAM software-arbitration lock and leaves NVRAM access enabled on its two timeout paths (BCM5906)
Summary
bge_nvram_getbyte pairs the BGE_NVRAM_SWARB acquire with release and saves/restores BGE_NVRAM_ACCESS on the success path - unlike the bnx sibling DF-3640 which never released - but returns without cleanup on the grant-timeout path (request bit left latched) and on the read-done timeout path (arbiter still held AND BGE_NVRAMACC_ENABLE left set). Reachable only at attach time on BCM5906. After a single NVRAM timeout during attach (already-malfunctioning hardware), the flash arbiter stays held for the driver's lifetime and the NVRAM access window remains enabled, starving ASF/management firmware of NVRAM - device-side functional DoS only; no host memory-safety impact and no unprivileged trigger.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3664 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| trigger.c | β | 3.2 KB | view raw | |
| build.sh | β | 107 B | view raw | |
| run.sh | β | 158 B | view raw | |
| build.log | β | 84 B | view raw | |
| run.log | β | 481 B | view raw | |
| env.txt | β | 342 B | view raw | |
| fix.diff | β | 714 B | view raw | |
| README.md | β | 1.4 KB | β raw | |
| VERDICT.md | β | 3.6 KB | β raw | |
| manifest.json | β | 1.0 KB | view raw | |
| verdict.json | β | 4.2 KB | view raw |
DF-3664 β bge_nvram_getbyte leaks NVRAM SWARB lock + access-enable on error paths
Build / Run / Expected
./build.sh # cc -O -Wall -o trigger trigger.c (in a DragonFly guest) ./run.sh # runs the trigger + records the guest-side reachability proof
Expected on real BCM5906 hardware (NOT available in this environment):
an NVRAM read that times out (grant timeout at if_bge.c:557 or read-done
timeout at if_bge.c:574) returns while BGE_NVRAMSWARB_SET1 is still
latched and BGE_NVRAMACC_ENABLE is still set; the flash arbiter stays
held by the driver for its lifetime, starving ASF/management firmware of
NVRAM access. Device-side functional DoS only β no host memory-safety
impact.
Actual on the audit guest: no Broadcom PCI device exists (virtio-net
only), so bge_attach/bge_nvram_getbyte are unreachable β the trigger
builds clean and prints its hardware-precondition notice (run.log).
Verification is therefore honestly inconclusive (source-visible
asymmetry; see VERDICT.md).
Files
trigger.cβ annotated hardware-precondition triggerrun.logβ guest proof:pci/if_bgecompiled into the kernel,NO_BROADCOM_DEVICE,sysctl hw.bgeunknown, /usr/src md5 == audited tree, clean build, trigger outputfix.diffβ git-apply-able release/restore on both error paths (mirrors the paired success-path cleanup at if_bge.c:584-589)verdict.json/manifest.jsonβ machine verdict
DF-3664 VERDICT β bge_nvram_getbyte leaks the NVRAM arbiter on error paths
Status: INCONCLUSIVE at runtime (no bge hardware β and specifically no BCM5906 β on the audit guest); source-level CERTAIN (visible asymmetry).
Root cause
bge_nvram_getbyte() β sys/dev/netif/bge/if_bge.c:544-592 β performs the
NVRAM software-arbitration handshake:
551: CSR_WRITE_4(sc, BGE_NVRAM_SWARB, BGE_NVRAMSWARB_SET1); /* REQUEST */
552: for (i = 0; i < 8000; i++) {
553: if (CSR_READ_4(sc, BGE_NVRAM_SWARB) & BGE_NVRAMSWARB_GNT1)
554: break;
555: DELAY(20);
556: }
557: if (i == 8000)
558: return (1); /* BUG #1: request still latched β
no BGE_NVRAMSWARB_CLR1 ever written */
561: access = CSR_READ_4(sc, BGE_NVRAM_ACCESS);
562: CSR_WRITE_4(sc, BGE_NVRAM_ACCESS, access | BGE_NVRAMACC_ENABLE);
565: CSR_WRITE_4(sc, BGE_NVRAM_CMD, BGE_NVRAM_READCMD);
566: for (i = 0; i < BGE_TIMEOUT * 10; i++) { ... }
574: if (i == BGE_TIMEOUT * 10) {
575: if_printf(&sc->arpcom.ac_if, "nvram read timed out\n");
576: return (1); /* BUG #2: SWARB lock STILL HELD and
BGE_NVRAMACC_ENABLE left set */
}
...
585: CSR_WRITE_4(sc, BGE_NVRAM_ACCESS, access); /* paired cleanup */
588: CSR_WRITE_4(sc, BGE_NVRAM_SWARB, BGE_NVRAMSWARB_CLR1); /* paired */
The success path is paired (584-589) β this is the e1000/bnx-audit discipline the orchestrator asked to verify, and it holds for the normal path. The two timeout paths return without either releasing the arbitration request or restoring the access register.
Reachability
bge_read_nvram() only proceeds for sc->bge_asicrev ==
BGE_ASICREV_BCM5906 (if_bge.c:603-604); it is called at attach time by
bge_get_eaddr_nvram() (if_bge.c:4977-4985) as the second station-address
source. One NVM read-done or grant timeout during attach therefore leaves
the arbiter held for the driver's whole lifetime.
Impact
Device-side functional denial of service only: after a timeout (i.e. on
already-malfunctioning hardware) other NVRAM masters β ASF/management
firmware, other agents sharing BGE_NVRAM_SWARB β can no longer arbitrate
for the flash, and the NVRAM access window stays enabled. No host
memory-safety impact, no unprivileged trigger. Contrast the bnx sibling
DF-3640 (bnx_reset never released, filed Low): bge's defect is strictly
weaker (error paths only), hence Info here.
Verification attempt (honest)
Audit guest: pci/if_bge compiled into the kernel (kldstat -v id 59) but
pciconf -l shows NO_BROADCOM_DEVICE (virtio-net only) and
sysctl hw.bge is an unknown oid β attach never runs, so neither the bug
nor the fix is exercisable. Guest /usr/src if_bge.c md5
f6c2d771384a52775cd4bb399d5dcab1 == audited tree (run.log).
Fix validation
fix.diff adds the missing cleanup on both error paths, mirroring the
paired success-path idiom (restore BGE_NVRAM_ACCESS, then
BGE_NVRAMSWARB_CLR1 + flush read; for the grant-timeout path only the
request drop, since access is not yet saved). git apply --check
passes against the pristine tree. Runtime baseline-vs-patched comparison
impossible for the hardware reason (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 adds the missing cleanup on both error paths using the exact register sequence of the paired success path (restore ACCESS then CLR1 + flush read; on the grant-timeout path only the request drop since 'access' is not yet saved); git apply --check passes against the pristine tree. Cannot be runtime-validated: the guest has no BCM5906 device so neither baseline nor patched behavior is exercisable.
fix.diff (git apply --check OK); baseline absence proof in run.log (NO_BROADCOM_DEVICE, sysctl hw.bge unknown oid)
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log (guest proof: driver in kernel, zero Broadcom devices, no attach, src md5 match, clean -Wall build), VERDICT.md (line-accurate acquire/release asymmetry trace with the paired success path for contrast), trigger.c (annotated hardware-precondition skeleton), fix.diff (git apply --check OK; release/restore added to both error paths)
PoC changes
No seed existed (new pass-2 finding); trigger written as an annotated hardware-precondition skeleton per the DF-3639/DF-3640 precedent, built and executed on the guest to prove the code path is unreachable there.
Verified recommended fix
In bge_nvram_getbyte, drop the arbitration request (BGE_NVRAMSWARB_CLR1) on the grant-timeout path and both restore BGE_NVRAM_ACCESS and release the lock on the read-timeout path, mirroring the success-path cleanup at if_bge.c:584-589 (see fix.diff).
Verdict
Source-level certain (visible asymmetry): bge_nvram_getbyte (sys/dev/netif/bge/if_bge.c:544-592) pairs the BGE_NVRAM_SWARB acquire (SET1, line 551) with release (CLR1, line 588) and the BGE_NVRAM_ACCESS save/restore (561-562 vs 585) on the SUCCESS path β the discipline the bnx sibling DF-3640 lacked β but its two error paths return without cleanup: the grant-timeout path at 557-558 leaves the SET1 request latched, and the read-done timeout path at 574-577 additionally leaves BGE_NVRAMACC_ENABLE set while holding the arbiter. Reachable only at attach time on BCM5906 (bge_read_nvram gates on asicrev==BCM5906, if_bge.c:603-604; called from bge_get_eaddr_nvram, if_bge.c:4977-4985). Impact: after one NVRAM timeout (already-malfunctioning hardware) the flash arbiter stays held for the driver's lifetime, starving ASF/management firmware of NVRAM β device-side functional DoS only, no host memory-safety impact, no unprivileged trigger. Runtime verification honestly inconclusive: the audit guest has virtio-net only; bge is compiled into the kernel (kldstat -v: pci/if_bge id 59) but no Broadcom PCI device exists, so bge_attach/bge_nvram_getbyte are unreachable (run.log: NO_BROADCOM_DEVICE, sysctl hw.bge unknown oid). Guest /usr/src if_bge.c md5 f6c2d771384a52775cd4bb399d5dcab1 is byte-identical to the audited tree.
No comments yet.