]> git.kaiwu.me - haproxy.git/commitdiff
OPTIM: resolvers: only walk duplicates when looking up an existing record
authorChristopher Faulet <cfaulet@haproxy.com>
Tue, 29 Sep 2026 09:07:34 +0000 (11:07 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Tue, 10 Nov 2026 14:47:52 +0000 (15:47 +0100)
When parsing answer records, resolv_validate_dns_response() looks up the
record's hash key in the answer tree to detect duplicates, then walks the
following nodes with eb32_next(), which doesn't stop at the end of the
duplicates of this key. When the key already exists but the record
differs (i.e. on a hash collision), all the remaining nodes of the tree
are compared, which is quadratic in the number of records for crafted
responses of colliding records (up to ~4000 records per 64kB TCP
response).

In normal operation the lookup returns NULL for new records and the first
node is the duplicate for repeated ones, so no difference could be
measured (60 resolutions of 4000 distinct A records over TCP cost the
same CPU time before and after). Still, let's use eb32_next_dup() to only
walk the nodes sharing the looked up key, as intended.

No backport is needed.

src/resolvers.c

index a9953fcf5b80ab2e827beab5febb61114027d0db..51fd04ca4232d63b8289d2290d4c49ce00a65afc 100644 (file)
@@ -1409,7 +1409,7 @@ static int resolv_validate_dns_response(unsigned char *resp, unsigned char *bufe
                /* Lookup to see if we already had this entry */
                found = 0;
 
-               for (eb32 = eb32_lookup(&r_res->answer_tree, key); eb32 != NULL;  eb32 = eb32_next(eb32)) {
+               for (eb32 = eb32_lookup(&r_res->answer_tree, key); eb32 != NULL; eb32 = eb32_next_dup(eb32)) {
                        tmp_record = eb32_entry(eb32, typeof(*tmp_record), link);
                        if (tmp_record->type != answer_record->type)
                                continue;