# DF-0877 — ext2_dx_csum OOB heap read via unvalidated htree entry count

**Status:** REPRODUCED | **Impact:** leak (info-leak / potential panic) | **Confidence:** certain

## Summary

`ext2_dx_csum_verify` (`sys/vfs/ext2fs/ext2_csum.c:268`) reads `count`
(`h_entries_num`) and `limit` (`h_entries_max`) from disk. It validates
`limit*8 <= bsize-tail` but NEVER checks `count <= limit`. The unvalidated
`count` drives a CRC32C read of `32 + count*8` bytes from a `bsize`-byte
directory block buffer. With `count=65535`, that is a **524312-byte read —
523288 bytes (511 KB) past a 1024-byte buffer**.

**Reachable POST-mount by ANY unprivileged user** with read permission on the
mountpoint: `ls`/`stat`/`readdir` triggers
`ext2_blkatoff → ext2_dir_blk_csum_verify → ext2_dx_csum_verify → ext2_dx_csum`.

## Files

| File | Description |
|------|-------------|
| `harness.c` | Deterministic C harness transcribing `ext2_dx_csum` verbatim |
| `craft_img.py` | Host-side image crafter (mke2fs + debugfs + binary patch) |
| `df0877.ext2` | 4MB crafted ext2 image with poisoned htree count=65535 |
| `df0877_control.ext2` | Control image with count=1 (legitimate) |
| `df0877_clean.ext2` | Clean ext2 image (no poisoning, for regression) |
| `build.sh` / `run.sh` | Exact build/run commands |
| `fix.diff` | Git-apply-able fix: `if (count > limit) return EIO;` |
| `VERDICT.md` | Full narrative with path:line citations |

## Reproduce

### A) Deterministic harness (no kernel required)

```sh
./build.sh && ./run.sh
```
Expected: CASE B shows `OOB read = 523288 bytes (511 KB) past the buffer`;
CASE C shows `SIGSEGV during dx_csum OOB read` at the predicted page boundary.

### B) In-kernel (root mounts, unprivileged triggers)

Host:
```sh
python3 craft_img.py df0877.ext2 4
scp df0877.ext2 dfbsd:/root/
```

Guest (root setup):
```sh
kldload ext2fs
vnconfig -c vn0 /root/df0877.ext2
mkdir -p /mnt/t1
mount_ext2fs -o ro /dev/vn0 /mnt/t1
```

Guest (unprivileged maxx):
```sh
ls /mnt/t1/testdir/
# ls: /mnt/t1/testdir/: Input/output error   (EIO — OOB read happened during csum verify)
```

### C) Fix validation (Phase 8)

```sh
# Apply fix, rebuild module, hot-swap:
scp fix.diff dfbsd:/root/
ssh dfbsd 'cd /usr/src && patch -p1 < /root/fix.diff'
ssh dfbsd 'cd /usr/src/sys/vfs/ext2fs && make'
ssh dfbsd 'kldunload ext2fs; cp /usr/obj/usr/src/sys/vfs/ext2fs/ext2fs.ko /boot/kernel/ext2fs.ko; kldload ext2fs'
# Re-run: ls /mnt/t1/testdir/ still returns EIO but the 520KB OOB read is prevented
# (count>limit early return at ext2_csum.c:285, before ext2_dx_csum is called)
```

## Fix

`if (count > limit) return EIO;` in `ext2_dx_csum_verify` (and `return;` in
`ext2_dx_csum_set`), after the existing limit check, before calling
`ext2_dx_csum`. See `fix.diff`.
