DF-0915 / fix_run.log
=== 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.