β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-3397

rx-ring init writes 4-byte int into const uint16_t nr_buf_size, clobbering the adjacent flags field (latent β€” module does not build on DFly)

Summary

netmap_mem_rings_create initializes the rx rings' buffer-size field with *(int *)&ring->nr_buf_size, but nr_buf_size is const uint16_t immediately followed by uint16_t flags: the 4-byte store overwrites flags with the high 16 bits of the buffer size. The tx-ring init 30 lines earlier is correct (*(uint16_t *)). Benign-by-luck with default 2KB buf pools (high half = 0); with pool objsize >= 65536 flags is silently set, flipping userspace-visible ring semantics (NR_TIMESTAMP/NR_FORWARD/NR_RX_TSTMP) on rx rings only. LATENT (module unbuildable - verified). Fix: match the tx path's uint16_t width.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3397 Β· 5 files
FileTypeDescriptionSize
VERDICT.md β€” 1.7 KB ↓ raw
netmap_build.log β€” 12.2 KB view raw
fix.diff β€” 486 B view raw
manifest.json β€” 623 B view raw
verdict.json β€” 1.7 KB view raw
VERDICT.md
↓ download raw

VERDICT β€” DF-3397: analysis-only (module does not build on DragonFly)

Root cause

netmap_mem_rings_create() initializes the rx rings' buffer-size field with a 4-byte write:

sys/net/netmap/netmap_mem2.c:974:
    *(int *)(uintptr_t)&ring->nr_buf_size =
        NETMAP_BDG_BUF_SIZE(na->nm_mem);

but struct netmap_ring.nr_buf_size is const uint16_t (sys/net/netmap/netmap.h:225), immediately followed by uint16_t flags (netmap.h:226). The 4-byte store therefore overwrites the adjacent flags field with the high 16 bits of the buffer size. The tx-ring initialization 30 lines earlier does it correctly: *(uint16_t *)(uintptr_t)&ring->nr_buf_size (netmap_mem2.c:945).

Consequence (latent)

With default pool sizes (buf objsize 2048) the high half of the size is 0, so flags is re-zeroed on an already-zeroed allocation β€” benign by luck. With a large buf size (pool is configurable up to objmaxsize 65536 via dev.netmap.buf_size; NETMAP_BDG_BUF_SIZE rounds to the pool _objsize), the high half becomes 1 and flags is silently set β€” userspace then sees NR_TIMESTAMP/NR_FORWARD/NR_RX_TSTMP style flag bits it never requested, changing ring semantics (e.g. timestamp writes into slots, buffer forwarding) on rx rings only. Latent: the module does not build on this tree (netmap_build.log; same platform fact as DF-3396).

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_testable
baseline no→ patch + rebuild →patched clean

fix.diff authored; cannot be built/validated because the netmap module does not compile on this tree.

fix.diff
↓ fix.diffper-fix-DF-3397

Confirmed kernel references

Detail

Evidence (decisive lines)

['VERDICT.md β€” the 4-byte vs 2-byte store asymmetry (:974 vs :945) and the adjacent-field layout (netmap.h:225-226)', 'netmap_build.log β€” module build failure on this tree']

PoC changes

No executable PoC possible; analysis-only.

Verified recommended fix

Use (uint16_t )(uintptr_t)&ring->nr_buf_size in the rx path, matching :945.

Verdict

Latent type/width bug (certain from source): rx-ring init writes a 4-byte int into the const uint16_t nr_buf_size field (netmap.h:225), clobbering the adjacent uint16_t flags (netmap.h:226) with the high half of NETMAP_BDG_BUF_SIZE; the tx-ring path at :945 uses the correct uint16_t store. Benign with default 2KB pools (high half 0), changes rx-ring semantics with >=64KB buf pools. PoC impossible: module does not compile on this tree.