Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: [email protected], [email protected]
Control: affects -1 + src:nghttp2
User: [email protected]
Usertags: pu
(for 13.8)
Fixes a low severity security issue in nghttp2, all tests in debusine
are fine. debdiff below.
Cheers,
Moritz
diff -Nru nghttp2-1.64.0/debian/changelog nghttp2-1.64.0/debian/changelog
--- nghttp2-1.64.0/debian/changelog 2026-04-15 17:04:15.000000000 +0200
+++ nghttp2-1.64.0/debian/changelog 2026-09-03 23:11:23.000000000 +0200
@@ -1,3 +1,9 @@
+nghttp2 (1.64.0-1.1+deb13u2) trixie; urgency=medium
+
+ * CVE-2026-58055 (Closes: #1140917)
+
+ -- Moritz Mühlenhoff <[email protected]> Thu, 03 Sep 2026 23:11:23 +0200
+
nghttp2 (1.64.0-1.1+deb13u1) trixie-security; urgency=medium
* Non-maintainer upload by the Security Team.
diff -Nru nghttp2-1.64.0/debian/patches/CVE-2026-58055.patch
nghttp2-1.64.0/debian/patches/CVE-2026-58055.patch
--- nghttp2-1.64.0/debian/patches/CVE-2026-58055.patch 1970-01-01
01:00:00.000000000 +0100
+++ nghttp2-1.64.0/debian/patches/CVE-2026-58055.patch 2026-09-03
23:11:20.000000000 +0200
@@ -0,0 +1,287 @@
+From ab28105c4a0197da24f8bfc414bc116055249e1e Mon Sep 17 00:00:00 2001
+From: Tatsuhiro Tsujikawa <[email protected]>
+Date: Fri, 22 May 2026 21:26:44 +0900
+Subject: [PATCH] nghttpx: Tighten up CONNECT and HTTP Upgrade handling
+
+--- nghttp2-1.64.0.orig/src/shrpx_downstream.cc
++++ nghttp2-1.64.0/src/shrpx_downstream.cc
+@@ -1146,7 +1146,8 @@ bool Downstream::can_detach_downstream_c
+ // state, especially for HTTP/1.1
+ return dconn_ && response_state_ == DownstreamState::MSG_COMPLETE &&
+ request_state_ == DownstreamState::MSG_COMPLETE && !upgraded_ &&
+- !resp_.connection_close && request_buf_.rleft() == 0;
++ !resp_.connection_close && blocked_request_buf_.rleft() == 0 &&
++ request_buf_.rleft() == 0;
+ }
+
+ DefaultMemchunks Downstream::pop_response_buf() {
+--- nghttp2-1.64.0.orig/src/shrpx_downstream.h
++++ nghttp2-1.64.0/src/shrpx_downstream.h
+@@ -231,6 +231,10 @@ struct Request {
+ // orig_authority and orig_path have the authority and path which
+ // are used for the first backend selection.
+ bool forwarded_once;
++ // true if HTTP/1 request message has been completed. This field is
++ // added because Downstream::get_request_state() might be altered
++ // from DownstreamState::MSG_COMPLETE.
++ bool http1_msg_complete;
+ };
+
+ struct Response {
+--- nghttp2-1.64.0.orig/src/shrpx_http2_upstream.cc
++++ nghttp2-1.64.0/src/shrpx_http2_upstream.cc
+@@ -359,6 +359,14 @@ int Http2Upstream::on_request_headers(Do
+ return 0;
+ }
+
++ if (method_token == HTTP_CONNECT && content_length) {
++ if (LOG_ENABLED(INFO)) {
++ ULOG(INFO, this) << "content-length are not allowed in CONNECT request";
++ }
++
++ return error_reply(downstream, 400);
++ }
++
+ auto faddr = handler_->get_upstream_addr();
+
+ // For HTTP/2 proxy, we require :authority.
+--- nghttp2-1.64.0.orig/src/shrpx_http3_upstream.cc
++++ nghttp2-1.64.0/src/shrpx_http3_upstream.cc
+@@ -2282,6 +2282,14 @@ int Http3Upstream::http_end_request_head
+ return 0;
+ }
+
++ if (method_token == HTTP_CONNECT && content_length) {
++ if (LOG_ENABLED(INFO)) {
++ ULOG(INFO, this) << "content-length are not allowed in CONNECT request";
++ }
++
++ return error_reply(downstream, 400);
++ }
++
+ auto faddr = handler_->get_upstream_addr();
+
+ auto config = get_config();
+--- nghttp2-1.64.0.orig/src/shrpx_http_downstream_connection.cc
++++ nghttp2-1.64.0/src/shrpx_http_downstream_connection.cc
+@@ -735,6 +735,34 @@ int HttpDownstreamConnection::push_reque
+ return 0;
+ }
+
++bool HttpDownstreamConnection::should_block_request_body() const {
++ const auto &req = downstream_->request();
++
++ return !downstream_->get_request_header_sent() ||
++ (req.upgrade_request && !downstream_->get_upgraded());
++}
++
++bool HttpDownstreamConnection::should_unblock_request_body_before_response()
++ const {
++ const auto &req = downstream_->request();
++
++ return !req.upgrade_request;
++}
++
++void HttpDownstreamConnection::process_blocked_request_buf_on_response() {
++ if (blocked_request_buf_processed_) {
++ return;
++ }
++
++ process_blocked_request_buf();
++
++ auto buf = downstream_->get_blocked_request_buf();
++ buf->reset();
++ blocked_request_buf_processed_ = true;
++
++ signal_write();
++}
++
+ int HttpDownstreamConnection::process_blocked_request_buf() {
+ auto src = downstream_->get_blocked_request_buf();
+
+@@ -764,7 +792,7 @@ int HttpDownstreamConnection::process_bl
+
+ int HttpDownstreamConnection::push_upload_data_chunk(const uint8_t *data,
+ size_t datalen) {
+- if (!downstream_->get_request_header_sent()) {
++ if (should_block_request_body()) {
+ auto output = downstream_->get_blocked_request_buf();
+ auto &req = downstream_->request();
+ output->append(data, datalen);
+@@ -796,7 +824,7 @@ int HttpDownstreamConnection::push_uploa
+ }
+
+ int HttpDownstreamConnection::end_upload_data() {
+- if (!downstream_->get_request_header_sent()) {
++ if (should_block_request_body()) {
+ downstream_->set_blocked_request_data_eof(true);
+ if (request_header_written_) {
+ signal_write();
+@@ -997,6 +1025,11 @@ int htp_hdrs_completecb(llhttp_t *htp) {
+ // upgrade succeeded, 101 response is treated as final in nghttpx.
+ downstream->check_upgrade_fulfilled_http1();
+
++ if (req.method == HTTP_CONNECT && resp.http_status / 100 == 2 &&
++ !downstream->get_upgraded()) {
++ resp.http_status = 502;
++ }
++
+ if (downstream->get_non_final_response()) {
+ // Reset content-length because we reuse same Downstream for the
+ // next response.
+@@ -1018,7 +1051,7 @@ int htp_hdrs_completecb(llhttp_t *htp) {
+ downstream->set_response_state(DownstreamState::HEADER_COMPLETE);
+ downstream->inspect_http1_response();
+
+- if (htp->flags & F_CHUNKED) {
++ if (!downstream->get_upgraded() && (htp->flags & F_CHUNKED)) {
+ downstream->set_chunked_response(true);
+ }
+
+@@ -1033,13 +1066,22 @@ int htp_hdrs_completecb(llhttp_t *htp) {
+ resp.connection_close = true;
+ // transfer-encoding not applied to upgraded connection
+ downstream->set_chunked_response(false);
+- } else if (http2::legacy_http1(req.http_major, req.http_minor)) {
+- if (resp.fs.content_length == -1) {
++
++ static_cast<HttpDownstreamConnection *>(dconn)
++ ->process_blocked_request_buf_on_response();
++ } else {
++ if (req.upgrade_request) {
+ resp.connection_close = true;
+ }
+- downstream->set_chunked_response(false);
+- } else if (!downstream->expect_response_body()) {
+- downstream->set_chunked_response(false);
++
++ if (http2::legacy_http1(req.http_major, req.http_minor)) {
++ if (resp.fs.content_length == -1) {
++ resp.connection_close = true;
++ }
++ downstream->set_chunked_response(false);
++ } else if (!downstream->expect_response_body()) {
++ downstream->set_chunked_response(false);
++ }
+ }
+
+ if (loggingconf.access.write_early && downstream->accesslog_ready()) {
+@@ -1213,7 +1255,10 @@ int htp_msg_completecb(llhttp_t *htp) {
+ int HttpDownstreamConnection::write_first() {
+ int rv;
+
+- process_blocked_request_buf();
++ auto should_unblock_req_body =
should_unblock_request_body_before_response();
++ if (should_unblock_req_body) {
++ process_blocked_request_buf();
++ }
+
+ if (conn_.tls.ssl) {
+ rv = write_tls();
+@@ -1234,8 +1279,11 @@ int HttpDownstreamConnection::write_firs
+ first_write_done_ = true;
+ downstream_->set_request_header_sent(true);
+
+- auto buf = downstream_->get_blocked_request_buf();
+- buf->reset();
++ if (should_unblock_req_body) {
++ auto buf = downstream_->get_blocked_request_buf();
++ buf->reset();
++ blocked_request_buf_processed_ = true;
++ }
+
+ // upstream->resume_read() might be called in
+ // write_tls()/write_clear(), but before blocked_request_buf_ is
+--- nghttp2-1.64.0.orig/src/shrpx_http_downstream_connection.h
++++ nghttp2-1.64.0/src/shrpx_http_downstream_connection.h
+@@ -91,6 +91,9 @@ public:
+ int noop();
+
+ int process_blocked_request_buf();
++ void process_blocked_request_buf_on_response();
++ bool should_unblock_request_body_before_response() const;
++ bool should_block_request_body() const;
+
+ private:
+ Connection conn_;
+@@ -117,6 +120,8 @@ private:
+ bool reusable_;
+ // true if request header is written to request buffer.
+ bool request_header_written_;
++ // true if blocked request buffer has been processed.
++ bool blocked_request_buf_processed_;
+ };
+
+ } // namespace shrpx
+--- nghttp2-1.64.0.orig/src/shrpx_https_upstream.cc
++++ nghttp2-1.64.0/src/shrpx_https_upstream.cc
+@@ -416,6 +416,17 @@ int htp_hdrs_completecb(llhttp_t *htp) {
+
+ downstream->inspect_http1_request();
+
++ if ((req.upgrade_request || llhttp_get_upgrade(htp)) &&
++ (req.fs.header(http2::HD_TRANSFER_ENCODING) ||
++ req.fs.header(http2::HD_CONTENT_LENGTH))) {
++ if (LOG_ENABLED(INFO)) {
++ ULOG(INFO, upstream) << "transfer-encoding and content-length are not "
++ "allowed in CONNECT or upgrade request";
++ }
++
++ return -1;
++ }
++
+ if (htp->flags & F_CHUNKED) {
+ downstream->set_chunked_request(true);
+ }
+@@ -559,6 +570,16 @@ int htp_bodycb(llhttp_t *htp, const char
+ int rv;
+ auto upstream = static_cast<HttpsUpstream *>(htp->data);
+ auto downstream = upstream->get_downstream();
++ const auto &req = downstream->request();
++
++ if (req.upgrade_request || llhttp_get_upgrade(htp)) {
++ if (LOG_ENABLED(INFO)) {
++ ULOG(INFO, upstream) << "Request body for Upgrade request is not
allowed";
++ }
++
++ return HPE_USER;
++ }
++
+ rv = downstream->push_upload_data_chunk(
+ reinterpret_cast<const uint8_t *>(data), len);
+ if (rv != 0) {
+@@ -592,6 +613,7 @@ int htp_msg_completecb(llhttp_t *htp) {
+ }
+
+ downstream->set_request_state(DownstreamState::MSG_COMPLETE);
++ req.http1_msg_complete = true;
+ rv = downstream->end_upload_data();
+ if (rv != 0) {
+ if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
+@@ -632,7 +654,8 @@ int HttpsUpstream::on_read() {
+
+ // downstream can be nullptr here, because it is initialized in the
+ // callback chain called by llhttp_execute()
+- if (downstream && downstream->get_upgraded()) {
++ if (downstream && downstream->request().http1_msg_complete &&
++ downstream->get_upgraded()) {
+ auto rv = downstream->push_upload_data_chunk(rb->pos(), rb->rleft());
+
+ if (rv != 0) {
+@@ -706,9 +729,13 @@ int HttpsUpstream::on_read() {
+
+ if (htperr != HPE_OK) {
+ if (LOG_ENABLED(INFO)) {
+- ULOG(INFO, this) << "HTTP parse failure: "
+- << "(" << llhttp_errno_name(htperr) << ") "
+- << llhttp_get_error_reason(&htp_);
++ if (htperr == HPE_USER) {
++ ULOG(INFO, this) << "HTTP callback error";
++ } else {
++ ULOG(INFO, this) << "HTTP parse failure: "
++ << "(" << llhttp_errno_name(htperr) << ") "
++ << llhttp_get_error_reason(&htp_);
++ }
+ }
+
+ if (downstream &&
diff -Nru nghttp2-1.64.0/debian/patches/series
nghttp2-1.64.0/debian/patches/series
--- nghttp2-1.64.0/debian/patches/series 2026-04-15 17:04:15.000000000
+0200
+++ nghttp2-1.64.0/debian/patches/series 2026-09-03 23:11:08.000000000
+0200
@@ -3,3 +3,4 @@
lp-2104171-avoid-rubydomain-namespace.patch
CVE-2026-27135.patch
CVE-2026-27135-test.patch
+CVE-2026-58055.patch