DF-4067 / verdict.json
{ "finding_id": "DF-4067", "status": "inconclusive", "reproduced": 0, "impact": "none observed (hardware-gated); source-level: kernel heap OOB read up to 63489 bytes past a 2048-byte std cluster (65535 on u16 ti_len) delivered into received frames via BPF/sockets; jumbo path up to 56513 bytes past a 9024-byte slot incl. past the contigmalloc block; page-fault panic variant", "confidence": "certain", "verdict": "Source-level certain, runtime hardware-gated (inconclusive per family convention, matching DF-3604/DF-3989/DF-4024/DF-4045/DF-4055). ti_rxeof sets the delivered mbufs length verbatim from the NIC-DMAd return-ring descriptor field ti_len (uint16_t, if_tireg.h:710) at if_ti.c:1690 (m->m_pkthdr.len = m->m_len = cur_rx->ti_len) and hands the mbuf to ifp->if_input at if_ti.c:1706 -- with NO bound against the buffer actually backing the descriptor: std ring cluster = MCLBYTES 2048 minus ETHER_ALIGN 2 = 2046 usable (if_ti.c:694-705, armed in ti_newbuf_std; firmware-legit ceiling rcb->ti_max_len = TI_FRAMELEN 1518 at if_ti.c:1279), mini ring MHLEN-2 (if_ti.c:730-741,1305), jumbo ring TI_JLEN slot 9024 minus 2 = 9022 usable (if_ti.c:799-802, rcb max 9018 at if_ti.c:1290). A descriptor claiming ti_len above the buffer capacity makes every m_len consumer (BPF tap, ether_input, protocol m_copydata/pullup, socket receive) read past the buffer: adjacent-cluster/mbuf/pool kernel heap disclosed into delivered packets (info leak to any local user with a socket on the interface) or unmapped-tail fault (panic). Worst cases: std m_len=65535 on 2048B cluster = 63489 bytes OOB (16x the cluster); jumbo 65535 on 9024B slot = 56513 bytes OOB, past the 384*9024=3.4MB contigmalloc pool for large claims. Composes with DF-4066 (same descriptor picks the buffer AND declares its length). Runtime: audit guest has zero Tigon-family PCI devices (env.txt), so the faulty-firmware trigger cannot be instantiated; trigger.c (extent model) builds and runs on the guest (run.log RC=0).", "exploit_chain": "On real (faulty/malicious) Tigon/Tigon2 hardware or PCIe DMA tampering: return-ring descriptor with no TI_BDFLAG_ERROR and ti_len=65535 -> driver builds an mbuf claiming 65535 bytes over a 2048-byte std cluster -> leaked kernel-heap bytes ride into received frames, readable by any local user with a receiving socket on tiX / a BPF listener, or the stack faults on an unmapped tail -> panic. Inbound-only: read primitive (plus DF-4066s write primitive on the same descriptor).", "evidence": "run.log (per-ring extent table, std 63489 B / jumbo 56513 B headline numbers, RC=0); build.log; env.txt (no Tigon PCI devices on guest, md5 match of audited source); VERDICT.md (path:line trace + cleared length surfaces); trigger.c; fix.diff; fix_build.log (patched module compiles -Werror clean on guest)", "kernel_refs": [ "sys/dev/netif/ti/if_ti.c:1690", "sys/dev/netif/ti/if_ti.c:1706", "sys/dev/netif/ti/if_ti.c:694", "sys/dev/netif/ti/if_ti.c:705", "sys/dev/netif/ti/if_ti.c:730", "sys/dev/netif/ti/if_ti.c:741", "sys/dev/netif/ti/if_ti.c:799", "sys/dev/netif/ti/if_ti.c:802", "sys/dev/netif/ti/if_ti.c:1279", "sys/dev/netif/ti/if_ti.c:1290", "sys/dev/netif/ti/if_ti.c:1305", "sys/dev/netif/ti/if_tireg.h:710", "sys/dev/netif/ti/if_tireg.h:821", "sys/dev/netif/ti/if_tireg.h:822", "sys/dev/netif/ti/if_tireg.h:1048", "sys/sys/param.h:497" ], "poc_changes": "No seed PoC (no prior findings on this file). Authored trigger.c (per-ring extent model) per the DF-4024 convention; honest hardware-absent verification.", "attempts": 3, "guest_uname": "DragonFly dfbsd 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": 40, "guest_dirty": 0, "build_cmd": "cc -O2 -Wall -Wextra -o /tmp/df4067 /tmp/df4067.c", "run_cmd": "vm.sh run_root /tmp/df4067; echo RC=$?", "code_hash": "b886450cc6ca0dd2ffb35fe780692e9379c45a79dce3d070a449e0813267b215", "notes": "Pass-2 checklist item (a) for if_ti.c (DF-3604/3761/3715/3989/4024/4045/4055 family application). MHLEN modeled with a conservative 224 lower bound (exact value boot-dependent); the OOB conclusion is independent of the exact MHLEN since ti_len is 16-bit. Cleared: TX direction writes device lengths from kernel mbufs (no device input); ti_newbuf_* descriptor/buffer pairs consistent.", "recommended_fix": "Bound cur_rx->ti_len by the rings buffer capacity (MCLBYTES-2 / MHLEN-2 / TI_JUMBO_FRAMELEN-2) in ti_rxeof; drop over-length frames (ierror + m_freem), see fix.diff.", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff adds a per-ring maxlen and drops over-length frames using the drivers existing error-drop idiom; git apply --check passes and the patched if_ti.ko builds clean on the guest with -Werror -std=gnu11 (fix_build.log FIXBUILD_f2_RC=0); runtime validation impossible without Tigon hardware.", "fix_evidence": "findings/poc/DF-4067/fix.diff + findings/poc/DF-4067/fix_build.log" } |