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

xe_pccard_probe flag loop exits at the DINGO bit before testing it: case XE_CARD_TYPE_FLAGS_DINGO is dead code, CEM56/REM56 Dingo cards are never detected as Dingos

Summary

for(i=1; i != DINGO; i<<=1) terminates the moment i==4, so the only writer of scp->dingo is unreachable - CEM56/REM56 combo cards get the non-Dingo bring-up (skipped DCOR config, wrong modem magic writes): misconfigured/dead interface. Also masks a use-after-release in xe_activate's Dingo ioport loop that goes live once fixed. Logic mirror on guest (stock: never sets dingo; '<=' fix: exactly the two Dingo entries); if_xe.ko fix validated. Hw-gated.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4970 Β· 14 files
FileTypeDescriptionSize
README.md β€” 1.4 KB ↓ raw
VERDICT.md β€” 3.1 KB ↓ raw
dingo_flags.c β€” 5.0 KB view raw
build.sh β€” 95 B view raw
run.sh β€” 94 B view raw
build.log β€” 66 B view raw
run.log β€” 953 B view raw
run.2.log β€” 946 B view raw
run.3.log β€” 946 B view raw
env.txt β€” 942 B view raw
fix.diff β€” 367 B view raw
fix_build.log β€” 1.9 KB view raw
manifest.json β€” 778 B view raw
verdict.json β€” 3.0 KB view raw

DF-4970 β€” xe pccard: DINGO flag never set (dead switch case in probe flag loop)

sys/dev/netif/xe/if_xe_pccard.c:311 β€” the flag-propagation loop for (i = 1; i != XE_CARD_TYPE_FLAGS_DINGO; i = i << 1) terminates when i == 4 (DINGO) before evaluating it, so case XE_CARD_TYPE_FLAGS_DINGO is dead code and scp->dingo is never set for any card β€” including the CEM56/REM56 "Dingo" combo cards whose table entries (if_xe_pccard.c:119-122) set exactly that flag.

Files

  • dingo_flags.c β€” logic harness: verbatim tables + stock and fixed loops
  • build.sh β€” cc -O -Wall -o dingo_flags dingo_flags.c
  • run.sh β€” run harness (expect VIOLATION lines for 0x110006/0x110007)
  • run.log, run.2.log, run.3.log β€” 3 runs, identical result
  • fix.diff β€” one-line fix (!= β†’ <=)
  • fix_build.log β€” patched if_xe.ko builds on the guest with -Werror
  • env.txt β€” guest environment (no PCMCIA hardware)

Reproduce

sh build.sh && sh run.sh

Expected decisive lines:

0x110006   CEM56    ... stock=0,1,0      fixed=0,1,1
           VIOLATION: CEM56 is a Dingo card but scp->dingo == 0 in the STOCK loop
stock loop ever sets dingo: NO (case is dead code)
RESULT: VIOLATIONS FOUND   (exit=1)

Kernel-path reproduction is impossible on this guest (no PCMCIA controller / no PC card slot is emulated by this QEMU setup) β€” see VERDICT.md.

VERDICT.md
↓ download raw

DF-4970 VERDICT β€” xe pccard DINGO dead-code: CONFIRMED at source level, kernel path not instantiable on guest

Bottom line

xe_pccard_probe() never sets scp->dingo. The loop at sys/dev/netif/xe/if_xe_pccard.c:311-323 iterates i = 1, 2 and exits the moment i == XE_CARD_TYPE_FLAGS_DINGO (0x4), so the case XE_CARD_TYPE_FLAGS_DINGO: arm β€” the only writer of scp->dingo β€” is unreachable. Every Dingo-era CEM56/REM56 combo card (table entries XE_PROD_MULTI_ID6/ID7 at if_xe_pccard.c:119-122, flags MOHAWK|DINGO) is therefore mis-detected as a plain Mohawk.

How it was proven

  1. Source trace (certain). i takes values 1 (CE2), 2 (MOHAWK); the loop condition i != 0x4 fails at 4, so 4 is never tested. No other code path in the tree writes sc->dingo (grep dingo sys/dev/netif/xe/*).
  2. Logic harness on the DragonFly guest (dingo_flags.c, 3 runs): for all 10 card types the stock loop leaves dingo = 0; the two CEM56 entries violate the invariant "table flags include DINGO β‡’ scp->dingo"; the one-line <= variant restores it for exactly those two entries. Build/run logs: build.log, run.log, run.2.log, run.3.log.
  3. Fix compiles: fix.diff applied to the guest's /usr/src copy builds if_xe.ko cleanly with -Werror (fix_build.log).

Why not a kernel-path reproduction

The path requires a 16-bit PCMCIA bus with a Xircom CEM56/REM56 card inserted. The audit guest (QEMU/KVM, X86_64_GENERIC) has no PCMCIA controller device model (env.txt: no pccard module, no /dev/card), so the probe routine cannot be entered. Status is therefore inconclusive* (logic-proven, hardware-gated), consistent with the family precedent (DF-4438/DF-4613).

Impact ceiling (honest)

Functional, not memory-safety: with dingo unset, - xe_cemfix() skips the Dingo DCOR0-4 configuration writes (if_xe_pccard.c:201-208) β€” 2ndF/modem interrupt routing and EEDIO setup never programmed for RealPort cards; - xe_activate() never takes the Dingo aligned-ioport path (if_xe.c:1915+) and xe_enable_intr/xe_disable_intr/LED code apply the wrong "magic" modem bits (if_xe.c:1170,1265,1282,1916). Result: CEM56/REM56 cards are brought up misconfigured/non-functional (local device DoS for the inserting user; no kernel memory corruption). Note: the Dingo branch of xe_activate contains a use-after-release read of port_res (if_xe.c, rman_get_start after bus_release_resource) which this bug currently keeps unreachable β€” fixing the flag loop without checking that loop is not advised; both should be fixed together (observed during this audit; filed as an observation, if_xe.c is pass-1 territory).

Fix

fix.diff: change the loop bound to i <= XE_CARD_TYPE_FLAGS_DINGO. Validated: harness fixed-loop output + if_xe.ko builds with -Werror.

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

inconclusive
baseline no→ patch + rebuild →patched clean

Kernel-level fix validation impossible (no PCMCIA hardware). Validated at two levels instead: (1) the harness's fixed loop variant sets dingo=1 for exactly the CEM56 entries and changes nothing else; (2) fix.diff applies cleanly to the guest /usr/src copy and if_xe.ko builds with -Werror (fix_build.log). Guest sources restored after the build.

fix_build.log (patch + cc -Werror + ld -r if_xe.ko, RESTORED_CLEAN); run.log fixed column
↓ fix.diffper-fix-DF-4970

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log lines '0x110006 CEM56 ... stock=0,1,0 fixed=0,1,1' + 'VIOLATION: CEM56 is a Dingo card but scp->dingo == 0 in the STOCK loop' + 'stock loop ever sets dingo: NO (case is dead code)'; fix_build.log shows patched module linking if_xe.ko with -Werror

PoC changes

harness authored fresh (no seed); DragonFly userland queue/TAILQ conventions adapted; stock and fixed loop variants compiled from the same table

Verified recommended fix

Change the flag-propagation loop bound in xe_pccard_probe from 'i != XE_CARD_TYPE_FLAGS_DINGO' to 'i <= XE_CARD_TYPE_FLAGS_DINGO' so the DINGO case is reachable (and separately fix the port_res use-after-release in xe_activate's Dingo branch)

Verdict

CONFIRMED at source level; kernel path not instantiable on the audit guest (no PCMCIA controller device model, env.txt). xe_pccard_probe()'s flag loop (if_xe_pccard.c:311, 'for (i = 1; i != XE_CARD_TYPE_FLAGS_DINGO; i = i << 1)') exits at i==4 before testing the DINGO bit, so case XE_CARD_TYPE_FLAGS_DINGO is dead code and scp->dingo is never set for any card, including CEM56/REM56 Dingo combos (table entries if_xe_pccard.c:119-122). Guest-compiled logic harness over the verbatim table proves stock loop never sets dingo (3 runs) while the one-line '<=' fix sets it for exactly the two CEM56 entries; patched if_xe.ko builds on the guest with -Werror (fix_build.log).