# DF-0842 — Missing sliding-window allocation in inflate `updatewindow()`

**Severity:** Medium (local DoS / NULL-pointer-write kernel panic)
**CWE:** CWE-476 (NULL Pointer Dereference)
**File:** `sys/vfs/hammer2/zlib/hammer2_zlib_inflate.c` (vendored zlib inflate)
**Kernel:** DragonFly 6.5-DEVELOPMENT, `X86_64_GENERIC` (HAMMER2 is the root filesystem)

## The bug

HAMMER2 ships a vendored copy of zlib whose `inflate` never allocates the
sliding window. `state->window` is set to `Z_NULL` in `inflateInit2_()`
(`:200`) and is only ever referenced by `kfree()` (`:174`, `:1047`) and by
`updatewindow()` (`:382`, `:389`, `:392`) — **no `kmalloc()` exists for it
anywhere in the file.** Upstream zlib 1.2.8 has an explicit
`if (state->window == Z_NULL) state->window = ZALLOC(...)` block inside
`updatewindow()`; that block was dropped when zlib was vendored into HAMMER2.
(The sibling `hammer2_zlib_deflate.c:254` *does* correctly `kmalloc()` its
window, confirming the asymmetry.)

When `inflate()` is driven with a stream that **produces output but exits
before the CHECK trailer** (e.g. truncated input, or output buffer exhaustion
mid-decode), the `inf_leave` guard at `:1018-1019` is TRUE and calls
`updatewindow()`, which `zmemcpy()`s through `state->window == NULL` →
**write to virtual address 0x0 → fatal page fault (trap 12).**

## Reachability (unprivileged)

`read(2)` of a HAMMER2 file whose on-disk block is ZLIB-compressed reaches
`hammer2_strategy_read_completion()` (`hammer2_strategy.c:458-484`) →
`hammer2_decompress_ZLIB_callback()` (`:229`, calls `inflate(&strm, Z_FINISH)`
at `:257`). A malformed block (e.g. a ZLIB stream that decompresses past the
16 KB output buffer, or a truncated stream) triggers the NULL-window write.
The root filesystem of the audit guest is itself HAMMER2, so this code path
is live in the default GENERIC kernel. Realistic precondition (per the audit
threat model): an admin mounts / makes mountable an attacker-controlled
HAMMER2 image; the unprivileged user then reads the malformed file.

## Reproduction

Two independent triggers, both confirmed against the unpatched `#0` kernel:

### 1. Deterministic harness (`df0842_harness.c` + `Makefile`, `build.sh`/`run.sh`)

A kernel module that calls the **real in-kernel** `z_inflate*` symbols (the
exact functions used by `hammer2_strategy.c:257`) on a 20-byte truncated zlib
stream that decodes ~1.8 KB of output before the input is exhausted.
`kldload` → fatal trap 12, fault VA = 0x0, `memcpy+0xfa`.

### 2. Real unprivileged filesystem-image trigger (`make_crafted_image.py`)

A HAMMER2 image is built with `setcomp zlib` + `setcheck none`; a data block's
ZLIB stream is replaced with one that decompresses to 200 KB (≫ the kernel's
16 KB output buffer). `cat <file>` **as the unprivileged `maxx` user**
panics the kernel at the identical RIP (`memcpy+0xfa`, VA = 0x0). See
`make_crafted_image.py` for the generator and `VERDICT.md` for the full
reachability chain.

## Build / run

```sh
# harness (deterministic):
ssh dfbsd     # root
cd /root/DF-0842 && make SYSDIR=/usr/src/sys KERNBUILDDIR=/usr/obj/usr/src/sys/X86_64_GENERIC
kldload ./df0842_harness.ko     # unpatched: panic; fixed: prints "NO PANIC"

# real unprivileged trigger:
# host: python3 make_crafted_image.py base.img crafted.img
# guest(root): vnconfig + mount crafted.img, chmod a+rX
# guest(maxx): cat /h2mnt/zd/big.bin        # unpatched: panic; fixed: clean read
```

## Impact

NULL-pointer-write at a fixed address (page 0 is unmapped in the kernel) →
**local denial of service (kernel panic)**. No escalation chain: the fault is
a write to VA 0x0 which cannot be redirected to an attacker-controlled page.
On a default GENERIC kernel (INVARIANTS ON) the panic is immediate.

## Fix

`fix.diff` restores the missing allocation block in `updatewindow()`,
matching upstream zlib 1.2.8 (using `kmalloc(..., C_ZLIB_BUFFER_INFLATE,
M_INTWAIT)` to mirror the existing inflate-state and deflate-window
allocations). Validated: on the patched `#1` kernel both triggers behave
gracefully (no panic); the malformed block yields a handled decompression
error (`HAMMER2 ZLIB: Fatal error during decompression.`) instead of a crash.
