# DF-3152 — HAMMER1 volume-add trusts ioc->vol_size; no device-size validation + KKASSERT error paths (deterministic kernel panic / silent half-add)

## What

`hammer_ioc_volume_add` (sys/vfs/hammer/hammer_volume.c:63) accepts three
fully attacker-controlled int64 fields from the privileged ioctl caller:
`vol_size`, `boot_area_size`, `memory_log_size`.
`hammer_format_volume_header` (hammer_volume.c:616) turns `vol_size` into
`vol_buf_end` with only a signedness check (`HAMMER_VOL_BUF_SIZE < 0`,
hammer_volume.c:664 — DF-0869 covers the *arithmetic overflow* variant).
The **claimed size is never compared against the actual size of the device
that gets opened** by `hammer_install_volume` (hammer_ondisk.c:109 — no
DIOCGPART/media_size check anywhere on the HAMMER1 add path; contrast
HAMMER2 which validates this: sys/vfs/hammer2/hammer2_ondisk.c:283-291).

When the claim exceeds the real device, `hammer_format_freemap`
(hammer_volume.c:382) bread()s layer2 entries at device offsets
[vol_buf_beg .. vol_buf_beg+8MB) — walking past the device end. The
clustered read completes against the slice bounds check and the kernel
panics in `cluster_callback` ("unexpected EOF"), reproducibly, from a
single ioctl.

Even when the I/O error is returned cleanly instead of panicking, the add
path cannot handle it: `KKASSERT(error == 0)` at hammer_volume.c:118/122/123
panics INVARIANTS kernels, and on production kernels the code **continues
and overwrites the error with `hammer_update_volumes_header`'s unconditional
0** (hammer_volume.c:673-739 — `error` is never set) — the ioctl returns
**success** with a half-formatted volume installed, `nvolumes` incremented,
and layer1 entries on the root volume already pointing at unreadable layer2
blocks on the new volume. Every later allocation that dips into that
volume's range fails (fs wedge), and `volume-del` of that volume then hits
`KKASSERT(layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL)` /
bread-error KKASSERTs (hammer_volume.c:541/577, 232).

## Privilege required

`HAMMERIOC_ADD_VOLUME` is gated twice by `caps_priv_check(cred,
SYSCAP_NOVFS_IOCTL)` (hammer_ioctl.c:72,197) → root-equivalent. Impact is a
privileged-user-triggered kernel panic (deterministic, INVARIANTS kernels)
or silent filesystem corruption/wedge (non-INVARIANTS kernels). Root can
also hit this *accidentally* (transient device I/O error mid-add, e.g. USB
yank — the error-path discipline bug, not the lie).

## How to reproduce (on the audit QEMU guest)

```
# 1. 12 GiB sparse backing file + HAMMER1 fs on vn0; 2 MiB device on vn1
mkdir -p /root/h1test /mnt/h1
dd if=/dev/zero of=/root/h1test/base.img bs=1m count=0 seek=12288
dd if=/dev/zero of=/root/h1test/small.img bs=1m count=2
vnconfig -c vn0 /root/h1test/base.img
vnconfig -c vn1 /root/h1test/small.img
newfs_hammer -f -L test /dev/vn0
mount_hammer /dev/vn0 /mnt/h1

# 2. build + run the trigger (claims vol_size = 2 GiB on the 2 MiB vn1)
cc -O -I/usr/src -o voladd_lie voladd_lie.c
./voladd_lie /mnt/h1 /dev/vn1 2147483648
```

## Expected result

Serial console (vm.sh log):

```
HAMMER(test) Initialize freemap volume 1
panic: cluster_callback: unexpected EOF on cluster 0xfffff8004f485cb0!
cpuid = 0
Trace beginning at frame ...
cluster_callback() at cluster_callback+0x192
vnstrategy() at vnstrategy+0x125
...
Debugger("panic")
db>
```

Observed twice (run.log, run.2.log), deterministic. On a non-INVARIANTS
kernel the failure instead surfaces as the ioctl returning success with a
broken volume installed (see VERDICT.md).

## Fix

`fix.diff` (not applied to sys/): DIOCGPART size check in
`hammer_install_volume` (mirroring HAMMER2), plus real error unwinding in
`hammer_ioc_volume_add` instead of KKASSERTs + error overwrite.
