]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Fix a null-deref in incoming DoH w/ nghttp2 14003/head
authorRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 29 Mar 2024 13:12:29 +0000 (14:12 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 29 Mar 2024 13:12:29 +0000 (14:12 +0100)
When an incoming DoH connection using the `nghttp2` provider is waiting
for a response from a backend that results in a I/O error or timeout,
and the incoming connection also fails due to a I/O error or timeout,
dnsdist could in some cases try to dereference a null pointer, leading
to a crash.

pdns/dnsdistdist/dnsdist-nghttp2-in.cc
pdns/dnsdistdist/test-dnsdistnghttp2-in_cc.cc

index f3f622b152fca5f8c238744993f93389f91f6851..263631b065b18fb8fddc84cdb03a2d6ea2ab3a01 100644 (file)
@@ -1099,7 +1099,9 @@ void IncomingHTTP2Connection::handleWritableIOCallback([[maybe_unused]] int desc
 
 void IncomingHTTP2Connection::stopIO()
 {
-  d_ioState->reset();
+  if (d_ioState) {
+    d_ioState->reset();
+  }
 }
 
 uint32_t IncomingHTTP2Connection::getConcurrentStreamsCount() const
@@ -1135,27 +1137,27 @@ void IncomingHTTP2Connection::updateIO(IOState newState, const FDMultiplexer::ca
   boost::optional<struct timeval> ttd{boost::none};
 
   auto shared = std::dynamic_pointer_cast<IncomingHTTP2Connection>(shared_from_this());
-  if (shared) {
-    struct timeval now
-    {
-    };
-    gettimeofday(&now, nullptr);
+  if (!shared || !d_ioState) {
+    return;
+  }
 
-    if (newState == IOState::NeedRead) {
-      /* use the idle TTL if the handshake has been completed (and proxy protocol payload received, if any),
-         and we have processed at least one query, otherwise we use the shorter read TTL  */
-      if ((d_state == State::waitingForQuery || d_state == State::idle) && (d_queriesCount > 0 || d_currentQueriesCount > 0)) {
-        ttd = getIdleClientReadTTD(now);
-      }
-      else {
-        ttd = getClientReadTTD(now);
-      }
-      d_ioState->update(newState, callback, shared, ttd);
+  timeval now{};
+  gettimeofday(&now, nullptr);
+
+  if (newState == IOState::NeedRead) {
+    /* use the idle TTL if the handshake has been completed (and proxy protocol payload received, if any),
+       and we have processed at least one query, otherwise we use the shorter read TTL  */
+    if ((d_state == State::waitingForQuery || d_state == State::idle) && (d_queriesCount > 0 || d_currentQueriesCount > 0)) {
+      ttd = getIdleClientReadTTD(now);
     }
-    else if (newState == IOState::NeedWrite) {
-      ttd = getClientWriteTTD(now);
-      d_ioState->update(newState, callback, shared, ttd);
+    else {
+      ttd = getClientReadTTD(now);
     }
+    d_ioState->update(newState, callback, shared, ttd);
+  }
+  else if (newState == IOState::NeedWrite) {
+    ttd = getClientWriteTTD(now);
+    d_ioState->update(newState, callback, shared, ttd);
   }
 }
 
index 968e6c2d7e5879d717e5a844f9716895fd116e0e..c3f555a2257efaef054b952112e73717c949a286 100644 (file)
@@ -738,5 +738,36 @@ BOOST_FIXTURE_TEST_CASE(test_IncomingConnection_BackendTimeout, TestFixture)
   }
 }
 
+BOOST_FIXTURE_TEST_CASE(test_IncomingConnection_ClientTimeout_BackendTimeout, TestFixture)
+{
+  auto local = getBackendAddress("1", 80);
+  ClientState localCS(local, true, false, 0, "", {}, true);
+  localCS.dohFrontend = std::make_shared<DOHFrontend>(std::make_shared<MockupTLSCtx>());
+  localCS.dohFrontend->d_urls.insert("/dns-query");
+
+  TCPClientThreadData threadData;
+  threadData.mplexer = std::make_unique<MockupFDMultiplexer>();
+
+  auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53));
+
+  timeval now{};
+  gettimeofday(&now, nullptr);
+
+  size_t counter = 0;
+  s_connectionContexts[counter++] = ExpectedData{{}, {}, {}, {}};
+  s_steps = {
+    {ExpectedStep::ExpectedRequest::handshakeClient, IOState::Done},
+    /* write to client, but the client closes the connection */
+    {ExpectedStep::ExpectedRequest::writeToClient, IOState::Done, 0},
+    /* server close */
+    {ExpectedStep::ExpectedRequest::closeClient, IOState::Done},
+  };
+
+  auto state = std::make_shared<IncomingHTTP2Connection>(ConnectionInfo(&localCS, getBackendAddress("84", 4242)), threadData, now);
+  auto base = std::static_pointer_cast<IncomingTCPConnectionState>(state);
+  IncomingHTTP2Connection::handleTimeout(base, true);
+  state->handleIO();
+}
+
 BOOST_AUTO_TEST_SUITE_END();
 #endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */