new file mode 100644
@@ -0,0 +1,112 @@
+From 355213b9175382db5e86e49731aa939a533b1b03 Mon Sep 17 00:00:00 2001
+From: "W.C.A. Wijngaards" <wouter@nlnetlabs.nl>
+Date: Wed, 22 Jul 2026 10:19:02 +0200
+Subject: [PATCH] - Fix CVE-2026-55991, Remote DNS-over-QUIC (DoQ) flow-control
+ assertion failure in libngtcp2. Thanks to Qifan Zhang, Palo Alto
+ Networks, for the report. In addition, thanks to Xuanchao Xie, for also
+ reporting this issue.
+
+(cherry picked from commit aac261cbb3795cbd60af2f37ef57bfa5c186aae6)
+
+CVE: CVE-2026-55991
+Upstream-Status: Backport [https://github.com/NLnetLabs/unbound/commit/aac261cbb3795cbd60af2f37ef57bfa5c186aae6]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ services/listen_dnsport.c | 46 +++++++++++++++++++++++++++------------
+ testcode/doqclient.c | 4 ++--
+ 2 files changed, 34 insertions(+), 16 deletions(-)
+
+diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c
+index 3c5010b6b..d49d4ad4c 100644
+--- a/services/listen_dnsport.c
++++ b/services/listen_dnsport.c
+@@ -4472,6 +4472,29 @@ doq_stream_reset_cb(ngtcp2_conn* ATTR_UNUSED(conn), int64_t stream_id,
+ return 0;
+ }
+
++/** ngtcp2 extend_max_stream_data function */
++int doq_extend_max_stream_data_cb(ngtcp2_conn* ATTR_UNUSED(conn),
++ int64_t stream_id, uint64_t max_data, void* user_data,
++ void* ATTR_UNUSED(stream_user_data))
++{
++ struct doq_conn* doq_conn = (struct doq_conn*)user_data;
++ struct doq_stream* stream;
++ verbose(VERB_ALGO, "doq extend_max_stream_data stream id %d "
++ "max_data %d ", (int)stream_id, (int)max_data);
++ if(max_data == 0)
++ return 0;
++ stream = doq_stream_find(doq_conn, stream_id);
++ if(!stream) {
++ verbose(VERB_ALGO, "doq: unknown stream %d", (int)stream_id);
++ return 0;
++ }
++ if(!stream->is_answer_available)
++ return 0;
++ doq_stream_on_write_list(doq_conn, stream);
++ doq_conn_write_enable(doq_conn);
++ return 0;
++}
++
+ /** ngtcp2 acked_stream_data_offset callback function */
+ static int
+ doq_acked_stream_data_offset_cb(ngtcp2_conn* ATTR_UNUSED(conn),
+@@ -4846,6 +4869,7 @@ doq_conn_setup(struct doq_conn* conn, uint8_t* scid, size_t scidlen,
+ callbacks.stream_open = doq_stream_open_cb;
+ callbacks.stream_close = doq_stream_close_cb;
+ callbacks.stream_reset = doq_stream_reset_cb;
++ callbacks.extend_max_stream_data = doq_extend_max_stream_data_cb;
+ callbacks.acked_stream_data_offset = doq_acked_stream_data_offset_cb;
+ callbacks.recv_stream_data = doq_recv_stream_data_cb;
+
+@@ -5427,26 +5451,20 @@ doq_conn_write_streams(struct comm_point* c, struct doq_conn* conn,
+ continue;
+ } else if(ret == NGTCP2_ERR_STREAM_DATA_BLOCKED) {
+ verbose(VERB_ALGO, "doq: ngtcp2_conn_writev_stream returned NGTCP2_ERR_STREAM_DATA_BLOCKED");
+-#ifdef HAVE_NGTCP2_CCERR_DEFAULT
+- ngtcp2_ccerr_set_application_error(
+- &conn->ccerr, -1, NULL, 0);
+-#else
+- ngtcp2_connection_close_error_set_application_error(&conn->last_error, -1, NULL, 0);
+-#endif
+- if(err_drop)
+- *err_drop = 0;
+- if(!doq_conn_close_error(c, conn)) {
+- if(err_drop)
+- *err_drop = 1;
++ if(stream) {
++ doq_stream_off_write_list(conn, stream);
++ stream = stream->write_next;
++ continue;
++ } else {
++ break;
+ }
+- return 0;
+ } else if(ret == NGTCP2_ERR_STREAM_SHUT_WR) {
+ verbose(VERB_ALGO, "doq: ngtcp2_conn_writev_stream returned NGTCP2_ERR_STREAM_SHUT_WR");
+ #ifdef HAVE_NGTCP2_CCERR_DEFAULT
+ ngtcp2_ccerr_set_application_error(
+- &conn->ccerr, -1, NULL, 0);
++ &conn->ccerr, DOQ_APP_ERROR_CODE, NULL, 0);
+ #else
+- ngtcp2_connection_close_error_set_application_error(&conn->last_error, -1, NULL, 0);
++ ngtcp2_connection_close_error_set_application_error(&conn->last_error, DOQ_APP_ERROR_CODE, NULL, 0);
+ #endif
+ if(err_drop)
+ *err_drop = 0;
+diff --git a/testcode/doqclient.c b/testcode/doqclient.c
+index 1994cd097..3cb25c98b 100644
+--- a/testcode/doqclient.c
++++ b/testcode/doqclient.c
+@@ -1519,9 +1519,9 @@ doq_client_send_pkt(struct doq_client_data* data, uint32_t ecn, uint8_t* buf,
+ }
+ log_err("doq sendmsg: %s", strerror(errno));
+ #ifdef HAVE_NGTCP2_CCERR_DEFAULT
+- ngtcp2_ccerr_set_application_error(&data->ccerr, -1, NULL, 0);
++ ngtcp2_ccerr_set_application_error(&data->ccerr, 1, NULL, 0);
+ #else
+- ngtcp2_connection_close_error_set_application_error(&data->last_error, -1, NULL, 0);
++ ngtcp2_connection_close_error_set_application_error(&data->last_error, 1, NULL, 0);
+ #endif
+ return 0;
+ }
@@ -40,6 +40,7 @@ SRC_URI = "git://github.com/NLnetLabs/unbound.git;protocol=https;branch=master;t
file://CVE-2026-55717.patch \
file://CVE-2026-55973.patch \
file://CVE-2026-55990.patch \
+ file://CVE-2026-55991.patch \
"
SRCREV = "f6269baa605d31859f28770e01a24e3677e5f82c"