# DF-0823: hammer2_xop_helper cleanup/create mismatch — UAF on multi-chain PFS unmount

## Summary

`hammer2_xop_helper_create` double-allocates `xop_groups` (no NULL check) when
called a second time from `hammer2_pfsalloc` (vfsops.c:589) after a SLAVE chain
joins a mounted PFS cluster. `hammer2_xop_helper_cleanup` then stops only
MASTER-indexed threads (`pfs_nmasters` instead of `nchains`), leaving SLAVE
threads alive when `kfree(xop_groups)` frees the backing memory. The orphan
threads access freed heap → kernel panic.

## Reproduction

```sh
# As root on the DragonFlyBSD guest:
./trigger_v2.sh
```

The trigger creates a 2-chain hammer2 PFS (1 MASTER + 1 SLAVE), does I/O,
then unmounts — triggering the cleanup loop mismatch and the orphan-thread UAF.

### Expected results

| Kernel | Result |
|--------|--------|
| Unpatched (#0) | `Fatal trap 12: page fault` in `hammer2_primary_xops_thread+0x2d9` within seconds |
| Two-fix (#1) | Clean exit 0, guest stays up indefinitely |

## Fix

Two changes in `sys/vfs/hammer2/hammer2_admin.c` (see `fix.diff`):

1. **Guard xop_groups allocation** in `hammer2_xop_helper_create` — only allocate
   if `xop_groups == NULL`, preventing the double-alloc leak and thread orphaning
2. **Fix cleanup loop** — change `pfs_nmasters` to `cluster.nchains`, matching the
   create loop's range so ALL threads (MASTER + SLAVE) are stopped before kfree

Both changes are required. See `VERDICT.md` for the full analysis.
