# DF-2623 VERDICT

**Status: reproduced (use-after-release read confirmed with pointer provenance; observable stale content NOT produced). Impact: none. Confidence: certain (mechanics).**

## Root cause (line-precise)

```c
sys/vfs/hammer2/hammer2_vfsops.c:2399-2406   (hammer2_fixup_pfses)
		ripdata = &chain->data->ipdata;                 /* :2399 into DIO */
		hammer2_trans_init(hmp->spmp, 0);
		error2 = hammer2_chain_modify(chain,            /* :2401 COW */
					      chain->bref.modify_tid, 0, 0);
		if (error2 == 0) {
			kprintf("hammer2: Correct mis-flagged PFS %s\n",
				ripdata->filename);           /* :2405-06 UAR read */
```

`hammer2_chain_modify()` for a first-time-modified INODE chain takes the
COW path (`newmod=1`, chain.c:1503-1522 — INODE chains never qualify for
overwrite-in-place), allocates a new block, and then:

```c
sys/vfs/hammer2/hammer2_chain.c:1797   io_bread(new data_off)         -> new dio
sys/vfs/hammer2/hammer2_chain.c:1827   bcopy(chain->data, bdata, bytes)
sys/vfs/hammer2/hammer2_chain.c:1858   hammer2_io_bqrelse(&tio);       /* OLD dio released */
sys/vfs/hammer2/hammer2_chain.c:1859   chain->data = (void *)bdata;    /* redirected */
```

`ripdata` still points into the **old** buffer after :1858; the kprintf
at :2405 reads through it.  Reachability: `hammer2_fixup_pfses()` runs on
every first RW mount of a device (vfsops.c:1336-1338, after
`hammer2_recovery()`) and on RO→RW remount (:1605); the kprintf arm
requires a PFS inode bref under the sroot with PFSROOT cleared — trivially
forged (`forge_2623.py`), and the very condition the function exists to
repair ("earlier H2 implementations" bug).

## Observed

1. Stock kernel #0 (`run_stock.log`, `console_excerpts.txt` [A]): RW
   mount of the forged image prints `hammer2: Correct mis-flagged PFS
   testvol`; a second mount prints nothing (the fixup re-set the flag and
   flushed it — the repair semantics work).
2. Instrumented kernel #1 (`console_excerpts.txt` [B]):
   `DF2623: ripdata=0xfffff80051cc6800 vs chain->data=0xfffff80051cc7000
   (same=0) name-by-old-ptr="testvol" name-by-new-ptr="testvol"` —
   **the kprintf's source pointer is not the chain's data anymore**;
   the old dio's reference was dropped inside modify.  On this run the
   COW allocated the new block in the same 64KB window (0x800 apart) and
   the old buffer still holds the bcopy'd content, so the name printed
   correctly.
3. Repeated attempts never produced garbage: `hammer2_io_bqrelse`
   (io.c:676 → putblk) returns the buffer to the cache with its data
   intact, and nothing issues I/O into that buffer between :1858 and
   :2405 within the same call chain.

## Honest classification

The **read-after-release is real and demonstrated** (ordering +
pointer provenance, instrumented).  The *claimed worse face* — printing
recycled buffer content — did not manifest: the release window is
sub-microsecond, the buffer cache retains released contents, and
recycling requires unrelated I/O to land in that exact window.  Impact:
none observable; a theoretical info-leak-to-console (root console) at
worst.  Low severity as filed — correct.

## Fix

```diff
 			kprintf("hammer2: Correct mis-flagged PFS %s\n",
-				ripdata->filename);
+				chain->data->ipdata.filename);
```

The COW copied the inode data (chain.c:1827), so the post-modify
`chain->data->ipdata.filename` holds the identical string in memory the
chain still references.

## Fix validation (kernel #2)

Freshly re-forged image, RW mount: `hammer2: Correct mis-flagged PFS
testvol` prints once (identical text, now from the valid buffer), second
mount silent, directory lists normally (`console_excerpts.txt` [C]).
