# DF-2720 — `load_section` file-size check bypassed by 64-bit wrap (`(off_t)filsz + offset`)

- **File**: sys/kern/imgact_elf.c:274
- **Severity**: Low (check bypass; no memory-unsafety demonstrated)
- **Confidence**: certain (bypass reproduced; impact assessed none)
- **Class**: CWE-190 integer overflow / hardening

## Root cause

```c
if ((off_t)filsz + offset > vp->v_filesize || filsz > memsz)   /* imgact_elf.c:274 */
```

`offset` is `vm_offset_t` (unsigned 64-bit), so the addition is computed
modulo 2^64 regardless of the `(off_t)` cast. With
`p_offset = 0xFFFFFFFFFFFFF000` and `p_filesz = 0x2000` the sum wraps to
`0x1000`, which is ≤ file size → the "truncated ELF file" check passes and
the segment is mapped file-backed with object pindex
`0xFFFFFFFFFFFFF000 >> 12` (~2^52 pages past EOF).

## Reproduce

```
python3 make_wrapseg2.py  # t2_wrapfile / t2_wrapanon + controls
# guest: /tmp/t2_wrapfile ; echo $?   -> 139 (SIGSEGV)
#        /tmp/t2_wrapanon ; echo $?   -> 65   (anon tail reads zero)
# controls: t2_readfile -> 66, t2_readanon -> 65, t2_readtext -> 66
```

Exec **succeeds** (no "elf_load_section: truncated ELF file" uprintf —
contrast with a genuinely-too-large p_filesz, which prints it), the wrapped
file-backed range is present in the map (visible in the process core:
`vaddr=0x600000 filesz=0x2000`), and touching it SIGSEGVs (vm_fault on a
vnode page far beyond EOF fails instead of zero-filling). The anon tail maps
and reads as zero.

## Impact

None demonstrated beyond a self-inflicted SIGSEGV: the pager handles the
beyond-EOF pindex gracefully on this guest (no INVARIATS panic, no leak, no
corruption). The check exists precisely to prevent walking "off the end of
the file object" — the wrap defeats it, so this is defense-in-depth.

## Fix

Detect the wrap:

```diff
-	if ((off_t)filsz + offset > vp->v_filesize || filsz > memsz) {
+	if (offset + filsz < offset || offset + filsz > vp->v_filesize ||
+	    filsz > memsz) {
```
