From 3e1ff31a8b8463139a0ee37edf2eeba9b20d4818 Mon Sep 17 00:00:00 2001 From: liweigang Date: Fri, 15 Aug 2025 14:48:21 +0800 Subject: [PATCH] Fix CVE-2025-8671 (cherry picked from commit b02e60be76e5950cbe17f3f5d3fd559cfcdc5c6c) --- CVE-2025-8671-1.patch | 81 +++++++++++++++++++++++ CVE-2025-8671-2.patch | 145 ++++++++++++++++++++++++++++++++++++++++++ CVE-2025-8671-3.patch | 109 +++++++++++++++++++++++++++++++ varnish.spec | 11 +++- 4 files changed, 345 insertions(+), 1 deletion(-) create mode 100644 CVE-2025-8671-1.patch create mode 100644 CVE-2025-8671-2.patch create mode 100644 CVE-2025-8671-3.patch diff --git a/CVE-2025-8671-1.patch b/CVE-2025-8671-1.patch new file mode 100644 index 0000000..84060bd --- /dev/null +++ b/CVE-2025-8671-1.patch @@ -0,0 +1,81 @@ +From 1ce9a6df61555bdc8bd491feb3b5f1ed3f5cbdaf Mon Sep 17 00:00:00 2001 +From: Martin Blix Grydeland +Date: Tue, 1 Jul 2025 14:57:56 +0200 +Subject: [PATCH] H2: Add a H2_Send_GOAWAY method + +This method sends a goaway frame. Change h2_tx_goaway() so that it uses this method. +--- + bin/varnishd/http2/cache_http2.h | 2 ++ + bin/varnishd/http2/cache_http2_proto.c | 7 +------ + bin/varnishd/http2/cache_http2_send.c | 20 ++++++++++++++++++++ + 3 files changed, 23 insertions(+), 6 deletions(-) + +diff --git a/bin/varnishd/http2/cache_http2.h b/bin/varnishd/http2/cache_http2.h +index ba036b84d6..ea25e89bd6 100644 +--- a/bin/varnishd/http2/cache_http2.h ++++ b/bin/varnishd/http2/cache_http2.h +@@ -259,6 +259,8 @@ void H2_Send_Frame(struct worker *, struct h2_sess *, + + void H2_Send_RST(struct worker *wrk, struct h2_sess *h2, + const struct h2_req *r2, uint32_t stream, h2_error h2e); ++void H2_Send_GOAWAY(struct worker *wrk, struct h2_sess *h2, ++ const struct h2_req *r2, h2_error h2e); + + void H2_Send(struct worker *, struct h2_req *, h2_frame type, uint8_t flags, + uint32_t len, const void *, uint64_t *acct); +diff --git a/bin/varnishd/http2/cache_http2_proto.c b/bin/varnishd/http2/cache_http2_proto.c +index 6e9e5bdc96..de4ba424a6 100644 +--- a/bin/varnishd/http2/cache_http2_proto.c ++++ b/bin/varnishd/http2/cache_http2_proto.c +@@ -414,19 +414,14 @@ h2_rx_goaway(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) + static void + h2_tx_goaway(struct worker *wrk, struct h2_sess *h2, h2_error h2e) + { +- char b[8]; +- + ASSERT_RXTHR(h2); + AN(h2e); + + if (h2->goaway || !h2e->send_goaway) + return; + +- h2->goaway = 1; +- vbe32enc(b, h2->highest_stream); +- vbe32enc(b + 4, h2e->val); + H2_Send_Get(wrk, h2, h2->req0); +- H2_Send_Frame(wrk, h2, H2_F_GOAWAY, 0, 8, 0, b); ++ H2_Send_GOAWAY(wrk, h2, h2->req0, h2e); + H2_Send_Rel(h2, h2->req0); + } + +diff --git a/bin/varnishd/http2/cache_http2_send.c b/bin/varnishd/http2/cache_http2_send.c +index d4e66aab8c..5f7a11d352 100644 +--- a/bin/varnishd/http2/cache_http2_send.c ++++ b/bin/varnishd/http2/cache_http2_send.c +@@ -427,6 +427,26 @@ H2_Send_RST(struct worker *wrk, struct h2_sess *h2, const struct h2_req *r2, + H2_Send_Frame(wrk, h2, H2_F_RST_STREAM, 0, sizeof b, stream, b); + } + ++void ++H2_Send_GOAWAY(struct worker *wrk, struct h2_sess *h2, const struct h2_req *r2, ++ h2_error h2e) ++{ ++ char b[8]; ++ ++ CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC); ++ CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC); ++ AN(H2_SEND_HELD(h2, r2)); ++ AN(h2e); ++ ++ if (h2->goaway) ++ return; ++ ++ vbe32enc(b, h2->highest_stream); ++ vbe32enc(b + 4, h2e->val); ++ H2_Send_Frame(wrk, h2, H2_F_GOAWAY, 0, 8, 0, b); ++ h2->goaway = 1; ++} ++ + void + H2_Send(struct worker *wrk, struct h2_req *r2, h2_frame ftyp, uint8_t flags, + uint32_t len, const void *ptr, uint64_t *counter) diff --git a/CVE-2025-8671-2.patch b/CVE-2025-8671-2.patch new file mode 100644 index 0000000..73c9830 --- /dev/null +++ b/CVE-2025-8671-2.patch @@ -0,0 +1,145 @@ +From a629c334c98a3b946e529b9d4ae4c07c0967b8bb Mon Sep 17 00:00:00 2001 +From: Martin Blix Grydeland +Date: Tue, 1 Jul 2025 15:32:25 +0200 +Subject: [PATCH] H2: Make rapid reset handling be calleable from any context + +This patch splits the rapid reset handling into a check and a charge +step. The check determines if this was a benign reset, that is whether it +should be charged against the budgest or not. + +The charge step subtracts from the budget, and handles raises an error +when exceeded. On error it will send a GOAWAY frame on the session +immediately. To allow an error to be sent from this function, and to give +protection to the rapid reset state variables, it is required that the +caller holds the send mutex when calling. +--- + bin/varnishd/http2/cache_http2.h | 5 +++ + bin/varnishd/http2/cache_http2_proto.c | 44 ++++++++++++++++++++------ + bin/varnishd/http2/cache_http2_send.c | 2 -- + 3 files changed, 39 insertions(+), 12 deletions(-) + +diff --git a/bin/varnishd/http2/cache_http2.h b/bin/varnishd/http2/cache_http2.h +index 3765d50..d54fd71 100644 +--- a/bin/varnishd/http2/cache_http2.h ++++ b/bin/varnishd/http2/cache_http2.h +@@ -244,6 +244,7 @@ h2_error h2h_decode_bytes(struct h2_sess *h2, const uint8_t *ptr, + size_t len); + + /* cache_http2_send.c */ ++#define H2_SEND_HELD(h2, r2) (VTAILQ_FIRST(&(h2)->txqueue) == (r2)) + void H2_Send_Get(struct worker *, struct h2_sess *, struct h2_req *); + void H2_Send_Rel(struct h2_sess *, const struct h2_req *); + +@@ -267,6 +268,10 @@ void h2_kill_req(struct worker *, struct h2_sess *, struct h2_req *, h2_error); + int h2_rxframe(struct worker *, struct h2_sess *); + h2_error h2_set_setting(struct h2_sess *, const uint8_t *); + void h2_req_body(struct req*); ++int h2_rapid_reset_check(struct worker *wrk, struct h2_sess *h2, ++ const struct h2_req *r2); ++h2_error h2_rapid_reset_charge(struct worker *wrk, struct h2_sess *h2, ++ const struct h2_req *r2); + task_func_t h2_do_req; + #ifdef TRANSPORT_MAGIC + vtr_req_fail_f h2_req_fail; +diff --git a/bin/varnishd/http2/cache_http2_proto.c b/bin/varnishd/http2/cache_http2_proto.c +index 1f779f5..6c65db2 100644 +--- a/bin/varnishd/http2/cache_http2_proto.c ++++ b/bin/varnishd/http2/cache_http2_proto.c +@@ -329,14 +329,14 @@ h2_rx_push_promise(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) + /********************************************************************** + */ + +-static h2_error +-h2_rapid_reset(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) ++int ++h2_rapid_reset_check(struct worker *wrk, struct h2_sess *h2, ++ const struct h2_req *r2) + { + vtim_real now; +- vtim_dur d; + + CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); +- ASSERT_RXTHR(h2); ++ CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC); + CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC); + + if (h2->rapid_reset_limit == 0) +@@ -348,6 +348,23 @@ h2_rapid_reset(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) + if (now - r2->req->t_first > h2->rapid_reset) + return (0); + ++ return (1); ++} ++ ++h2_error ++h2_rapid_reset_charge(struct worker *wrk, struct h2_sess *h2, ++ const struct h2_req *r2) ++{ ++ vtim_real now; ++ vtim_dur d; ++ h2_error h2e = NULL; ++ ++ CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); ++ AN(H2_SEND_HELD(h2, r2)); ++ CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC); ++ ++ now = VTIM_real(); ++ + d = now - h2->last_rst; + h2->rst_budget += h2->rapid_reset_limit * d / + h2->rapid_reset_period; +@@ -355,20 +372,23 @@ h2_rapid_reset(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) + h2->rapid_reset_limit); + h2->last_rst = now; + +- if (h2->rst_budget < 1.0) { ++ h2->rst_budget -= 1.0; ++ ++ if (h2->rst_budget < 0) { + Lck_Lock(&h2->sess->mtx); + VSLb(h2->vsl, SLT_Error, "H2: Hit RST limit. Closing session."); + Lck_Unlock(&h2->sess->mtx); +- return (H2CE_RAPID_RESET); ++ h2e = H2CE_RAPID_RESET; ++ H2_Send_GOAWAY(wrk, h2, r2, h2e); + } +- h2->rst_budget -= 1.0; +- return (0); ++ ++ return (h2e); + } + + static h2_error v_matchproto_(h2_rxframe_f) + h2_rx_rst_stream(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) + { +- h2_error h2e; ++ h2_error h2e = NULL; + + CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); + ASSERT_RXTHR(h2); +@@ -378,7 +398,11 @@ h2_rx_rst_stream(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) + return (H2CE_FRAME_SIZE_ERROR); + if (r2 == NULL) + return (0); +- h2e = h2_rapid_reset(wrk, h2, r2); ++ if (h2_rapid_reset_check(wrk, h2, r2)) { ++ H2_Send_Get(wrk, h2, r2); ++ h2e = h2_rapid_reset_charge(wrk, h2, r2); ++ H2_Send_Rel(h2, r2); ++ } + h2_kill_req(wrk, h2, r2, h2_streamerror(vbe32dec(h2->rxf_data))); + return (h2e); + } +diff --git a/bin/varnishd/http2/cache_http2_send.c b/bin/varnishd/http2/cache_http2_send.c +index 0745cd3..cfa2028 100644 +--- a/bin/varnishd/http2/cache_http2_send.c ++++ b/bin/varnishd/http2/cache_http2_send.c +@@ -41,8 +41,6 @@ + #include "vend.h" + #include "vtim.h" + +-#define H2_SEND_HELD(h2, r2) (VTAILQ_FIRST(&(h2)->txqueue) == (r2)) +- + static h2_error + h2_errcheck(const struct h2_req *r2, const struct h2_sess *h2) + { diff --git a/CVE-2025-8671-3.patch b/CVE-2025-8671-3.patch new file mode 100644 index 0000000..46bdc13 --- /dev/null +++ b/CVE-2025-8671-3.patch @@ -0,0 +1,109 @@ +From 5202a6e329651cd0121e9eac78e60b66351a50bf Mon Sep 17 00:00:00 2001 +From: Martin Blix Grydeland +Date: Tue, 1 Jul 2025 15:50:10 +0200 +Subject: [PATCH] H2: Check rapid reset whenever we send a RST frame for a + stream + +This checks and charges the rapid reset budget whenever we send a RST +frame, causing a session error if the budget is exhausted. + +This fixes the reverse rapid reset vulnerability. +--- + bin/varnishd/http2/cache_http2_send.c | 6 +++ + bin/varnishtest/tests/f00017.vtc | 63 +++++++++++++++++++++++++++ + 2 files changed, 69 insertions(+) + create mode 100644 bin/varnishtest/tests/f00017.vtc + +diff --git a/bin/varnishd/http2/cache_http2_send.c b/bin/varnishd/http2/cache_http2_send.c +index 41c45961b3..3461bdb380 100644 +--- a/bin/varnishd/http2/cache_http2_send.c ++++ b/bin/varnishd/http2/cache_http2_send.c +@@ -413,6 +413,7 @@ H2_Send_RST(struct worker *wrk, struct h2_sess *h2, const struct h2_req *r2, + uint32_t stream, h2_error h2e) + { + char b[4]; ++ h2_error h2e_rr = NULL; + + CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC); + CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC); +@@ -423,6 +424,11 @@ H2_Send_RST(struct worker *wrk, struct h2_sess *h2, const struct h2_req *r2, + vbe32enc(b, h2e->val); + + H2_Send_Frame(wrk, h2, H2_F_RST_STREAM, 0, sizeof b, stream, b); ++ ++ if (h2_rapid_reset_check(wrk, h2, r2)) ++ h2e_rr = h2_rapid_reset_charge(wrk, h2, r2); ++ if (h2e_rr != NULL) ++ h2->error = h2e_rr; + } + + void +diff --git a/bin/varnishtest/tests/f00017.vtc b/bin/varnishtest/tests/f00017.vtc +new file mode 100644 +index 0000000000..6370652bef +--- /dev/null ++++ b/bin/varnishtest/tests/f00017.vtc +@@ -0,0 +1,63 @@ ++varnishtest "h2 reverse rapid reset" ++ ++barrier b1 sock 2 -cyclic ++barrier b2 sock 5 -cyclic ++ ++server s1 { ++ rxreq ++ txresp ++} -start ++ ++varnish v1 -cliok "param.set feature +http2" ++varnish v1 -cliok "param.set debug +syncvsl" ++varnish v1 -cliok "param.set h2_rapid_reset_limit 3" ++varnish v1 -cliok "param.set h2_rapid_reset 5" ++ ++varnish v1 -vcl+backend { ++ import vtc; ++ ++ sub vcl_recv { ++ if (req.http.barrier) { ++ vtc.barrier_sync(req.http.barrier); ++ } ++ vtc.barrier_sync("${b2_sock}"); ++ } ++ ++} -start ++ ++client c1 { ++ stream 0 { ++ rxgoaway ++ expect goaway.err == ENHANCE_YOUR_CALM ++ } -start ++ ++ stream 1 { ++ txreq -hdr barrier ${b1_sock} ++ barrier b1 sync ++ txwinup -size 0 ++ rxrst ++ } -run ++ stream 3 { ++ txreq -hdr barrier ${b1_sock} ++ barrier b1 sync ++ txwinup -size 0 ++ rxrst ++ } -run ++ stream 5 { ++ txreq -hdr barrier ${b1_sock} ++ barrier b1 sync ++ txwinup -size 0 ++ rxrst ++ } -run ++ stream 7 { ++ txreq -hdr barrier ${b1_sock} ++ barrier b1 sync ++ txwinup -size 0 ++ rxrst ++ } -run ++ ++ barrier b2 sync ++ stream 0 -wait ++} -run ++ ++varnish v1 -expect sc_rapid_reset == 1 diff --git a/varnish.spec b/varnish.spec index d1724e8..f2b05c5 100644 --- a/varnish.spec +++ b/varnish.spec @@ -3,7 +3,7 @@ Name: varnish Summary: A web application accelerator Version: 7.4.3 -Release: 3 +Release: 4 License: BSD-2-Clause URL: https://www.varnish-cache.org/ Source0: http://varnish-cache.org/_downloads/varnish-%{version}.tgz @@ -17,6 +17,12 @@ Patch0004: CVE-2025-47905-1.patch Patch0005: CVE-2025-47905-2.patch Patch0006: CVE-2025-47905-3.patch Patch0007: CVE-2025-47905-4.patch +# https://github.com/varnishcache/varnish-cache/commit/1ce9a6df61555bdc8bd491feb3b5f1ed3f5cbdaf +Patch0008: CVE-2025-8671-1.patch +# https://github.com/varnishcache/varnish-cache/commit/a629c334c98a3b946e529b9d4ae4c07c0967b8bb +Patch0009: CVE-2025-8671-2.patch +# https://github.com/varnishcache/varnish-cache/commit/5202a6e329651cd0121e9eac78e60b66351a50bf +Patch0010: CVE-2025-8671-3.patch BuildRequires: python3-sphinx python3-docutils pkgconfig make graphviz nghttp2 systemd-units BuildRequires: ncurses-devel pcre2-devel libedit-devel gcc @@ -165,6 +171,9 @@ test -f /etc/varnish/secret || (uuidgen > /etc/varnish/secret && chmod 0600 /etc %{_mandir}/man7/*.7* %changelog +* Fri Aug 15 2025 liweigang - 7.4.3-4 +- Fix CVE-2025-8671 + * Mon May 19 2025 yaoxin <1024769339@qq.com> - 7.4.3-3 - Fix CVE-2025-47905 -- Gitee