Use a keyed hash for the RRL bucket table

The previous hash_key() was a deterministic, unkeyed (<<1) + add over the
key words.  An off-path attacker could invert it offline and submit
queries whose source /24, qname hash, and qtype map to a single bucket;
under chaining this turns every lookup into an O(N) walk under
rrl->lock and starves legitimate query processing on the very feature
deployed to mitigate DoS.

Replace it with isc_hash32(), which is HalfSipHash-2-4 keyed by a
per-process random seed, so collision sets cannot be precomputed.

Assisted-by: Claude:claude-opus-4-7
(cherry picked from commit a6b7ce29c4)
This commit is contained in:
Ondřej Surý 2026-04-29 18:20:03 +02:00 committed by Ondřej Surý
parent 93cfd196db
commit 0f821104e0
No known key found for this signature in database
GPG key ID: 2820F37E873DEA41

View file

@ -22,6 +22,8 @@
#include <inttypes.h>
#include <stdbool.h>
#include <isc/hash.h>
#include <isc/log.h>
#include <isc/mem.h>
#include <isc/net.h>
#include <isc/netaddr.h>
@ -374,14 +376,12 @@ key_cmp(const dns_rrl_key_t *a, const dns_rrl_key_t *b) {
static uint32_t
hash_key(const dns_rrl_key_t *key) {
uint32_t hval;
int i;
hval = key->w[0];
for (i = sizeof(key->w) / sizeof(key->w[0]) - 1; i >= 0; --i) {
hval = key->w[i] + (hval << 1);
}
return hval;
/*
* The key includes attacker-controlled bits (client /24, qname
* hash, qtype). Use the keyed, per-process-randomised hash so
* collisions cannot be engineered to overload one bucket chain.
*/
return isc_hash32(key, sizeof(*key), true);
}
/*