]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Tolerate dnspython post-2038 timestamp overflow on 32-bit 12079/head
authorMichal Nowak <mnowak@isc.org>
Thu, 21 May 2026 07:31:15 +0000 (07:31 +0000)
committerMichal Nowak <mnowak@isc.org>
Thu, 21 May 2026 14:56:46 +0000 (16:56 +0200)
dnspython's RRSIG.to_text() converts the signature inception/expiration
fields by calling time.gmtime(), which on 32-bit platforms raises
OverflowError for values past 2038-01-19 (INT32_MAX). Several DNSSEC
test fixtures use far-future expirations: the precomputed RRSIGs in
the dnssec test's rsasha1.example.db.in zone expire in 2093, ans4 of
the chain test hardcodes 2090, and ans10 of the dnssec test uses
2**32-1 (year 2106). Whenever a response carrying such an RRSIG is
formatted with str()/to_text() the overflow propagates out and either
fails the test (when triggered in isctest.query's debug logging) or
kills the asyncserver-based ans* server (when triggered in its
response logger), which in turn cascades into "Failed to stop
servers" teardown errors and SERVFAIL responses for subsequent tests.

Wrap the to_text() calls in isctest/query.py and the str(response)
call in asyncserver's _log_response() with try/except OverflowError,
falling back to a placeholder message. The conversions are only used
for debug logging, so losing the human-readable form there does not
affect what the tests actually validate.

Assisted-by: Claude:claude-opus-4-7
bin/tests/system/isctest/asyncserver.py
bin/tests/system/isctest/query.py

index dd9f6239166e265c7c2fa54a9d2fd149e6d6fce8..fefd03bb2742759ae960b866212095c17b1ba1a1 100644 (file)
@@ -1375,8 +1375,12 @@ class AsyncDnsServer(AsyncServer):
                 qctx.socket,
                 qctx.protocol.name,
             )
+            try:
+                response_text = str(response)
+            except OverflowError:
+                response_text = "<response not representable as text>"
             logging.debug(
-                "\n".join([f"[OUT] {l}" for l in [""] + str(response).splitlines()])
+                "\n".join([f"[OUT] {l}" for l in [""] + response_text.splitlines()])
             )
             return
 
index a7e862b7f6c95fb5422dd84b45bb3f16ec5fbb91..650c1fc637e9036370bc10511baf4f5da5fc4cdf 100644 (file)
@@ -44,6 +44,17 @@ def generic_query(
     log_response: bool = True,
 ) -> Any:
 
+    def _safe_to_text(msg: dns.message.Message) -> str:
+        """
+        Convert a DNS message to text, tolerating dnspython's failure to render
+        RRSIG inception/expiration timestamps that overflow the platform's
+        time_t (e.g. post-2038 values on 32-bit systems).
+        """
+        try:
+            return msg.to_text()
+        except OverflowError:
+            return "<message not representable as text>"
+
     def log_querymsg(exception: Exception | None = None) -> None:
         """
         Helper for logging query message. Call this *after* query_func() has
@@ -54,7 +65,7 @@ def generic_query(
         nonlocal log_query
         if log_query:
             isctest.log.debug(
-                f"isc.query.{query_func.__name__}(): query\n{message.to_text()}"
+                f"isc.query.{query_func.__name__}(): query\n{_safe_to_text(message)}"
             )
             log_query = False  # only log query once
 
@@ -99,7 +110,7 @@ def generic_query(
         if res:
             if log_response:
                 isctest.log.debug(
-                    f"isc.query.{query_func.__name__}(): response\n{res.to_text()}"
+                    f"isc.query.{query_func.__name__}(): response\n{_safe_to_text(res)}"
                 )
             if res.rcode() == expected_rcode or expected_rcode is None:
                 return res