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

MP_ReadOtherFunPciEConfigSpace never reads RE_CSIDR (constant 0xffffffff return) -> NULL CMAC bus_space handle dereference at re(4) attach on RTL8168FP/RTL8117, plus PCIe config RMW clobbering on all CSIAR users

Summary

The CSIAR config-space read helper performs the completion handshake and then returns its initialized-to-0xffffffff local without ever reading the RE_CSIDR data register - a dropped line in the vendor-driver port; every consumer sees 0xffffffff. Chain: re.c:4456 reads 0xffffffff -> bit0 set -> bus_space_map skipped -> re_cmac_handle=NULL -> re_exit_oob calls Dash2DisableTxRx for MACFG_70-72 whose !re_dash guard is commented out -> RE_CMAC_WRITE_1 on handle NULL offset 0xF9 -> *(volatile u_int8_t *)0xF9 -> kernel page fault at attach (boot-time panic with the card installed). Collateral: all 14 live RMW consumers in re_hw_start_unlock operate on 0xffffffff and write all-ones into NIC PCIe config dwords, misconfiguring common 8168 chips and silencing UE error reporting (0xF108). Vulnerable functions confirmed compiled into the stock /boot/kernel/if_re.ko (nm proof). Hardware-gated (not unpriv-triggerable); virtio-only guest -> honest not_reproduced with a mechanical source-proof harness (ALL CHAIN LINKS VERIFIED) + kldload check. Architectural fact established: on DragonFly this 32k-line file compiles only the rtl_* register back-end (entire data path is #ifndef __DragonFly__ dead code; if_re.c is the live driver).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3570 Β· 10 files
FileTypeDescriptionSize
README.md β€” 1.4 KB ↓ raw
VERDICT.md β€” 5.8 KB ↓ raw
trigger.c β€” 1.7 KB view raw
proof.sh β€” 1.7 KB view raw
proof.log β€” 4.2 KB view raw
run.log β€” 1.0 KB view raw
env.txt β€” 256 B view raw
fix.diff β€” 1.5 KB view raw
manifest.json β€” 1.6 KB view raw
verdict.json β€” 4.5 KB view raw

DF-3570 β€” MP_ReadOtherFunPciEConfigSpace never reads RE_CSIDR (constant 0xffffffff return) => NULL CMAC bus_space handle dereference (kernel panic) at re(4) attach on RTL8168FP/RTL8117, plus silent PCIe-config clobbering on every chip using the CSIAR read-modify-write paths.

Build / Run

Nothing to compile from userland. The fault is deterministic at device attach. On a machine with an RTL8168FP / RTL8117 (MACFG_70/71/72):

# kldload if_re          # or boot with the device present

Expected (unpatched): kernel page fault in Dash2DisableTx during rtl_exit_oob, before any interface registers. The serial console shows a panic with fault address 0xf9/0xf8/0xfb.

What IS verified in this pack (no such hardware on the audit guest)

./proof.sh     # mechanical re-derivation of every chain link from sys/

Guest (audit target) facts

dfbsd-qemu/vm.sh run_root 'pciconf -l'   # -> virtio only, NO_REALTEK_HW
nm /boot/kernel/if_re.ko                  # -> Dash2DisableTx,
                                          #    MP_ReadOtherFunPciEConfigSpace,
                                          #    rtl_* back-end all present
                                          #    in the shipped module

So the vulnerable code is compiled into the stock kernel module; the audit guest simply has no Realtek NIC to drive it. Status: not_testable (hardware absent), source-proven (see VERDICT.md).

VERDICT.md
↓ download raw

DF-3570 β€” VERDICT

Status: not_testable (audit guest has virtio-net only; no Realtek silicon). Source-level proof: complete and mechanical (proof.log). The vulnerable code is confirmed compiled into the stock if_re.ko shipped on the guest (nm evidence in env.txt), and the guest loads the module without error β€” there is simply no 10ec:8168 device to trigger attach.

Root cause (one dropped line)

MP_ReadOtherFunPciEConfigSpace() β€” sys/dev/netif/re/re.c:31163-31192 β€” performs the CSIAR config-space read handshake and then:

u_int32_t RetVal = 0xffffffff;      /* :31171 */
...
do { DELAY(100); ReadDone = CSR_READ_4(sc, RE_CSIAR); Timeout++; }
while (((ReadDone & 0x80000000) == 0) && (Timeout < WaitCount));
DELAY(50);
return RetVal;                      /* :31191 β€” never reads RE_CSIDR */

RetVal is never assigned the contents of RE_CSIDR (0x64), so every call returns the constant 0xffffffff. The write twin (MP_WriteOtherFunPciEConfigSpace, :31132-31161) does use RE_CSIDR (:31148), which makes the omission unambiguous β€” a porting drop of the upstream RetVal = RTL_R32(PCI_CSIDR); line.

Impact chain 1 β€” NULL CMAC handle => attach-time kernel panic

  1. re.c:4413-4417 β€” HwSuppDashVer = 3 for MACFG_70/71/72 (RTL8168FP / RTL8117 family).
  2. re.c:4448-4449 β€” sc->re_dash = re_check_dash(sc) (OCP 0x128 bit0).
  3. re.c:4456 β€” CmacMemPhysAddress = MP_ReadOtherFunPciEConfigSpace(sc,0,0xf018) == 0xffffffff (constant).
  4. re.c:4457 β€” if (!(CmacMemPhysAddress & BIT_0)) is FALSE for 0xffffffff, so the whole mapping block (:4458-4473) incl. bus_space_map() (:4468) is skipped and re_mapped_cmac_handle stays NULL (softc is bzeroed by if_re.c's attach). This holds for DASH and non-DASH SKUs alike β€” for non-DASH SKUs the if (sc->re_dash) gate (:4451) skips mapping anyway.
  5. re.c:4484-4489 β€” for MACFG_70/71/72 the softc unconditionally does sc->re_cmac_handle = sc->re_mapped_cmac_handle (NULL) and sc->re_cmac_tag = sc->re_mapped_cmac_tag (0).
  6. if_re.c:772 β€” attach calls rtl_exit_oob(sc) β†’ re.c:3588-3596 β€” for MACFG_61/62/67/70/71/72 β†’ Dash2DisableTxRx(sc).
  7. re.c:3445-3453 β€” Dash2DisableTx(): the if (!sc->re_dash) return; guard is commented out (:3447), and HW_DASH_SUPPORT_TYPE_3(sc) is true for MACFG_70-72, so it executes RE_CMAC_WRITE_1(sc, RE_CMAC_IBCR2, RE_CMAC_READ_1(sc, RE_CMAC_IBCR2) & ~BIT_0).
  8. On DragonFly the CMAC macros (re_dragonfly.h:43-54) are raw bus_space_* calls, and x86_64 bus_space_read_1 (sys/cpu/x86_64/include/bus_dma.h:135-141) is *(volatile u_int8_t *)(handle + offset) for memory tags β†’ read/write of virtual address 0xF9/0xF8/0xFB β†’ kernel page fault at attach, before the interface exists. Deterministic for every MACFG_70/71/72 device; boot with the card, or devd/kldload with it present.

Impact chain 2 β€” PCIe config-space clobbering (all CSIAR RMW users)

Live read-modify-write consumers of the broken read all receive 0xffffffff:

  • re.c:3891-3893 (re_hw_init): writes 0xffffffff to config 0xF108 (PCIE uncorrectable-error mask) β€” masks all UE reporting for the NIC.
  • re.c:5765, 5806, 5938, 5982, 6059, 6125, 6227, 6318, 6383, 6470, 6585, 6598, 6792, 6952 (re_hw_start_unlock, per-MACFG branches for 8168C/D/E/V): e.g. Data32 = 0xffffffff & 0xC0FFFFFF | (0x3F<<24) = 0xffffffff β€” the "preserve bits 23:0" intent of the RMW is defeated and the NIC's config dword is written all-ones (clock-gating / max-payload related setup).
  • re.c:7414 (re_hw_start_unlock_8125): DASH descriptor-format select made from garbage.

Reliability/functional misconfiguration of the device; no kernel memory corruption.

Threat model / severity

  • The panic is NOT triggerable by an unprivileged local user β€” it fires deterministically when the driver attaches to MACFG_70-72 silicon (device present at boot, devd autoload, or explicit kldload by root). Severity therefore Low ("requires specific hardware"), but certain: with an RTL8117/8168FP installed, the machine panics during re(4) attach.
  • The config clobbering affects common 8168 chips but stays inside the device's own config space.
  • No info leak, no privilege boundary crossed, no remote reach.

Why not reproduced on the guest

pciconf -l on the audit QEMU guest shows only virtio devices (NO_REALTEK_HW). The RTL8168FP MMIO/CSIAR registers cannot be driven, so the path cannot execute. kldload if_re succeeds and is a no-op (no matching PCI ID). Honest classification: not_testable / hardware absent, not false_positive β€” the chain is source-proven link by link (proof.log) and the vulnerable functions are present in the shipped module binary (env.txt).

Fix (fix.diff)

  1. Restore the dropped data-register read: RetVal = CSR_READ_4(sc, RE_CSIDR); after the handshake in MP_ReadOtherFunPciEConfigSpace.
  2. Defense in depth for the NULL-handle deref, so a DASH detection mistake can never fault the kernel: bail out of Dash2DisableTx/Dash2DisableRx when sc->re_cmac_handle == 0, and skip the CMAC path in re_init_software_variable's MACFG_70-72 assignment if unmapped.

Fix validation: not_testable β€” validating the runtime effect requires RTL8168FP hardware (panic present before patch, gone after). The patch is two-line plus guards, compile-checkable by inspection against the write twin at :31148; no kernel build was performed on the guest for this Low-severity, hardware-gated finding (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_testable
baseline no→ patch + rebuild →patched clean

fix.diff restores the RE_CSIDR read (mirrors the write path at re.c:31148) and adds NULL-handle guards so Dash2* can never fault through an unmapped CMAC window. Runtime before/after validation requires RTL8168FP/RTL8117 hardware which the audit guest does not have (virtio-net only); validation was therefore not possible and is recorded as not_testable rather than claimed.

fix.diff (git-apply-able vs sys/dev/netif/re/re.c); VERDICT.md 'Fix' section; proof.log chain link [1] would flip to FAIL once the read is restored (guard built into proof.sh).
↓ fix.diffper-fix-DF-3570

Confirmed kernel references

Detail

Evidence (decisive lines)

proof.log (mechanical re-derivation of every chain link; ALL CHAIN LINKS VERIFIED); run.log + env.txt (guest uname 6.5-DEVELOPMENT, pciconf virtio-only, nm shows MP_ReadOtherFunPciEConfigSpace/Dash2DisableTx/re_rar_set/rtl_set_eaddr in shipped if_re.ko); VERDICT.md (full narrative with path:line); fix.diff (restore RetVal = CSR_READ_4(sc, RE_CSIDR); NULL-handle guards in Dash2*/MACFG_70-72 assignment)

PoC changes

No runnable PoC exists for userland (fault is at device attach); trigger.c documents the trigger; proof.sh replaces the runtime PoC with a mechanical source-proof harness; guest evidence limited to module symbol presence + kldload per the orchestrator's hardware-unreachability guidance.

Verified recommended fix

Add the dropped data-register read (RetVal = CSR_READ_4(sc, RE_CSIDR)) in MP_ReadOtherFunPciEConfigSpace and guard Dash2DisableTx/Rx plus the MACFG_70-72 cmac assignment against a NULL re_cmac_handle.

Verdict

Not runtime-reproducible on the audit guest (virtio-net only, NO_REALTEK_HW in env.txt/run.log); recorded honestly instead of fabricated. Source-level proof is complete and mechanical: MP_ReadOtherFunPciEConfigSpace (re.c:31163-31192) initializes RetVal=0xffffffff at :31171 and returns it at :31191 without ever reading the RE_CSIDR data register (the write twin uses it at :31148), so every caller gets the constant 0xffffffff. Consequence 1 (panic): re_init_software_variable:4456-4457 sees bit0 set in 0xffffffff, skips bus_space_map(:4468); for MACFG_70/71/72 (RTL8168FP/RTL8117, HwSuppDashVer=3 per :4413-4417) re_cmac_handle is assigned the NULL re_mapped_cmac_handle at :4487 (also NULL for non-DASH SKUs since mapping is gated by sc->re_dash at :4451); re_exit_oob:3588-3596 then calls Dash2DisableTxRx at attach (if_re.c:772), whose !re_dash guard is commented out (:3447), producing RE_CMAC_READ_1/WRITE_1 on bus_space handle NULL at offset 0xF8/0xF9/0xFB β€” on x86_64 that is (volatile u_int8_t )(0+0xF9) (bus_dma.h:135-141) β€” a deterministic NULL-page kernel fault at driver attach on that silicon. Consequence 2 (misconfiguration): all live CSIAR read-modify-write consumers (re.c:3891-3893; 14 sites in re_hw_start_unlock incl. 5765/5806/.../6952; 7414 in re_hw_start_unlock_8125) RMW with 0xffffffff, clobbering NIC PCIe config dwords meant to be partially preserved (e.g. 5765-5768 yields 0xffffffff). nm proves the vulnerable functions are compiled into the stock /boot/kernel/if_re.ko on the guest and kldload if_re succeeds (no device match). Severity Low: hardware-gated, not unprivileged-user-triggerable; real-world effect is a boot-time panic on machines with an RTL8117/8168FP and wrong PCIe config on common 8168 chips.