# DF-2731 VERDICT

**Status: reproduced (impact: panic — unprivileged kernel DoS, config ext2 mount).**
**Fix: validated (fixed).**

## Chain (baseline, stock kernel #0, INVARIANTS X86_64_GENERIC)

`pread(fd, buf, 8, (off_t)-5)` from uid 1001 on a world-readable file on a
root-mounted ext2 filesystem:

```
sys_extpread()            sys/kern/sys_generic.c:153   — no offset<0 validation
  auio.uio_offset = uap->offset (-5)                   :168
  flags |= O_FOFFSET (offset != -1)                    :175-176
kern_preadv()             sys/kern/sys_generic.c:252   — passes uio through
vn_read()                 sys/kern/vfs_vnops.c:729     — O_FOFFSET set: keeps uio_offset=-1
vop_read -> ext2_read()   sys/vfs/ext2fs/ext2_vnops.c:1842
  KASSERT(uio->uio_offset >= 0)  -> panic
```

Console proof: `panic.txt` (libc pread form) and `panic2.txt`
(documented-API form `extpread(fd, buf, n, O_FOFFSET, -1)` — the O_FOFFSET
flag defeats the `-1` file-position convention at sys_generic.c:175 because
the `!= -1` test fails, but the flag is honored at :174/:238/:381/:442).
Both panics observed from an unprivileged account; guest wedged in DDB
(`vm.sh status` ⇒ down) — two independent boots.

## Why the syscall layer is the defect locus
The offset reaches the VFS raw on every filesystem; only incidental
filesystem defenses stopped it on hammer2 (EINVAL/EFAULT/EFBIG returned from
cluster/chain internals — see run.baseline_hammer2.log), and ext2's only
guard is a debug assert. POSIX (pread(2)): offset<0 ⇒ EINVAL.

## Fix validation A/B
* Baseline stock kernel #0: panic (both forms). Guest down.
* Patched kernel #1 (`fix.diff`: 4×offset-EINVAL in sys_generic.c +
  KASSERT→EINVAL in ext2_vnops.c read+write; ext2fs.ko rebuilt):
  - ext2: libc `pread(-5)` → EINVAL; raw `(flags=0, offset=-5)` → EINVAL;
    raw `(O_FOFFSET, offset=-1)` → EINVAL (ext2 hunk catches the residual
    documented-API path). Guest stayed up through all legs
    (run.patched_kernel.log, run.patched_kernel.2.log).
  - hammer2: every negative-offset leg now EINVAL at the syscall layer.
  - No documented-API regression: `extpwrite(flags=O_FAPPEND, offset=-1)`
    still appends at EOF (ret=7, size 20→27).
* Guest reset to stock (`vm.sh reset with-src`) after validation.

## Exploitability ceiling
Panic/DoS, not memory corruption: on this INVARIANTS kernel the assert fires
before any block math. On RELEASE kernels the negative offset flows into
ext2's `lblkno`/`bread` path and hammer2's `hammer2_write_file` (negative
`uio_offset` at hammer2_vnops.c:1046, `new_eof = offset + resid` at :1033) —
all buffer-cache accesses remained in-bounds in testing; no OOB write
primitive was demonstrated, so impact is recorded as panic/dos.
