From: W.C.A. Wijngaards Date: Wed, 22 Jul 2026 08:19:02 +0000 (+0200) Subject: - Fix CVE-2026-55991, Remote DNS-over-QUIC (DoQ) flow-control X-Git-Tag: release-1.25.2~3 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=aac261cbb3795cbd60af2f37ef57bfa5c186aae6;p=thirdparty%2Funbound.git - 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. --- diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c index 0500bff8d..38b493a25 100644 --- a/services/listen_dnsport.c +++ b/services/listen_dnsport.c @@ -4508,6 +4508,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), @@ -4882,6 +4905,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; @@ -5470,26 +5494,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 ce4d3417d..4c2142211 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; }