⬢ DragonFlyBSD Kernel Audit
DF-3759 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-3759",
  "status": "inconclusive",
  "reproduced": 0,
  "impact": "none",
  "confidence": "certain",
  "verdict": "Source-level certain: alc_rxeof (sys/dev/netif/alc/if_alc.c:3307) takes the received frame length from the controller-DMAd RRS 14-bit field (RRD_BYTES, if_alcreg.h:1240) and uses it to set m->m_pkthdr.len (:3368) and the last mbufs m_len (:3372, :3386) with NO bound against nsegs * alc_buf_size (1536) or ETHER_CRC_LEN, on MCLBYTES(2048) clusters from alc_newbuf (:3187). A faulty/malicious AR8131/8132/8151/8152/816x/817x can hand the stack an mbuf describing up to 16379 bytes in a 2048-byte cluster -> ~14.3KB heap OOB read past the cluster into bpf/socket receive paths (kernel heap info leak to an unprivileged local user), or lengths that underflow to negative m_len inside the delivered chain via the CRC-strip arithmetic at :3375-3381 (mbuf invariant violation -> panic). Runtime verification honestly inconclusive per family convention: the audit guest has virtio-net only, no Atheros PCI device (env.txt NO_ATHEROS_PCI, ifconfig=vtnet0 lo0, no hw.alc sysctl, if_alc.ko present but not loaded), so alc_attach/alc_rxeof are unreachable. Guest /usr/src copy is byte-identical to the audited tree (md5 fb1a46ef2b2559de4aa5d508e03efcd5). trigger.c (userland arithmetic model of :3368-3386) builds and runs on the guest (run.log TRIGGER_RC=0) and prints the exact corrupted lengths for the three device-controlled shapes. Distinct from this files DF-1207 (index half: rxdesc[rx_cons] OOB) - this is the length half; same descriptor, different sink, different primitive. Same class as DF-3604 (bce), DF-1478 (my), DF-1481 (vr), DF-1490 (tx), DF-1514 (ste), DF-1519 (lge), DF-1562 (pcn), DF-1351 (nfe); FreeBSD head has the identical missing bound (inherited defect, verified against FreeBSD sys/dev/alc/if_alc.c:3640,3701-3715).",
  "exploit_chain": "On real hardware: (1) device DMAs an RRS with RRD_VALID set, rd_cnt=1, bytes=16383, no error bits; (2) alc_rxeof hands the stack an mbuf with m_len=m_pkthdr.len=16379 backed by a 2048-byte cluster; (3) an unprivileged user with a TCP/UDP socket on alc0 receives up to ~14.3KB of kernel heap adjacent to the cluster (info leak); variant bytes=100/rd_cnt=15 delivers negative m_len (-21404/-19872) -> mbuf walker panic (local DoS). No write primitive from this bug alone.",
  "evidence": "run.log (trigger model output: OOB and negative lengths computed deterministically + presence probe), env.txt (NO_ATHEROS_PCI / NO_ALC_KLD / no hw.alc / md5 of guest src == audited src), VERDICT.md (path:line trace), trigger.c (annotated hardware precondition), fix.diff (drop-frame bound, git-apply clean)",
  "kernel_refs": [
    "sys/dev/netif/alc/if_alc.c:3307",
    "sys/dev/netif/alc/if_alc.c:3368",
    "sys/dev/netif/alc/if_alc.c:3372",
    "sys/dev/netif/alc/if_alc.c:3375",
    "sys/dev/netif/alc/if_alc.c:3386",
    "sys/dev/netif/alc/if_alc.c:3400",
    "sys/dev/netif/alc/if_alc.c:3187",
    "sys/dev/netif/alc/if_alc.c:3653",
    "sys/dev/netif/alc/if_alcreg.h:1240",
    "sys/dev/netif/alc/if_alcreg.h:554"
  ],
  "poc_changes": "No seed PoC. Authored trigger.c as an annotated hardware-precondition + userland arithmetic model of if_alc.c:3368-3386 (a syscall-level PoC is meaningless for a DMA-device bug); honest hardware-absent verification per family convention (DF-3604 pattern).",
  "attempts": 2,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64",
  "runtime_sec": 110,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -Wall -Wextra -o trigger trigger.c",
  "run_cmd": "vm.sh run_root cc -O2 -Wall -Wextra -o /root/trigger /root/trigger.c && /root/trigger (after scp of trigger.c)",
  "code_hash": "c18dd86967142cdf805b33af85b5734a0447ef11f2e3e4f94da2c7e710025297",
  "notes": "Pass-2 result for sys/dev/netif/alc/if_alc.c (GLM 5.3). First model build had a NULL-deref in the models own out-param (fixed in place; final TRIGGER_RC=0). Not a re-report of DF-1207: DF-1207 is the rxdesc[] index half, DF-3759 is the mbuf length half - both were re-derived independently and only the length half is new.",
  "recommended_fix": "In alc_rxeof, drop the frame when alc_rxlen > nsegs * sc->alc_buf_size or alc_rxlen < ETHER_CRC_LEN (see fix.diff).",
  "fix_status": "not_testable",
  "fix_baseline_reproduced": 0,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff adds the nsegs*alc_buf_size / ETHER_CRC_LEN bound using the files own error-frame early-return idiom (cf. if_alc.c:3322-3324; buffers reused in place). Cannot be runtime-validated: guest has no Atheros device, so neither baseline nor patched behavior is exercisable. The patch touches only existing symbols on an already-compiled path (git-apply clean).",
  "fix_evidence": "fix.diff"
}