File bsc1243378.patch of Package dnsdist.38874
based on
commit bb9aaec283b97c1ef2081cc66a2cf33e6edc40ea
Author: Remi Gacogne <remi.gacogne@powerdns.com>
Date: Tue May 13 15:50:21 2025 +0200
dnsdist: Fix a crash when TCP queries and responses keep coming
It happens when we keep finding queries waiting for us on the incoming
TCP socket from the client, and responses waiting for us on the TCP
socket to the backend after forwarding a new query. This is quite
unlikely but not impossible to happen, as reported by Renaud Allard
(many thanks for taking the time to investigate the issue!).
Index: dnsdist-1.8.0/dnsdist-tcp-upstream.hh
===================================================================
--- dnsdist-1.8.0.orig/dnsdist-tcp-upstream.hh
+++ dnsdist-1.8.0/dnsdist-tcp-upstream.hh
@@ -176,4 +176,5 @@ static void handleTimeout(std::shared_pt
bool d_proxyProtocolPayloadHasTLV{false};
bool d_lastIOBlocked{false};
bool d_hadErrors{false};
+ bool d_handlingIO{false};
};
Index: dnsdist-1.8.0/dnsdist-tcp.cc
===================================================================
--- dnsdist-1.8.0.orig/dnsdist-tcp.cc
+++ dnsdist-1.8.0/dnsdist-tcp.cc
@@ -827,8 +827,39 @@ void IncomingTCPConnectionState::handleI
handleIO(conn, now);
}
+class HandlingIOGuard
+{
+public:
+ HandlingIOGuard(bool& handlingIO) :
+ d_handlingIO(handlingIO)
+ {
+ }
+ HandlingIOGuard(const HandlingIOGuard&) = delete;
+ HandlingIOGuard(HandlingIOGuard&&) = delete;
+ HandlingIOGuard& operator=(const HandlingIOGuard& rhs) = delete;
+ HandlingIOGuard& operator=(HandlingIOGuard&&) = delete;
+ ~HandlingIOGuard()
+ {
+ d_handlingIO = false;
+ }
+
+private:
+ bool& d_handlingIO;
+};
+
void IncomingTCPConnectionState::handleIO(std::shared_ptr<IncomingTCPConnectionState>& state, const struct timeval& now)
{
+ // let's make sure we are not already in handleIO() below in the stack:
+ // this might happen when we have a response available on the backend socket
+ // right after forwarding the query, and then a query waiting for us on the
+ // client socket right after forwarding the response, and then a response available
+ // on the backend socket right after forwarding the query.. you get the idea.
+ if (state->d_handlingIO) {
+ return;
+ }
+ state->d_handlingIO = true;
+ HandlingIOGuard reentryGuard(state->d_handlingIO);
+
// why do we loop? Because the TLS layer does buffering, and thus can have data ready to read
// even though the underlying socket is not ready, so we need to actually ask for the data first
IOState iostate = IOState::Done;