# DF-0780 — Heap buffer overflow in `fuse_io_execute` READ (oversized daemon reply)

Reproduction + fix-validation evidence pack.

## What this proves

`fuse_io_execute()` (`sys/vfs/fuse/fuse_vnops.c:2054`) copies a FUSE daemon's
READ reply into a kernel buffer-cache buffer with
`memcpy(bp->b_data, fuse_out_data(fip), fuse_out_data_size(fip))` and **no
check** that `fuse_out_data_size <= bp->b_bcount`.  The only existing guard,
`fuse_audit_length()` in `fuse_device_write()`, is advisory: on failure it
returns `EPROTO` to the daemon's `write()` only, while still completing the
IPC with `ohd->error == 0`; so `fuse_ipc_tx()` returns success and the
unchecked `memcpy` runs with the daemon-chosen length.  A daemon that replies
with more bytes than requested overflows `bp->b_data`.

## Files

* `evil_daemon.c` — self-contained raw `/dev/fuse` malicious daemon.  Mounts
  a synthetic FUSE fs (`/mnt/fuse`) exposing `target` (8192 bytes) and
  answers every `FUSE_READ` with **131072** bytes.
* `build.sh`  — `cc -O0 -g -o evil_daemon evil_daemon.c`
* `run.sh`    — loads `fuse`, starts the daemon (root), then reads the file
  as `maxx` to trigger the overflow.
* `fix.diff`  — consumer-side bound check in `fuse_io_execute` (validated).
* `build.log` / `run.log` / `fix_run.log` / `fix_build.log` / `panic.txt` /
  `env.txt` / `VERDICT.md` / `manifest.json`.

## Reproduce (panic / proof of overflow)

On the audit guest (unprivileged `maxx` exists; root needed only to load the
module and open `/dev/fuse`):

```sh
# as root, on the guest:
cp /usr/obj/usr/src/sys/X86_64_GENERIC/usr/src/sys/vfs/fuse/fuse.ko /root/fuse.ko
# as maxx:
cd poc/DF-0780 && sh build.sh
# as root:
sh run.sh            # maxx reads /mnt/fuse/target -> kernel panics
```

Expected on the **unpatched** kernel: guest dies, `dfbsd-qemu/boot.log` shows
```
panic: assertion "obj != NULL" failed in vm_object_hold_shared ...
--- trap 000000000000000c ---
memcpy() at memcpy+0xfa
```

## Validate the fix

```sh
scp fix.diff dfbsd:/root/fix.diff
vm.sh run_root 'cd /usr/src && patch -p1 --forward < /root/fix.diff'
vm.sh run_root 'cd /usr/src/sys/vfs/fuse && make obj && make && \
                cp fuse.ko /root/fuse.ko'
vm.sh run_root 'cd /usr/src && nohup sh -c "make -j6 nativekernel \
                KERNCONF=X86_64_GENERIC; echo done" </dev/null >/root/nk.log 2>&1 &'
# …poll /root/nk.log for "done"…
vm.sh run_root 'cp /usr/obj/.../kernel.stripped /boot/kernel/kernel; \
                cp /usr/obj/.../kernel.debug /boot/kernel/kernel.debug'
vm.sh down && vm.sh up
sh run.sh           # maxx reads -> cat: Invalid argument, NO panic, guest up
```

## Threat model / scope (read VERDICT.md for detail)

The trigger is root-only on default DragonFly (kldload + `/dev/fuse`
root:operator 0660 + mount capability check all require root), so this is a
root→kernel hardening gap / DoS, **not** an unprivileged→root escalation on
a default system.  The bug is a genuine memory-safety defect and is fixed.
