From a6f53d47b2253a04a9875e889c2bcd95bf70f10e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Tue, 17 Mar 2026 04:45:16 +0100 Subject: [PATCH] Fix use-after-free in resolver SIG(0) async verification path When a SIG(0)-signed response triggers async ECDSA verification via dns_message_checksig_async(), the respctx_t holds a raw pointer to the resquery_t. If the fetch context is shut down while verification is in flight (e.g. due to recursive-clients quota exhaustion), the query is destroyed and the callback dereferences a dangling pointer. Take a reference on the resquery_t when initializing the respctx_t, and release it in both cleanup paths. The query's own reference to the fetch context keeps the fctx alive transitively. --- lib/dns/resolver.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index e66e602888..50f88209dd 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -7543,6 +7543,7 @@ resquery_response(isc_result_t eresult, isc_region_t *region, void *arg) { return; cleanup: + resquery_detach(&rctx->query); isc_mem_putanddetach(&rctx->mctx, rctx, sizeof(*rctx)); } @@ -7903,6 +7904,7 @@ resquery_response_continue(void *arg, isc_result_t result) { rctx_done(rctx, result); cleanup: + resquery_detach(&rctx->query); isc_mem_putanddetach(&rctx->mctx, rctx, sizeof(*rctx)); } @@ -7916,7 +7918,7 @@ static void rctx_respinit(resquery_t *query, fetchctx_t *fctx, isc_result_t result, isc_region_t *region, respctx_t *rctx) { *rctx = (respctx_t){ .result = result, - .query = query, + .query = resquery_ref(query), .fctx = fctx, .broken_type = badns_response, .retryopts = query->options };