# DF-4794 — VERDICT

**Status: untested (logic-level proven from source + upstream divergence;
guest has no SR-IOV igb VF hardware). Severity: Low. Confidence: certain.**

## Root cause (path:line)

`sys/dev/netif/ig_hal/e1000_vf.c:421-427` — in
`e1000_update_mc_addr_list_vf()`, when `mc_addr_count > 30` the code first
ORs `E1000_VF_SET_MULTICAST_OVERFLOW` (0x00800000, `e1000_mbx.h:82`) into
`msgbuf[0]` (:422), then unconditionally **assigns**
`msgbuf[0] = E1000_VF_SET_MULTICAST` (:426), destroying the overflow
indication before `e1000_write_msg_read_ack(hw, msgbuf,
E1000_VFMAILBOX_SIZE)` (:436) hands the message to the PF.

(Line :422 also *reads* uninitialized `msgbuf[0]` — the array is never
initialized — but that value is immediately overwritten, so the read is
dead; the live uninitialized-tail transmit issue is DF-4795.)

## Reachability (live path, not dead code)

- igb driver binds VF devices: `sys/dev/netif/igb/if_igb.c:108,123`
  (`IGB_DEVICE(82576_VF)`, `IGB_DEVICE(I350_VF)`).
- `e1000_set_mac_type` maps those IDs to `e1000_vfadapt` /
  `e1000_vfadapt_i350` (`sys/dev/netif/ig_hal/e1000_api.c:392-397`), and
  `e1000_init_function_pointers_vf` installs the VF op table
  (`e1000_api.c:510-514`).
- `igb_set_multi()` (`if_igb.c:1267-1310`) counts joined AF_LINK multicast
  addresses into `mta` and calls `e1000_update_mc_addr_list(&sc->hw, mta,
  mcnt)` whenever `mcnt < MAX_NUM_MULTICAST_ADDRESSES`; a VF therefore
  reaches `e1000_update_mc_addr_list_vf` with any count up to that cap
  (well above 30). Joining multicast groups requires no privilege.

## Impact

Multicast *availability* on igb VF interfaces only: with >30 groups joined,
groups beyond the 30 forwarded hashes stop receiving traffic (the PF never
enables multicast promiscuous). Typical victim: IPv6 solicited-node
multicast for addresses beyond the forwarded hash set → neighbor discovery
fails → connectivity loss for those addresses. No memory corruption, no
info disclosure, no privilege boundary crossed. Severity Low
(CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L).

## Evidence

- `run.log` — `mcmsg_demo` output on the audit guest: current ordering →
  PF-visible `msgbuf[0]=0x001e0003` (flag LOST); fixed ordering →
  `0x009e0003` (flag SET). Deterministic arithmetic with the real macro
  values from `e1000_mbx.h:62-82`.
- Upstream divergence: FreeBSD current `sys/dev/e1000/e1000_vf.c`
  (`e1000_update_mc_addr_list_vf`) assigns
  `msgbuf[0] = E1000_VF_SET_MULTICAST;` *before*
  `msgbuf[0] |= E1000_VF_SET_MULTICAST_OVERFLOW;` — the opposite order of
  DragonFly — i.e. upstream keeps the flag. Fetched 2026-09-23 from
  cgit.freebsd.org.
- `build.log` — fix compile-validation on the audit guest:
  BASE_CC_RC=0, FIXED_CC_RC=0 (only pre-existing `systm.h:216` builtin
  warning). Guest `/usr/src` copy verified bit-identical to the audited
  tree (sha256 c0cdce6b…).
- `env.txt` — guest is virtio-only; no e1000/VF device ⇒ runtime path not
  exercisable there (hence `untested`, attempts 0).

## Why not panics/corruption classes on this file (pass-2 kill list)

- All mailbox reads into fixed stack arrays use sizes ≤ buffer size:
  `e1000_reset_hw_vf` read_posted 3 into `u32 msgbuf[3]` (:263,288);
  `rar_set_vf` 3 into `[3]` (:327,337); `promisc_set_vf` 1 into 1 word
  (:498-501); `write_msg_read_ack` 16 into `[E1000_VFMAILBOX_SIZE]`
  (:384-388). `e1000_read_mbx_vf` copies exactly `size` words from VMBMEM
  registers (e1000_mbx.c:497-513) — no PF-controlled length can exceed the
  caller's buffer. (Posted-wrapper size-validation gap itself is DF-4638.)
- `memcpy(hw->mac.perm_addr, addr, 6)` with `addr=&msgbuf[1]` — 6 bytes
  inside the 12-byte buffer; perm_addr is `ETH_ADDR_LEN` (6). In-bounds.
- `hash_list[i]` writes: clamped `i<30`, bytes 4..63 of the 64-byte
  msgbuf — exactly fits (`E1000_VFMAILBOX_SIZE=16`, e1000_mbx.h:62).
- `e1000_hash_mc_addr_vf` shift loop terminates for the hard-coded
  `mta_reg_count=128` (bit_shift=4); upstream bounded it defensively
  (start 1, `<4`) but no reachable input misbehaves here.
- All wait loops bounded: reset wait 200×5µs (E1000_VF_INIT_TIMEOUT,
  e1000_82575.h:456); posted read/write bounded by `mbx->timeout`
  (E1000_VF_MBX_INIT_TIMEOUT=2000, e1000_mbx.h:92) inside e1000_mbx.c.
- No NULL-op dispatch from this op table found in-tree (vf hal consumed
  only via e1000_api dispatchers, all NULL-guarded — e1000_api.c pass).

## Fix

`fix.diff` hunk 2 mirrors upstream ordering (assign SET_MULTICAST, OR
OVERFLOW in the >30 branch, else-branch plain assign). Hunk 1 (the
`= { 0 }` initializer) addresses sibling DF-4795. Combined diff is
git-apply-clean (`git apply --check` OK) and compile-validated on the
guest.

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