{
  "finding_id": "DF-5146",
  "status": "reproduced",
  "reproduced": true,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "REPRODUCED in-kernel on DragonFly 6.5-DEVELOPMENT x86_64: the linux-compat hash_64() (sys/dev/drm/include/linux/hash.h:32-39) misuses hash32_buf()s seed argument and returns identically 0 for every bit-width <= 32, so on 64-bit platforms every drm_ht hash table collapses to a single bucket (verified: 1/4096 buckets non-empty, chain 8192, per-insert time growing linearly => O(n^2) fills) while correctness (find/dup/remove) is preserved. Impact is a reachable local CPU/lock-contention DoS for any user with /dev/dri/card0 (GEM mmap-offset inserts via MODE_CREATE_DUMB/MAP_DUMB + O(n) lookups under the global DRM_LOCK), not memory corruption: the bucket index is constant 0 and always in-bounds, so there is no uid=0 route. Fixed-header rebuild of the same real drm_hashtab.c spreads items over all 4096 buckets with flat insert times (~450-600x faster at the tail), validating fix.diff.",
  "exploit_chain": "unprivileged user opens /dev/dri/card0 -> DRM_IOCTL_MODE_CREATE_DUMB + DRM_IOCTL_MODE_MAP_DUMB repeatedly (drm_gem.c:381-406, drm_gem.c:576-577): each objects sequential alloc_unr key appends to the single bucket-0 chain => O(n) walk per insert, O(n^2) cumulative kernel CPU; every subsequent GEM mmap (drm_gem.c:1074 under global DRM_LOCK at drm_gem.c:1089) and legacy map mmap (drm_vm.c:89-91) burns O(n) under the device-global lock, stalling all GPU clients. Bounded by per-object memory cost; no privilege boundary crossed.",
  "evidence": "run.log (decisive lines: nonempty_buckets=1/4096 max_chain=8192 baseline vs nonempty_buckets=4096/4096 max_chain=2 fixed; per-1024-insert wall time 581 1978 3689 4919 6354 8366 9381 11403 baseline vs flat 19 18 18 17 18 17 17 18 fixed; RESULT: hash_long(key,bits)==0 for ALL 6400000 inputs userspace proof); run.2.log (same numbers, stability run); build.log; htcollapse.c compiles the REAL unmodified /usr/src/sys/dev/drm/drm_hashtab.c; VERDICT.md for the full narrative",
  "kernel_refs": [
    "sys/dev/drm/include/linux/hash.h:32-39",
    "sys/sys/hash.h:44-53",
    "sys/dev/drm/drm_hashtab.c:80",
    "sys/dev/drm/drm_hashtab.c:98",
    "sys/dev/drm/drm_hashtab.c:117",
    "sys/dev/drm/drm_hashtab.c:148",
    "sys/dev/drm/drm_hashtab.c:150-155",
    "sys/dev/drm/drm_drv.c:603",
    "sys/dev/drm/drm_gem.c:148",
    "sys/dev/drm/drm_gem.c:381-406",
    "sys/dev/drm/drm_gem.c:576-577",
    "sys/dev/drm/drm_gem.c:1074",
    "sys/dev/drm/drm_gem.c:1089-1090",
    "sys/dev/drm/drm_vm.c:89-91",
    "sys/dev/drm/drm_bufs.c:125-127",
    "sys/dev/drm/amd/lib/chash.c:398"
  ],
  "poc_changes": "No seed PoC existed (finding produced by this pass-2 audit); pack written from scratch. Iterations needed: (1) module_t -> moduledata_t for DFs DECLARE_MODULE; (2) initialize t0 for -Werror=maybe-uninitialized; (3) generate bus_if.h/device_if.h/pci_if.h via kmod.mk SRCS instead of empty stubs (drmP.h pci prototypes need them); (4-5) provide local drm_err/drm_dbg shims so drm_hashtab.o links without drm.ko; (6) prepend -Ifixinc BEFORE the stock drm include dir so the fixed header actually wins the include race (first fixed run silently used the stock header).",
  "attempts": 6,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64",
  "runtime_sec": 1100,
  "guest_dirty": 1,
  "build_cmd": "cd /root/df5146 && sh build.sh  (kld: env SYSDIR=/usr/src/sys make [FIX=1]; userspace: cc -O2 -o hash64_collapse hash64_collapse.c)",
  "run_cmd": "cd /root/df5146 && sh run.sh  (kldload htcollapse baseline + fixed, dmesg capture)",
  "code_hash": "6150809ea09f37fac8e399f91deff0829788f00356d394cdf63a8f5aed371fa6",
  "notes": "Not a memory-safety bug (index constant 0, in-bounds; proven errors=0 incl. double-remove/reinsert) - no escalation chain, hence no kernel rebuild required by contract; fix validated by rebuilding the same real drm_hashtab.c with the fixed header. Guest has no GPU (no /dev/dri), so the userspace-reachable DoS ceiling is argued from drm_gem.c call paths + measured in-kernel O(n^2) behavior, not driven through card0. i386 unaffected (hash_32 path is structurally correct). Blast radius beyond drm_hashtab.c: amd/lib/chash.c:398,433 hash_64 collapses identically (same fix cures it). i915 execbuffer LUT uses hash_32 and is unaffected. Cross-file note for the drm_gem.c audit: drm_gem.c:397 insert path lacks visible serialization vs the drm_hashtab.h:64-72 contract.",
  "recommended_fix": "Rewrite hash_64() in sys/dev/drm/include/linux/hash.h with the canonical 64-bit Fibonacci-hashing mixer (and guard bits==0/>64) so it returns a genuinely bits-wide value; hash_32 can stay as-is.",
  "fix_status": "fixed",
  "fix_baseline_reproduced": true,
  "fix_patched_reproduced": false,
  "fix_verdict": "fix.diff validated WITHOUT a full kernel rebuild, by compiling the same unmodified /usr/src/sys/dev/drm/drm_hashtab.c into htcollapse.ko with only the header changed (fixinc/linux/hash.h, logic identical to fix.diff): baseline build reproduces the collapse (1/4096 buckets, max_chain 8192, linearly growing insert times 581->11403 us per 1024); patched build eliminates it (4096/4096 buckets, max_chain 2, flat 17-19 us chunks) with identical functional correctness (find/dup/remove all OK, errors=0). fix_baseline_reproduced=true (bug present on baseline); fix_patched_reproduced=false (bug ABSENT on patched - desired outcome).",
  "fix_evidence": "run.log sections === kernel baseline === vs === kernel fixed ===; run.2.log same; fix.diff; fixinc/linux/hash.h"
}