{
  "finding_id": "DF-2714",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "sys_sendfile() marshals hdtr headers into an unbounded M_WAITOK mbuf chain (m_uiomove) before any socket validation or buffer accounting; reproduced as unprivileged user maxx on the stock kernel: a 56 MB header request transiently held 26684/33296 (80%) of the global mbuf cluster pool, and a 256 MB request left the syscall permanently blocked in the objcache allocator (D3, WCHAN=objcache) holding essentially every cluster, unkillable with kill -9, requiring a guest reset. A 1 MB cap on header/trailer totals (fix.diff, kernel #2) removes both the spike and the wedge (peak cluster sample 256/33296 = idle baseline).",
  "exploit_chain": "open(\"/etc/passwd\") for fd; socket(AF_INET,SOCK_DGRAM) for s (any socket works - validation happens after marshalling); sf_hdtr with one iovec of attacker-chosen length; sendfile() allocates attacker-chosen mbuf memory. Below pool size: transient 80%+ pool hold. Above pool size: uninterruptible sleep holding the entire cluster pool -> system-wide network starvation until reboot.",
  "evidence": [
    "run.log: '26684/33296 mbuf clusters in use' sampled at 100ms during the 56MB run (baseline 256), call then returns EINVAL",
    "run.2.log: '33494/33296 mbuf clusters in use', process 'D3 objcache', survives kill -9, SYSCALL_RETURNED never printed",
    "fix_run.log: kernel #2 with fix.diff - 56MB and 256MB both EINVAL instantly, PEAK_CLUSTER_SAMPLE '256/33296'"
  ],
  "kernel_refs": [
    "sys/kern/uipc_syscalls.c:1617",
    "sys/kern/uipc_syscalls.c:1625",
    "sys/kern/uipc_syscalls.c:1637",
    "sys/kern/uipc_syscalls.c:1645",
    "sys/kern/uipc_syscalls.c:1654",
    "sys/kern/kern_subr.c:455",
    "sys/kern/kern_subr.c:488",
    "sys/kern/uipc_mbuf.c:2664"
  ],
  "poc_changes": "Initial seed had no struct sf_hdtr (taken from sys/socket.h), missing errno.h; header size parameterized so the same binary performs both the bounded and the wedge variant.",
  "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": 1140,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -o sendfile_hdtr_dos sendfile_hdtr_dos.c",
  "run_cmd": "./sendfile_hdtr_dos 56 ; ./sendfile_hdtr_dos 256",
  "code_hash": "321a12de8f73eee8f31060d7335f34a91c86c0217e5abf4840806768fcfd2d35",
  "notes": "Wedge run required guest reset (performed). Contrast case: sosend()/sendmsg self-throttles on ssb_space so a single sendmsg cannot hold more than the socket send-buffer worth of mbufs - the sendfile header path has no equivalent bound. The sbytes=1 printed on failing calls is the already-filed DF-2695 uninit stack copyout, not re-reported.",
  "recommended_fix": "Cap sendfile hdtr header and trailer totals (SF_HDTR_MAXBYTES = 1MB) immediately after iovec_copyin, before m_uiomove (uipc_syscalls.c:1629/1659).",
  "fix_status": "fixed",
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #2: Mon Aug 31 04:55:00 UTC 2026  root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Applied fix.diff (SF_HDTR_MAXBYTES caps) to the guest's /usr/src, make nativekernel + installkernel (kernel #2, also carrying the DF-2713 fix - independent file), rebooted, re-ran the identical PoC at 56MB and 256MB: both return EINVAL immediately and the 100ms cluster sampler recorded a peak of 256/33296 (idle baseline). No allocation spike, no wedge.",
  "fix_evidence": [
    "fix_run.log",
    "fix.diff"
  ]
}
