# DF-0832 — Off-by-one OOB read in udf_bmap_internal ICB iteration

## Bug
`udf_bmap_internal` (`sys/vfs/udf/udf_vnops.c`) iterates an ICB allocation-
descriptor list with a strict `>` bound check (`if (ad_offset > fentry->l_ad)`).
When `l_ad` is an exact multiple of `sizeof(short_ad)` (8) or `sizeof(long_ad)`
(16), the iteration where `ad_offset == l_ad` passes the check and the
subsequent `GETICB()` dereferences `&fentry->data[l_ea + l_ad]` — one
descriptor PAST the end of the allocation-descriptor area. This is a heap
OOB read of 8 bytes (short_ad) or 16 bytes (long_ad).

## Severity
Medium — OOB read in filesystem image parsing (mount-time / bmap threat model).
No write primitive; ceiling is info-leak / DoS.

## Files
- `harness.c` — userspace deterministic harness (replicates the verbatim loop)
- `udf_oob_kmod.c` — in-kernel kld module (deterministic, real slab allocator)
- `make_image.py` — Python UDF image builder
- `df0832.udf` — pre-built crafted UDF image
- `fix.diff` — the fix (`>` → `>=`)
- `build.sh` / `run.sh` — build/run the userspace harness

## Reproduce
```sh
./build.sh && ./run.sh
# Expected (bug present): "OOB read detected: YES", exit 1
```

### In-kernel module (stronger proof)
```sh
# as root, on the guest:
cd /tmp/kmod && make  # (Makefile alongside udf_oob_kmod.c)
kldload ./udf_oob.ko
dmesg | grep DF-0832
# Expected: "DF-0832: OOB READ DETECTED ..."
kldunload udf_oob
```

### Real UDF image (actual kernel code path)
```sh
# as root:
vnconfig -c vn0 df0832.udf
mount_udf -o rdonly /dev/vn0 /mnt
dd if=/mnt/target bs=1 skip=2048 count=1  # -> EINVAL
dmesg | tail  # -> "File offset out of bounds"
```

## Fix
`sys/vfs/udf/udf_vnops.c` lines 1104 and 1128: change `>` to `>=`.
See `fix.diff`.
