# DF-5146 — DRM hash tables collapse to a single bucket on 64-bit (local DoS)

## What

`sys/dev/drm/include/linux/hash.h:hash_64()` misuses DragonFly's
`hash32_buf()` (sys/sys/hash.h:44): its third argument is a djb2
**initval**, not a bit-width, and it returns a full 32-bit hash.
`hash_64()` casts that to `u64` and shifts right by `(64 - bits)` —
which is **identically 0 for every bits <= 32**. On amd64
(`hash_long == hash_64`), every key hashed by
`sys/dev/drm/drm_hashtab.c` (drm_ht_find_key:80, drm_ht_find_key_rcu:98,
drm_ht_insert_item:117, drm_ht_just_insert_please:148) lands in
**bucket 0**; the other 4095 buckets of the in-tree order-12 tables
(drm_drv.c:603 `dev->map_hash`, drm_gem.c:148 `mm->offset_hash`) stay
empty forever. All operations degrade to a linear walk of one sorted
hlist: inserts O(n) (append-to-tail for the sequential `alloc_unr`
keys used at drm_gem.c:576), lookups O(n) — and every GEM mmap lookup
runs under the global `DRM_LOCK` (drm_gem.c:1089). Not memory
corruption: the index is always in-bounds (0); correctness of
find/dup-reject/remove is preserved (proven below).

## Reproduce (on the DragonFly guest)

```
# pack copied to /root/df5146 on the guest
cd /root/df5146
sh build.sh     # builds userspace proof + kld twice: baseline + FIX=1
sh run.sh       # runs all three proofs, prints everything
```

Expected (baseline, stock kernel code):
```
RESULT: hash_long(key,bits)==0 for ALL 6400000 inputs ...
HTCOLLAPSE: per-1024-insert wall time (us): 581 1978 3689 ... 11403   <- grows linearly
HTCOLLAPSE: nonempty_buckets=1/4096 max_chain=8192 ...
HTCOLLAPSE: VERDICT ... hash_long collapse CONFIRMED in-kernel
```

Expected (fixed header, `fixinc/linux/hash.h`):
```
HTCOLLAPSE: per-1024-insert wall time (us): 19 18 18 17 18 17 17 18   <- flat
HTCOLLAPSE: nonempty_buckets=4096/4096 max_chain=2 ...
HTCOLLAPSE: VERDICT ... hash distributed normally (fixed header in effect)
```

## Files

- `hash64_collapse.c`  — userspace arithmetic proof (verbatim replication
  of sys/sys/hash.h + sys/dev/drm/include/linux/hash.h)
- `htcollapse.c`       — kernel module harness; Makefile `.PATH`s the REAL
  unmodified `/usr/src/sys/dev/drm/drm_hashtab.c` into the module
- `fixinc/linux/hash.h` — fixed header used by `make FIX=1` (the fix under
  test, byte-identical logic to `fix.diff`)
- `build.sh`, `run.sh` — exact commands
- `build.log`, `run.log`, `run.2.log`, `env.txt` — full untrimmed logs
- `fix.diff`           — git-apply-able fix against sys/dev/drm/include/linux/hash.h
- `manifest.json`, `verdict.json` — machine-readable results
