# DF-0265 — PoC: heap OOB read in zlib 1.0.4 inflate when windowBits < 15

Verbatim copy of the audited `sys/net/zlib.c` (and `sys/net/zlib.h`), plus a
userspace harness that links them unmodified and drives inflate with a crafted
raw-DEFLATE stream. A guard page placed before every zlib allocation surfaces
the sliding-window underflow as a SIGSEGV — conclusive proof of the missing
distance-bounds check.

## Build
```
./build.sh          # cc -O2 -Wall -include kshim.h -o harness harness.c zlib.c
```
`kshim.h` only stubs out the kernel-only `MODULE_VERSION` / `MAX` tokens that
`zlib.c` references at file scope; it does **not** touch the inflate code
under test (the file is byte-identical to `sys/net/zlib.c`).

## Run
```
./run.sh            # vulnerable config: windowBits=-8 (256-byte window)
./run.sh -15        # control:           windowBits=-15 (32KiB window), no OOB
```

## Expected (bug present), windowBits=-8
```
[harness] *** SIGSEGV/SIGBUS during inflate at 0x800480f01 ***
[harness] nearest alloc #2 [user=0x800481000 size=256 (0x100)]: fault is -255 bytes (BEFORE-start (underflow into guard page))
[harness] RESULT: OOB READ CONFIRMED -- inflate dereferenced memory outside an allocation ...
```
The fault address is **255 bytes before the 256-byte sliding window** — i.e.
inflate read `s->end - (dist - (q - s->window))` with `dist` far larger than
the window and underflowed past `s->window` into the guard page.

## Expected, windowBits=-15 (control)
```
[harness] inflate returned 1, msg=(null), total_out=916
```
The 32 KiB window swallows the stream's distance (658) cleanly — same code,
same stream, no OOB. This isolates the cause to the missing `dist <= window`
check at small `windowBits`.

## With the fix applied (see `fix.diff`)
```
windowBits=-8 : inflate returned -3, msg=invalid distance too far back   # rejected, no OOB
windowBits=-15: inflate returned 1,  total_out=916                       # still decodes cleanly
```

## Files
| file | purpose |
|------|---------|
| `harness.c`        | guard-page allocator + inflate driver; identifies the faulting allocation |
| `zlib.c`,`zlib.h`  | verbatim copies of the audited `sys/net/zlib.c` / `sys/net/zlib.h` |
| `kshim.h`          | userspace stubs for the kernel-only `MODULE_VERSION` / `MAX` tokens (`-include`) |
| `build.sh`,`run.sh`| exact reproduce commands |
| `gen_stream.py`    | how the crafted raw-DEFLATE stream embedded in `harness.c` was produced |
| `build.log`        | final successful build output |
| `run.log`          | decisive vulnerable run (wbits=-8), full output |
| `run.control.log`  | control run (wbits=-15), full output |
| `run_stress.log`   | 3 repeat runs (deterministic) |
| `env.txt`          | guest uname / cc / kldstat / module availability |
| `fix.diff`         | git-apply-able unified diff adding the distance-bounds check |
| `VERDICT.md`       | full narrative analysis |
| `manifest.json`    | machine-readable artifact catalog |

See `VERDICT.md` for the full root-cause analysis, reachability on the audited
kernel, and the threat model.
