DF-5146 / hash64_collapse.c
/* * DF-5146 -- userspace arithmetic proof. * * Claim: on 64-bit DragonFly, the drm linux-compat hash_long() is * identically ZERO for every bit-width <= 32, so every key hashed by * sys/dev/drm/drm_hashtab.c lands in bucket 0 of drm hash tables * (order 12 => 4096 buckets; 4095 stay empty forever). * * This program replicates, verbatim, the arithmetic of: * sys/sys/hash.h:33-53 (djb2 hash32_buf; 3rd arg = initval) * sys/dev/drm/include/linux/hash.h:32-54 (hash_64 / hash_32 / hash_long) * * Build: cc -O2 -o hash64_collapse hash64_collapse.c * Run: ./hash64_collapse * Success criterion: "RESULT: hash_long(key,bits)==0 for ALL ... zero outputs" * and, for contrast, hash_32 (the i386 path) spreading over the bucket space. */ #include <stdio.h> #include <stdint.h> #include <string.h> /* ---- sys/sys/hash.h (verbatim) ---- */ #define HASHINIT 5381 #define HASHSTEP(x,c) (((x << 5) + x) + c) static uint32_t hash32_buf(const void *buf, size_t len, uint32_t hash) { const unsigned char *p = buf; while (len--) hash = HASHSTEP(hash, *p++); return hash; } /* ---- sys/dev/drm/include/linux/hash.h (verbatim) ---- */ static uint64_t hash_64(uint64_t val, unsigned int bits) { uint64_t ret; ret = (uint64_t)hash32_buf(&val, sizeof(val), bits); return ret >> (64 - bits); } static uint32_t hash_32(uint32_t val, unsigned int bits) { uint32_t ret; ret = hash32_buf(&val, sizeof(val), bits); return ret >> (32 - bits); } int main(void) { unsigned bits; unsigned long long key; unsigned long long tested = 0, zero64 = 0; unsigned maxb32 = 0; /* 1) hash_long == hash_64 on x86_64: must be 0 for all bits<=32 */ for (bits = 1; bits <= 32; bits++) { for (key = 0; key < 200000; key++) { uint64_t k = key * 0x9E3779B97F4A7C15ULL; /* spread keys */ uint64_t h = hash_64(k, bits); tested++; if (h == 0) zero64++; if (h >> bits) { /* wider than bits => would index OOB */ printf("BUG-IN-PROOF: h wider than bits\n"); return 1; } } } /* 2) the actual drm table order used in-tree: 12 (drm_drv.c:603, * drm_gem.c:148) -- show a few concrete values */ printf("hash_long(key, 12) samples (drm map_hash / gem offset_hash):\n"); for (key = 0; key < 8; key++) { uint64_t k = key * 0x9E3779B97F4A7C15ULL; printf(" key=0x%016llx -> bucket=%llu\n", (unsigned long long)k, (unsigned long long)hash_64(k, 12)); } /* 3) i386 path for contrast: hash_32 uses top bits properly */ for (key = 0; key < 200000; key++) { uint32_t k = (uint32_t)(key * 2654435761UL); unsigned h = hash_32(k, 12); if (h > maxb32) maxb32 = h; } printf("i386 hash_32(key,12) max bucket = %u (table has 4096 buckets)\n", maxb32); printf("tested=%llu zero64=%llu\n", tested, zero64); if (zero64 == tested && maxb32 <= 4095) { printf("RESULT: hash_long(key,bits)==0 for ALL %llu inputs " "with bits in [1,32] -- 64-bit drm hash tables collapse " "to bucket 0\n", tested); return 0; } printf("RESULT: NOT reproduced (unexpected)\n"); return 1; } |