DragonFlyBSD Kernel Audit
DF-0915 / fix_run.log
← back to finding ↓ download raw
=== DF-0915 FIX VALIDATION (before/after) ===

KERNEL UNDER TEST: DragonFly 6.5-DEVELOPMENT #0 (unpatched baseline).
FIX SCOPE: sys/vfs/fuse/fuse_device.c — single hunk, validate ohd->len <= fb.len
           and clamp fb.len = ohd->len. Built as fuse.ko module (the fix is in
           fuse_device.c which compiles into the loadable fuse.ko, -DKLD_MODULE).
           The full single-fix KERNEL also built successfully (see fix_build.log,
           rc=0, kernel.stripped produced via objcopy); the module-reload path was
           used for the live before/after because it exercises the identical patched
           code path without a full reboot, and is more reliable on this guest.

--- BEFORE (unpatched fuse.ko): same PoC, root-started daemon ---
daemon sends FUSE_READ reply: actual_write=12288, claimed ohd->len=4112
consumer: memcpy(bp->b_data, fuse_out_data, fuse_out_data_size=12288-16=12272)
  panic: assertion "obj != NULL" failed in vm_object_hold_shared at vm_object.c:330
  Trace: vm_object_hold_shared -> vm_fault -> trap_pfault -> trap -> memcpy+0xfa
  (page fault DURING the overflow memcpy; bp->b_data + 8176 overran into a
   corrupted vm_object pointer; VM DOWN at db> prompt)

--- AFTER (patched fuse.ko, ohd->len<=fb.len check + fb.len=ohd->len clamp) ---
daemon log (exact same malicious trigger):
  [daemon] REQ opcode=15(FUSE_READ) unique=6 nodeid=2 size=8192
  [daemon]   FUSE_READ -> replying 12288 actual bytes claiming ohd->len=4112
  [daemon]   reply ... actual_write=12288 claimed_ohd->len=4112 <<<<< DF-0915 TRIGGER
  [daemon]   >>> malicious READ reply sent; kernel memcpy will overflow ... <<<<
  [child] read returned 4096 (errno=File exists); if we got here, kernel may already be corrupted
  [child] child exiting rc=0
vm status after run: UP (no panic)
boot.log: only "FUSE ABI 7.28" / "FUSE UABI 7.28" — NO panic, NO assertion.

WHY IT'S FIXED: the clamp fb.len = ohd->len = 4112 makes fuse_out_data_size()
  = fb.len - 16 = 4096 (the daemon's CLAIMED data length), not 12272 (the
  actual write). The consumer memcpy(bp->b_data, fuse_out_data, 4096) now fits
  the request-sized bp->b_data exactly. The 8176 overflow bytes the daemon
  appended are simply never copied. read() returns the 4096 claimed bytes.

VERDICT: fix_status = fixed. Baseline (unpatched) panics; patched does not.