Prevent passing NULL to dns_dispatch_resume()
If a query sent using the dns_request API times out when the view it was associated with gets torn down, the dns_dispatch_resume() call in req_response() may be issued with the 'resp' argument set to NULL, triggering an assertion failure. Consider the following scenario ([A] and [B] are thread identifiers):
-
[A] Read timeout for a dispatch query fires.
-
[A] udp_recv() is called. It locks the dispatch, determines it timed out, prepares for calling the higher-level callback with ISC_R_TIMEDOUT, and unlocks the dispatch (lib/dns/dispatch.c:633).
-
[B] The last reference to a view is released. dns_requestmgr_shutdown() is called, canceling all in-flight requests for that view. (Note that udp_recv() in thread [A] already unlocked the dispatch, so its state can be modified.) As a part of this process, request_cancel() calls dns_dispatch_done() on request->dispentry, setting it to NULL.
-
[A] udp_recv() calls the higher-level callback (req_response()) with ISC_R_TIMEDOUT.
-
[A] Since the request timed out, req_response() retries sending it. In the process, it calls dns_dispatch_resume(), passing request->dispentry as the 'resp' argument.
-
[A] Since 'resp' is NULL, the REQUIRE(VALID_RESPONSE(resp)); assertion in dns_dispatch_resume() fails.
Fix by checking whether the request has been canceled before calling dns_dispatch_resume(), similarly to how it is done in req_connected() and req_senddone().
Closes #4719 (closed)