DF-0134 — l64_readdisklabel lacks structural validation
=======================================================

## Verdict: REPRODUCED (missing structural validation; out-of-slice label accepted)

## Mechanism

`l64_readdisklabel()` (`sys/kern/subr_disklabel64.c:146-201`) validates only:
`d_magic == DISKMAGIC64` (`:183`), `d_npartitions <= MAXPARTITIONS64` (`:185`),
and the CRC (`:187`). On success it **blindly copies the whole on-disk label
into the in-core label** (`:191-193`):

```c
} else {
    dlp->d_crc = savecrc;
    (*lpp).lab64 = kmalloc(sizeof(*dlp), M_DEVBUF, M_WAITOK|M_ZERO);
    *(*lpp).lab64 = *dlp;          /* no bounds check on any partition */
    msg = NULL;
}
```

It performs **none** of the structural validation that the **write** path
`l64_setdisklabel()` (`:278-304`) performs: `d_total_size <= slicebsize`,
per-partition `p_boffset + p_bsize <= d_total_size`, `p_boffset >= d_pbase`,
alignment, and the `p_bsize==0 ⇒ p_boffset==0` rule. `l32_readdisklabel` calls
`l32_fixlabel` which validates partitions against the slice; `l64` has no
equivalent on the read path.

`disk_probe_slice()` (`sys/kern/subr_disk.c:205-210`) tries disklabel32 first
and, on "no disk label", falls back to disklabel64. So a crafted image with no
disklabel32 magic + a CRC-correct disklabel64 with out-of-slice partitions is
loaded verbatim, and the OOB partitions get real device nodes (when `p_fstype`
is set) via `disk_probe_slice` `:228-229`.

## Proof (decisive)

Crafted 32 MiB vnode disk (`/dev/vn0`) carrying a disklabel64 whose partition
'b' has `p_boffset+p_bsize = 100728832` bytes against `d_total_size =
33554432`. After `vnconfig -c`, the in-core label read back via `DIOCGDINFO64`
shows the OOB partition unchanged:

```
[*] in-core disklabel64: magic=0xc4464c59 npart=3 d_total_size=33554432 ...
    part[1]: p_boffset=65536  p_bsize=100663296  end=100728832  *** OUT-OF-SLICE (readdisklabel accepted it!) ***
RESULT: LEAK_CONFIRMED — in-core label contains a partition extending beyond d_total_size
```

The OOB partition device is created and **accessible** — a read of
`/dev/vn0s0b` succeeds (`dd` 4 blocks => 2048 bytes, rc=0). The `vn` backend
clamps reads beyond the backing media (short read / 0 bytes, no panic); a real
block driver would translate the OOB geometry into out-of-slice / cross-
partition I/O.

Contrast: the same out-of-slice label submitted via `DIOCSDINFO64` (the set
path) is **rejected** (`rc=-1`) — confirming the read/set validation asymmetry.

## Impact / realism

* Realistic precondition: a crafted disk image made attachable/mountable
  (admin attaches it; or `vfs.usermount=1` + a root-created image owned by the
  attacker — the accepted audit threat model for FS-image findings). On this
  guest `vfs.usermount=0`, so the demonstration is run as root (vnconfig),
  which is the standard "crafted disk" setup.
* Class: structural-validation / robustness gap. The kernel loads a
  structurally-invalid disklabel64 (partitions beyond the slice) and exposes
  the OOB partitions as accessible devices. On the `vn` backend this does not
  crash (reads clamp); on real media the effect is out-of-slice / cross-
  partition I/O (info exposure / geometry confusion / reserved-area EROFS).
* No memory-corruption primitive is triggered on the default kernel here (the
  `vn` backend bounds-checks against its own size). The realistic default-kernel
  ceiling is the invalid-label acceptance enabling cross-boundary device
  access; "potential offset overflow panic" is config/driver-dependent and was
  not reproduced on `vn`.

## Fix

`fix.diff`: in `l64_readdisklabel`, after the CRC check, bound partitions
against the slice — reject labels whose `d_total_size > slice` or any
non-empty partition with `p_boffset + p_bsize > d_total_size` (and
`p_bsize==0 ⇒ p_boffset==0`). This catches the out-of-slice case while NOT
over-rejecting the legitimate RAW/whole-disk partition or the kernel's own
virgin label, both of which use `p_boffset = 0` (so we deliberately do **not**
check `p_boffset >= d_pbase`, which would wrongly reject them). Supersedes
(specifies) the finding's proposal.

## Reproduce
```
cc -o craft_label craft_label.c     # build.sh (also readback.c)
cc -o readback readback.c
# run.sh (as root): dd image, craft label, vnconfig, readback
```
