# DF-2624 — kstrdup(ripdata->filename) with no NUL-termination guarantee overreads the 64KB DIO buffer

* Cited: `sys/vfs/hammer2/hammer2_vfsops.c:495` (`hammer2_pfsalloc`)

## What this pack contains

| file | what |
|---|---|
| `forge_2624.py` + `h2common.py` | image forger: poison PFS inode with non-NUL filename, relocated to a 64KB-window tail |
| `run_2624.sh` | guest trigger (mount → hammer2_update_pmps → pfsalloc) |
| `run_stock.log` | stock kernel #0 run (silent overread; mount succeeds) |
| `console_excerpts.txt` | instrumented `len=772` print, the `strlen ← hammer2_pfsalloc+0x157` panic trace, fix-kernel warnings |
| `fix.diff` | bounded copy (strnlen + kmalloc + bcopy + warning) |

## Build

1. Guest (root): base image — `newfs_hammer2 -L testvol`, mount, one
   file, `hammer2 pfs-create /mnt/h2x/poison` (creates the second PFS the
   forger poisons), sync, umount; pull to host.
2. Host: `python3 forge_2624.py base2624.img craft2624.img`
   * relocates the poison PFS inode block to `WIN|0xFC00|10`
     (1024-byte block ending exactly at the 64KB buffer end),
   * fills `[0x100,0x400)` (the whole `filename[256]` array + the inode
     blockset area) with 0x50 — **no NUL anywhere before the buffer end**,
   * poison bref: mirror_tid=0 (no recovery recursion), CHECK_NONE;
     sroot bref CHECK_NONE; volhdr CRCs recomputed.
3. Push `craft2624.img` to guest.

## Run (root)

```sh
vnconfig -c vn0 craft2624.img
mount -o ro -t hammer2 /dev/vn0@testvol /mnt/h2x
hammer2 pfs-list /mnt/h2x        # shows the poison PFS
```

## Expected

`hammer2_update_pmps()` (vfsops.c:1552-1571) pfsallocs **every** PFS under
the sroot on any mount, so `pfsalloc()` runs
`kstrdup(ripdata->filename)` with no NUL in the 256-byte array, walking
through the blockset filler and off the end of the 64KB DIO buffer:

* stock, adjacent page mapped (observed): silent — kstrdup result was
  772 bytes = 768 in-block + **4 bytes past the DIO buffer** (instrumented
  print), copied into `pmp->pfs_names[0]`;
* adjacent page unmapped (observed on the instrumented kernel; the
  faulting strlen is the stock kstrdup — the instrumentation only adds a
  post-return report): **kernel panic**
  `strlen() at strlen+0x14 ← hammer2_pfsalloc() at +0x157`,
  fault VA page-granular at the DIO buffer end;
* fixed kernel: 3/3 mounts succeed, no panic, console warns
  `hammer2_pfsalloc: PFS filename not NUL-terminated (truncated to 256)`.

Userspace observability of the overread bytes on stock: none found —
thread names are truncated to MAXCOMLEN (16) and `hammer2 pfs-list`
prints the on-disk name via `meta.name_len`; the copied heap bytes stay
in kernel memory (`pmp->pfs_names[]`).  The demonstrated impact is the
mount-time panic (memory-layout dependent) plus the bounded heap overread.
