Compare commits

...
Sign in to create a new pull request.

5 commits

Author SHA1 Message Date
Ingvar Hagelund
9ec6fa9592 run check 2025-07-30 00:54:57 +02:00
Ingvar Hagelund
f636b2b0f4 Added security patch for VSV00016 aka CVE-2025-47905, rhbz#2369404 2025-07-30 00:52:40 +02:00
Ingvar Hagelund
b055f8da2c Added fix prohibiting build on s390x 2025-03-25 15:59:21 +01:00
Ingvar Hagelund
4fc1b59674 Added security patch VSV00015 aka CVE-2025-30346, rhbz#2354008 2025-03-25 12:56:03 +01:00
Ingvar Hagelund
e729d24ebf Added security patch VSV00015 aka CVS-2025-30346 2025-03-24 21:51:47 +01:00
4 changed files with 462 additions and 5 deletions

46
VSV00015.patch Normal file
View file

@ -0,0 +1,46 @@
commit 8ef69a03b36aeac5f364c01eb20f821860e47f14
Author: Dag Haavi Finstad <daghf@varnish-software.com>
Date: Fri Jan 10 13:07:54 2025 +0100
req_fsm: Close the connection on a malformed request
diff --git a/bin/varnishd/cache/cache_req_fsm.c b/bin/varnishd/cache/cache_req_fsm.c
index 1004cbc5f..803810210 100644
--- a/bin/varnishd/cache/cache_req_fsm.c
+++ b/bin/varnishd/cache/cache_req_fsm.c
@@ -962,6 +962,7 @@ cnt_recv(struct worker *wrk, struct req *req)
if (http_CountHdr(req->http0, H_Host) > 1) {
VSLb(req->vsl, SLT_BogoHeader, "Multiple Host: headers");
wrk->stats->client_req_400++;
+ req->doclose = SC_RX_BAD;
(void)req->transport->minimal_response(req, 400);
return (REQ_FSM_DONE);
}
@@ -969,6 +970,7 @@ cnt_recv(struct worker *wrk, struct req *req)
if (http_CountHdr(req->http0, H_Content_Length) > 1) {
VSLb(req->vsl, SLT_BogoHeader, "Multiple Content-Length: headers");
wrk->stats->client_req_400++;
+ req->doclose = SC_RX_BAD;
(void)req->transport->minimal_response(req, 400);
return (REQ_FSM_DONE);
}
diff --git a/bin/varnishtest/tests/b00037.vtc b/bin/varnishtest/tests/b00037.vtc
index ce0e84112..e6185bd07 100644
--- a/bin/varnishtest/tests/b00037.vtc
+++ b/bin/varnishtest/tests/b00037.vtc
@@ -11,6 +11,7 @@ client c1 {
varnish v1 -vsl_catchup
varnish v1 -expect client_req_400 == 1
+varnish v1 -expect sc_rx_bad == 1
client c1 {
txreq -method POST -hdr "Content-Length: 12" -hdr "Content-Length: 12" -bodylen 12
@@ -20,6 +21,7 @@ client c1 {
varnish v1 -vsl_catchup
varnish v1 -expect client_req_400 == 2
+varnish v1 -expect sc_rx_bad == 2
varnish v1 -cliok "param.set feature +http2"

215
VSV00016.patch Normal file
View file

@ -0,0 +1,215 @@
based on commit 6dd6607c93af5a9cc663a174aefc3a0aeff8f26c Mon Sep 17 00:00:00 2001
From: Nils Goroll <nils.goroll@uplex.de>
Date: Mon, 30 May 2022 13:09:11 +0200
Subject: [PATCH] v1f: Read end-of-chunk as part of the chunk
Until now, we read the (CR)?LF at the end of a chunk as part of the
next chunk header (see: /* Skip leading whitespace */).
For a follow up commit, we are going to want to know if the next chunk
header is available for read, so we now consume the chunk end as part
of the chunk itself.
This also fixes a corner case: We previously accepted chunks with a
missing end-of-chunk (see fix of r01729.vtc).
Ref: https://datatracker.ietf.org/doc/html/rfc7230#section-4.1
---
bin/varnishtest/tests/r01184.vtc | 2 ++
bin/varnishtest/tests/r01506.vtc | 16 ++++++++--------
bin/varnishtest/tests/r01729.vtc | 6 +++---
4 files changed, 39 insertions(+), 21 deletions(-)
--- bin/varnishd/http1/cache_http1_vfp.c.orig 2024-11-08 11:47:35.000000000 +0100
+++ bin/varnishd/http1/cache_http1_vfp.c 2025-07-30 00:09:27.239254185 +0200
@@ -88,77 +88,116 @@
return (i + l);
}
+/*--------------------------------------------------------------------
+ * read (CR)?LF at the end of a chunk
+ */
+static enum vfp_status
+v1f_chunk_end(struct vfp_ctx *vc, struct http_conn *htc)
+{
+ char c;
+
+ if (v1f_read(vc, htc, &c, 1) <= 0)
+ return (VFP_Error(vc, "chunked read err"));
+ if (c == '\r' && v1f_read(vc, htc, &c, 1) <= 0)
+ return (VFP_Error(vc, "chunked read err"));
+ if (c != '\n')
+ return (VFP_Error(vc, "chunked tail no NL"));
+ return (VFP_OK);
+}
+
/*--------------------------------------------------------------------
- * Read a chunked HTTP object.
+ * Parse a chunk header and, for VFP_OK, return size in a pointer
*
* XXX: Reading one byte at a time is pretty pessimal.
*/
-static enum vfp_status v_matchproto_(vfp_pull_f)
-v1f_chunked_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr,
- ssize_t *lp)
+static enum vfp_status
+v1f_chunked_hdr(struct vfp_ctx *vc, struct http_conn *htc, ssize_t *szp)
{
- struct http_conn *htc;
char buf[20]; /* XXX: 20 is arbitrary */
- char *q;
unsigned u;
uintmax_t cll;
- ssize_t cl, l, lr;
+ ssize_t cl, lr;
+ char *q;
CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
- CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
- CAST_OBJ_NOTNULL(htc, vfe->priv1, HTTP_CONN_MAGIC);
- AN(ptr);
- AN(lp);
+ CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
+ AN(szp);
+ assert(*szp == -1);
+
+ /* Skip leading whitespace */
+ do {
+ lr = v1f_read(vc, htc, buf, 1);
+ if (lr <= 0)
+ return (VFP_Error(vc, "chunked read err"));
+ } while (vct_isows(buf[0]));
- l = *lp;
- *lp = 0;
- if (vfe->priv2 == -1) {
- /* Skip leading whitespace */
+ if (!vct_ishex(buf[0]))
+ return (VFP_Error(vc, "chunked header non-hex"));
+
+ /* Collect hex digits, skipping leading zeros */
+ for (u = 1; u < sizeof buf; u++) {
do {
- lr = v1f_read(vc, htc, buf, 1);
+ lr = v1f_read(vc, htc, buf + u, 1);
if (lr <= 0)
return (VFP_Error(vc, "chunked read err"));
- } while (vct_islws(buf[0]));
+ } while (u == 1 && buf[0] == '0' && buf[u] == '0');
+ if (!vct_ishex(buf[u]))
+ break;
+ }
+ if (u >= sizeof buf)
+ return (VFP_Error(vc, "chunked header too long"));
- if (!vct_ishex(buf[0]))
- return (VFP_Error(vc, "chunked header non-hex"));
+ /* Skip trailing white space */
+ while (vct_isows(buf[u])) {
+ lr = v1f_read(vc, htc, buf + u, 1);
+ if (lr <= 0)
+ return (VFP_Error(vc, "chunked read err"));
+ }
+ if (buf[u] == '\r' && v1f_read(vc, htc, buf + u, 1) <= 0)
+ return (VFP_Error(vc, "chunked read err"));
+ if (buf[u] != '\n')
+ return (VFP_Error(vc, "chunked header no NL"));
- /* Collect hex digits, skipping leading zeros */
- for (u = 1; u < sizeof buf; u++) {
- do {
- lr = v1f_read(vc, htc, buf + u, 1);
- if (lr <= 0)
- return (VFP_Error(vc, "chunked read err"));
- } while (u == 1 && buf[0] == '0' && buf[u] == '0');
- if (!vct_ishex(buf[u]))
- break;
- }
+ buf[u] = '\0';
+ cll = strtoumax(buf, &q, 16);
+ if (q == NULL || *q != '\0')
+ return (VFP_Error(vc, "chunked header number syntax"));
+ cl = (ssize_t)cll;
+ if (cl < 0 || (uintmax_t)cl != cll)
+ return (VFP_Error(vc, "bogusly large chunk size"));
- if (u >= sizeof buf)
- return (VFP_Error(vc, "chunked header too long"));
+ *szp = cl;
+ return (VFP_OK);
+}
- /* Skip trailing white space */
- while (vct_islws(buf[u]) && buf[u] != '\n') {
- lr = v1f_read(vc, htc, buf + u, 1);
- if (lr <= 0)
- return (VFP_Error(vc, "chunked read err"));
- }
- if (buf[u] != '\n')
- return (VFP_Error(vc, "chunked header no NL"));
+/*--------------------------------------------------------------------
+ * Read a chunked HTTP object.
+ *
+ */
- buf[u] = '\0';
+static enum vfp_status v_matchproto_(vfp_pull_f)
+v1f_chunked_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr,
+ ssize_t *lp)
+{
+ static enum vfp_status vfps;
+ struct http_conn *htc;
+ ssize_t l, lr;
- cll = strtoumax(buf, &q, 16);
- if (q == NULL || *q != '\0')
- return (VFP_Error(vc, "chunked header number syntax"));
- cl = (ssize_t)cll;
- if (cl < 0 || (uintmax_t)cl != cll)
- return (VFP_Error(vc, "bogusly large chunk size"));
+ CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
+ CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
+ CAST_OBJ_NOTNULL(htc, vfe->priv1, HTTP_CONN_MAGIC);
+ AN(ptr);
+ AN(lp);
- vfe->priv2 = cl;
+ l = *lp;
+ *lp = 0;
+ if (vfe->priv2 == -1) {
+ vfps = v1f_chunked_hdr(vc, htc, &vfe->priv2);
+ if (vfps != VFP_OK)
+ return (vfps);
}
if (vfe->priv2 > 0) {
if (vfe->priv2 < l)
@@ -168,18 +207,15 @@
return (VFP_Error(vc, "chunked insufficient bytes"));
*lp = lr;
vfe->priv2 -= lr;
- if (vfe->priv2 == 0)
- vfe->priv2 = -1;
- return (VFP_OK);
+ if (vfe->priv2 != 0)
+ return (VFP_OK);
+
+ vfe->priv2 = -1;
+ return (v1f_chunk_end(vc, htc));
}
AZ(vfe->priv2);
- if (v1f_read(vc, htc, buf, 1) <= 0)
- return (VFP_Error(vc, "chunked read err"));
- if (buf[0] == '\r' && v1f_read(vc, htc, buf, 1) <= 0)
- return (VFP_Error(vc, "chunked read err"));
- if (buf[0] != '\n')
- return (VFP_Error(vc, "chunked tail no NL"));
- return (VFP_END);
+ vfps = v1f_chunk_end(vc, htc);
+ return (vfps == VFP_OK ? VFP_END : vfps);
}
static const struct vfp v1f_chunked = {

168
VSV00016.test.patch Normal file
View file

@ -0,0 +1,168 @@
From c1e6774ae5b7e5fdd7aef7352170b1d6e7613070 Mon Sep 17 00:00:00 2001
From: Dridi Boukelmoune <dridi.boukelmoune@gmail.com>
Date: Thu, 3 Apr 2025 15:52:56 +0200
Subject: [PATCH] vtc: Test coverage for VSV16
---
bin/varnishtest/tests/f00016.vtc | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 69 insertions(+)
create mode 100644 bin/varnishtest/tests/f00016.vtc
diff --git a/bin/varnishtest/tests/f00016.vtc b/bin/varnishtest/tests/f00016.vtc
new file mode 100644
index 0000000..a38b8b1
--- /dev/null
+++ b/bin/varnishtest/tests/f00016.vtc
@@ -0,0 +1,69 @@
+varnishtest "Do not tolerate anything else than CRLF as chunked ending"
+
+server s0 {
+ rxreq
+ expect_close
+} -dispatch
+
+varnish v1 -vcl+backend {} -start
+
+logexpect l1 -v v1 {
+ expect * 1001 FetchError "chunked tail no NL"
+ expect * 1004 FetchError "chunked tail no NL"
+ expect * 1007 FetchError "chunked header non-hex"
+ expect * 1010 FetchError "chunked header non-hex"
+} -start
+
+client c1 {
+ non_fatal
+ txreq -req POST -hdr "Transfer-encoding: chunked"
+ send "1\r\n"
+ send "This is more than one byte of data\r\n"
+ send "0\r\n"
+ send "\r\n"
+ fatal
+ rxresp
+ expect resp.status == 503
+ expect_close
+} -run
+
+client c2 {
+ non_fatal
+ txreq -req POST -hdr "Transfer-encoding: chunked"
+ send "1\r\n"
+ send "Z 2\r\n"
+ send "3d\r\n"
+ send "0\r\n\r\nPOST /evil HTTP/1.1\r\nHost: whatever\r\nContent-Length: 5\r\n\r\n"
+ send "0\r\n"
+ send "\r\n"
+ fatal
+ rxresp
+ expect resp.status == 503
+ expect_close
+} -run
+
+client c3 {
+ non_fatal
+ txreq -req POST -hdr "Transfer-encoding: chunked"
+ send "d\r\n"
+ send "Spurious CRLF\r\n\r\n"
+ send "0\r\n"
+ send "\r\n"
+ fatal
+ rxresp
+ expect resp.status == 503
+ expect_close
+} -run
+
+client c4 {
+ non_fatal
+ txreq -req POST -hdr "Transfer-encoding: chunked"
+ send "\n0\r\n"
+ send "\r\n"
+ fatal
+ rxresp
+ expect resp.status == 503
+ expect_close
+} -run
+
+logexpect l1 -wait
--
diff --git a/bin/varnishtest/tests/r01184.vtc b/bin/varnishtest/tests/r01184.vtc
index 0988e65..94ecd3c 100644
--- a/bin/varnishtest/tests/r01184.vtc
+++ b/bin/varnishtest/tests/r01184.vtc
@@ -62,6 +62,7 @@ server s1 {
sendhex " 10 45 f3 a9 83 b8 18 1c 7b c2 30 55 04 17 13 c4"
sendhex " 0f 07 5f 7a 38 f4 8e 50 b3 37 d4 3a 32 4a 34 07"
sendhex " FF FF FF FF FF FF FF FF 72 ea 06 5f b3 1c fa dd"
+ send "\n"
expect_close
} -start
@@ -93,6 +94,7 @@ server s1 {
sendhex " 10 45 f3 a9 83 b8 18 1c 7b c2 30 55 04 17 13 c4"
sendhex " 0f 07 5f 7a 38 f4 8e 50 b3 37 d4 3a 32 4a 34 07"
sendhex " FF FF FF FF FF FF FF FF 72 ea 06 5f b3 1c fa dd"
+ send "\n"
expect_close
} -start
diff --git a/bin/varnishtest/tests/r01506.vtc b/bin/varnishtest/tests/r01506.vtc
index 96b7b54..f7f89a7 100644
--- a/bin/varnishtest/tests/r01506.vtc
+++ b/bin/varnishtest/tests/r01506.vtc
@@ -7,15 +7,15 @@ server s0 {
txresp -nolen \
-hdr "Transfer-Encoding: chunked" \
-hdr "Connection: close"
- send "11\r\n0_23456789abcdef\n"
- send "11\r\n1_23456789abcdef\n"
- send "11\r\n2_23456789abcdef\n"
- send "11\r\n3_23456789abcdef\n"
+ send "11\r\n0_23456789abcdef\n\n"
+ send "11\r\n1_23456789abcdef\n\n"
+ send "11\r\n2_23456789abcdef\n\n"
+ send "11\r\n3_23456789abcdef\n\n"
barrier b1 sync
- send "11\r\n4_23456789abcdef\n"
- send "11\r\n5_23456789abcdef\n"
- send "11\r\n6_23456789abcdef\n"
- send "11\r\n7_23456789abcdef\n"
+ send "11\r\n4_23456789abcdef\n\n"
+ send "11\r\n5_23456789abcdef\n\n"
+ send "11\r\n6_23456789abcdef\n\n"
+ send "11\r\n7_23456789abcdef\n\n"
chunkedlen 0
} -dispatch
diff --git a/bin/varnishtest/tests/r01729.vtc b/bin/varnishtest/tests/r01729.vtc
index 883a60c..f6a01e9 100644
--- a/bin/varnishtest/tests/r01729.vtc
+++ b/bin/varnishtest/tests/r01729.vtc
@@ -11,7 +11,7 @@ server s1 {
send "\r\n"
send "14\r\n"
send "0123456789"
- send "0123456789"
+ send "0123456789\n"
send "0\r\n"
send "\r\n"
@@ -29,7 +29,7 @@ client c1 {
send "\r\n"
send "14\r\n"
send "0123456789"
- send "0123456789"
+ send "0123456789\n"
send "0\r\n"
send "\r\n"
@@ -45,7 +45,7 @@ client c1 {
send "\r\n"
send "14\r\n"
send "0123456789"
- send "0123456789"
+ send "0123456789\n"
send "0\r\n"
send "\r\n"
--
libgit2 1.9.0

View file

@ -37,12 +37,19 @@
Summary: High-performance HTTP accelerator
Name: varnish
Version: 7.6.1
Release: 2%{?dist}
Release: 6%{?dist}
License: BSD-2-Clause AND (BSD-2-Clause-FreeBSD AND BSD-3-Clause AND LicenseRef-Fedora-Public-Domain AND Zlib)
URL: https://www.varnish-cache.org/
Source0: http://varnish-cache.org/_downloads/%{name}-%{version}.tgz
Source1: https://github.com/varnishcache/pkg-varnish-cache/archive/%{commit1}.tar.gz#/pkg-varnish-cache-%{shortcommit1}.tar.gz
# CVE-2025-30346, patch from upstream
Patch1: VSV00015.patch
# CVE-2025-47905, patches from upstream
Patch2: VSV00016.patch
Patch3: VSV00016.test.patch
%if 0%{?fedora} > 29 || 0%{?rhel} > 7
Provides: varnish%{_isa} = %{version}-%{release}
Provides: varnishd(abi)%{_isa} = %{abi}
@ -140,12 +147,20 @@ Documentation files for %name
%prep
%setup -q
%patch 1 -p1
%patch 2 -p0
%patch 3 -p1
tar xzf %SOURCE1
ln -s pkg-varnish-cache-%{commit1}/redhat redhat
ln -s pkg-varnish-cache-%{commit1}/debian debian
cp redhat/find-provides .
sed -i 's,rst2man-3.6,rst2man-3.4,g; s,rst2html-3.6,rst2html-3.4,g; s,phinx-build-3.6,phinx-build-3.4,g' configure
# This is a bug in varnishtest making it incompatible with nghttp2 >= 1.65
%if 0%{?fedora} > 41
rm bin/varnishtest/tests/a02022.vtc
%endif
%build
%if %{with system_allocator}
export CFLAGS="%{optflags}"
@ -162,6 +177,10 @@ export CFLAGS="$CFLAGS -ffloat-store -fexcess-precision=standard"
%endif
%endif
%if 0%{?fedora} > 41
export CFLAGS="$CFLAGS -std=gnu17"
%endif
%ifarch s390x
export CFLAGS="$CFLAGS -Wno-error=free-nonheap-object"
%endif
@ -208,11 +227,9 @@ rm -rf doc/html/_sources
sed -i 's/thread_pool_stack 80k/thread_pool_stack 128k/g;' bin/varnishtest/tests/*.vtc
sed -i 's/file,2M/file,8M/' bin/varnishtest/tests/r04036.vtc
# Just a hack to avoid too high load on secondary arch builders
# Avoid too high load on special arch builders
%ifarch s390x ppc64le
# This works when ran alone, but not in the whole suite. Load and/or timing issues
rm bin/varnishtest/tests/t02014.vtc
make -j2 check
make check
%else
%make_build check
%endif
@ -253,7 +270,9 @@ chmod 644 lib/libvmod_*/*.h
%endif
%files
%if 0%{?rhel} > 0 || 0%{?fedora} < 42
%{_sbindir}/*
%endif
%{_bindir}/*
%{_libdir}/*.so.*
%{_libdir}/%{name}
@ -313,6 +332,15 @@ test -f /etc/varnish/secret || (uuidgen > /etc/varnish/secret && chmod 0600 /etc
%changelog
* Wed Jul 30 2025 Ingvar Hagelund <ingvar@redpill-linpro.com> - 7.6.1-6
- Added security patch for VSV00016 aka CVE-2025-47905, rhbz#2369404
* Mon Mar 24 2025 Ingvar Hagelund <ingvar@redpill-linpro.com> - 7.6.1-5
- Added fix prohibiting build on s390x
* Mon Mar 24 2025 Ingvar Hagelund <ingvar@redpill-linpro.com> - 7.6.1-4
- Added security patch VSV00015 aka CVE-2025-30346, rhbz#2354008
* Sun Jan 19 2025 Fedora Release Engineering <releng@fedoraproject.org> - 7.6.1-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild