From b794b4eeed404ac5ed2ed613295e6d20d1817359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ayd=C4=B1n=20Mercan?= Date: Mon, 9 Mar 2026 15:48:34 +0300 Subject: [PATCH 1/2] Add system test for HTTP/2 SETTINGS frame flood Send a valid DoH query followed by a flood of SETTINGS frames to trigger a use-after-free in the write buffer. Under ASan, named will abort if the bug is present. --- bin/tests/system/doth/tests_malicious.py | 73 ++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 bin/tests/system/doth/tests_malicious.py diff --git a/bin/tests/system/doth/tests_malicious.py b/bin/tests/system/doth/tests_malicious.py new file mode 100644 index 0000000000..7529f2b7e1 --- /dev/null +++ b/bin/tests/system/doth/tests_malicious.py @@ -0,0 +1,73 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +import socket +import ssl + +from h2.config import H2Configuration +from h2.connection import H2Connection +from h2.settings import SettingCodes + +import dns.message + + +def test_settings_frame_flood(ns1, named_httpsport): + msg = dns.message.make_query(".", "SOA") + wire = msg.to_wire() + + with socket.create_connection((ns1.ip, named_httpsport), timeout=10) as sock: + ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + ctx.check_hostname = False + ctx.verify_mode = ssl.CERT_NONE + ctx.set_alpn_protocols(["h2"]) + + with ctx.wrap_socket(sock, server_hostname=ns1.ip) as tls: + config = H2Configuration(client_side=True, header_encoding="utf-8") + conn = H2Connection(config=config) + conn.initiate_connection() + tls.sendall(conn.data_to_send()) + + stream_id = conn.get_next_available_stream_id() + conn.send_headers( + stream_id, + [ + (":method", "POST"), + (":path", "/dns-query"), + (":scheme", "https"), + (":authority", f"{ns1.ip}:{named_httpsport}"), + ("content-type", "application/dns-message"), + ("accept", "application/dns-message"), + ("content-length", str(len(wire))), + ], + ) + conn.send_data(stream_id, wire, end_stream=True) + tls.sendall(conn.data_to_send()) + + for i in range(4096): + try: + conn.update_settings( + { + SettingCodes.MAX_CONCURRENT_STREAMS: (i % 100) + 1, + SettingCodes.INITIAL_WINDOW_SIZE: i + 1, + } + ) + tls.sendall(conn.data_to_send()) + except Exception: # pylint: disable=broad-except + break + + if i % 500 == 0: + tls.settimeout(0.05) + try: + while (data := tls.recv(65535)) != b"": + conn.receive_data(data) + tls.sendall(conn.data_to_send()) + except Exception: # pylint: disable=broad-except + pass From 4d16a8c9f2d9a5e88cd4588246d51eb5731d3b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ayd=C4=B1n=20Mercan?= Date: Tue, 10 Mar 2026 14:48:02 +0300 Subject: [PATCH 2/2] Fix use-after-free in DoH write buffer after HTTP/2 send After the send callback completes, the UV request is freed but the HTTP/2 socket's write buffer still points to the freed memory. If nghttp2 subsequently needs to send frames (e.g. SETTINGS ACK), the server_read_callback reads from the dangling buffer. Clear the write buffer before freeing the UV request. --- lib/isc/netmgr/http.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/isc/netmgr/http.c b/lib/isc/netmgr/http.c index 4d8fe48174..0055311cb2 100644 --- a/lib/isc/netmgr/http.c +++ b/lib/isc/netmgr/http.c @@ -2743,6 +2743,8 @@ server_httpsend(isc_nmhandle_t *handle, isc_nmsocket_t *sock, } else { cb(handle, result, cbarg); } + + isc_buffer_initnull(&sock->h2->wbuf); isc__nm_uvreq_put(&req); }