# DF-2879 — phantom swap tail: swaponvp() round-up registers swap pages beyond the device end

* **File:** `sys/vm/vm_swap.c` (`swaponvp`, lines 365–383; sibling fill-loop in
  `swapoff_one`, lines 539–544)
* **Class:** incorrect resource accounting / out-of-bounds swap registration →
  system-wide DoS (pageout error-retry livelock)
* **Trigger:** root `swapon` of any block device whose PAGE_SIZE block count is
  not a multiple of `SWB_DMMAX` (64 pages = 256 KiB)
* **Buckets:** base:vm / DoS

## Build

```
cc -O2 -o /tmp/hog /tmp/hog.c        # guest, base system cc
```

No kernel changes are needed for the reproduction.

## Run

```
./trigger.sh        # registration proof: 520-block device registered as 1024
./run.sh            # consequence proof: phantom pageout I/O-error livelock
```

## Expected output (stock kernel)

`trigger.sh`:

```
/dev/vn1  blksize=512 ... size=0x000000000041000  260.00 KB   <- 520 512-blocks
Device:       512-blocks      Used:
/dev/vn1            1024          0                                <- registered 2x real size
vm.swap_size: 1048576    (= 1048512 primary + 64 from vn1, of which 1 page is real)
```

`run.sh` (primary swap removed; 5.2 GB hog): the pageout daemon retries the
same phantom blocks forever; serial console fills with

```
swap_pager: I/O error - pageout failed; offset 1368064,size 65536, error 22
```

(25,712 lines in run 1, 16,000+ in run 2) and the guest stops responding
(ssh commands hang, `dmesg` fails with `Cannot allocate memory`, `vm.sh
status` → down).  Offset 1368064 / 4096 = interleaved block 334 →
seg 5 → device index 1 (vn1) → device page 78 of a **65-page** device.

## Patched kernel (fix.diff applied)

`trigger.sh` registers vn1 with its real 520 blocks (`vm.swap_size` gains
exactly 1 page, the only real page in the allocatable stripe); `run.sh` shows
no `I/O error - pageout failed` lines — the pager correctly reports swap full
instead of paging to nonexistent blocks — and the guest stays responsive.
