From: Matthijs Mekking Date: Thu, 18 Feb 2021 15:09:41 +0000 (+0100) Subject: Don't allow recursion on staleonly lookups X-Git-Tag: v9.17.11~24^2~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=9e061faaae81d1cae79331351d10ac9129d75024;p=thirdparty%2Fbind9.git Don't allow recursion on staleonly lookups Fix a crash that can happen in the following scenario: A client request is received. There is no data for it in the cache, (not even stale data). A resolver fetch is created as part of recursion. Some time later, the fetch still hasn't completed, and stale-answer-client-timeout is triggered. A staleonly lookup is started. It will also find no data in the cache. So 'query_lookup()' will call 'query_gotanswer()' with ISC_R_NOTFOUND, so this will call 'query_notfound()' and this will start recursion. We will eventually end up in 'ns_query_recurse()' and that requires the client query fetch to be NULL: REQUIRE(client->query.fetch == NULL); If the previously started fetch is still running this assertion fails. The crash is easily prevented by not requiring recursion for staleonly lookups. Also remove a redundant setting of the staleonly flag at the end of 'query_lookup_staleonly()' before destroying the query context. Add a system test to catch this case. --- diff --git a/bin/tests/system/serve-stale/tests.sh b/bin/tests/system/serve-stale/tests.sh index a11319c8544..4b85dd358e8 100755 --- a/bin/tests/system/serve-stale/tests.sh +++ b/bin/tests/system/serve-stale/tests.sh @@ -1072,7 +1072,8 @@ echo_i "sending queries for tests $((n+1))-$((n+4))..." $DIG -p ${PORT} @10.53.0.3 data.example TXT > dig.out.test$((n+1)) & $DIG -p ${PORT} @10.53.0.3 othertype.example CAA > dig.out.test$((n+2)) & $DIG -p ${PORT} @10.53.0.3 nodata.example TXT > dig.out.test$((n+3)) & -$DIG -p ${PORT} @10.53.0.3 nxdomain.example TXT > dig.out.test$((n+4)) +$DIG -p ${PORT} @10.53.0.3 nxdomain.example TXT > dig.out.test$((n+4)) & +$DIG -p ${PORT} @10.53.0.3 notfound.example TXT > dig.out.test$((n+5)) wait @@ -1112,6 +1113,16 @@ grep "example\..*30.*IN.*SOA" dig.out.test$n > /dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) +# The notfound.example check is different than nxdomain.example because +# we didn't send a prime query to add notfound.example to the cache. +n=$((n+1)) +echo_i "check notfound.example (max-stale-ttl default) ($n)" +ret=0 +grep "status: SERVFAIL" dig.out.test$n > /dev/null || ret=1 +grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + # # Now test server with serve-stale answers disabled. # diff --git a/lib/ns/query.c b/lib/ns/query.c index 9d5844084f2..8dd1a7f3611 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -6017,13 +6017,13 @@ query_lookup_staleonly(ns_client_t *client) { qctx_init(client, NULL, client->query.qtype, &qctx); dns_db_attach(client->view->cachedb, &qctx.db); + client->query.attributes &= ~NS_QUERYATTR_RECURSIONOK; client->query.dboptions |= DNS_DBFIND_STALEONLY; (void)query_lookup(&qctx); if (qctx.node != NULL) { dns_db_detachnode(qctx.db, &qctx.node); } qctx_freedata(&qctx); - client->query.dboptions &= ~DNS_DBFIND_STALEONLY; qctx_destroy(&qctx); }