squid: patch CVE-2026-33515

Pick SQUID-2026:3 patch per [1].

[1] https://github.com/squid-cache/squid/security/advisories/GHSA-84p4-hcx7-jj7c

Signed-off-by: Peter Marko <peter.marko@siemens.com>
Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
Peter Marko authored and Anuj Mittal committed 2026-10-26 10:10:45 +05:30
1 parent 4cf01e38f6
commit d657b86f28
2 files changed
+200

No files matched your search

@@ -0,0 +1,199 @@
From 8138e909d2058d4401e0ad49b583afaec912b165 Mon Sep 17 00:00:00 2001
From: Joshua Rogers <MegaManSec@users.noreply.github.com>
Date: Thu, 12 Feb 2026 20:28:43 +0000
Subject: [PATCH] ICP: Fix validation of packet sizes and URLs (#2220)
Fix handling of malformed ICP queries and replies instead of passing
invalid URL pointer to consumers, leading to out-of-bounds memory reads
and other problems. These fixes affect both ICP v2 and ICP v3 traffic.
* Reject packets with URLs that are not NUL-terminated.
* Reject packets with URLs containing embedded NULs or trailing garbage.
The above two restrictions may backfire if popular ICP agents do send
such malformed URLs, and we will need to do more to handle them
correctly, but it is _safe_ to reject them for now.
Also protect icpHandleUdp() from dereferencing a nil icpOutgoingConn
pointer. It is not clear whether icpHandleUdp() can be exposed to nil
icpOutgoingConn in current code. More work is needed to polish this.
CVE: CVE-2026-33515
Upstream-Status: Backport [https://github.com/squid-cache/squid/commit/8138e909d2058d4401e0ad49b583afaec912b165]
Signed-off-by: Peter Marko <peter.marko@siemens.com>
---
src/ICP.h | 8 ++++--
src/icp_v2.cc | 57 +++++++++++++++++++++++++++++++++++++------
src/icp_v3.cc | 10 +++++---
src/tests/stub_icp.cc | 5 ++--
4 files changed, 66 insertions(+), 14 deletions(-)
diff --git a/src/ICP.h b/src/ICP.h
index 10a8e1c67..e9ad8d0ce 100644
--- a/src/ICP.h
+++ b/src/ICP.h
@@ -89,8 +89,12 @@ extern Comm::ConnectionPointer icpIncomingConn;
extern Comm::ConnectionPointer icpOutgoingConn;
extern Ip::Address theIcpPublicHostID;
+/// A URI extracted from the given raw packet buffer.
+/// On errors, details the problem and returns nil.
+const char *icpGetUrl(const Ip::Address &from, const char *, const icp_common_t &);
+
/// \ingroup ServerProtocolICPAPI
-HttpRequest* icpGetRequest(char *url, int reqnum, int fd, Ip::Address &from);
+HttpRequest *icpGetRequest(const char *url, int reqnum, int fd, const Ip::Address &from);
/// \ingroup ServerProtocolICPAPI
bool icpAccessAllowed(Ip::Address &from, HttpRequest * icp_request);
@@ -102,7 +106,7 @@ void icpCreateAndSend(icp_opcode, int flags, char const *url, int reqnum, int pa
icp_opcode icpGetCommonOpcode();
/// \ingroup ServerProtocolICPAPI
-void icpDenyAccess(Ip::Address &from, char *url, int reqnum, int fd);
+void icpDenyAccess(const Ip::Address &from, const char *url, int reqnum, int fd);
/// \ingroup ServerProtocolICPAPI
PF icpHandleUdp;
diff --git a/src/icp_v2.cc b/src/icp_v2.cc
index 25f7b71d2..312104024 100644
--- a/src/icp_v2.cc
+++ b/src/icp_v2.cc
@@ -425,7 +425,7 @@ icpCreateAndSend(icp_opcode opcode, int flags, char const *url, int reqnum, int
}
void
-icpDenyAccess(Ip::Address &from, char *url, int reqnum, int fd)
+icpDenyAccess(const Ip::Address &from, const char * const url, const int reqnum, const int fd)
{
debugs(12, 2, "icpDenyAccess: Access Denied for " << from << " by " << AclMatchedName << ".");
@@ -453,8 +453,41 @@ icpAccessAllowed(Ip::Address &from, HttpRequest * icp_request)
return checklist.fastCheck().allowed();
}
+const char *
+icpGetUrl(const Ip::Address &from, const char * const buf, const icp_common_t &header)
+{
+ const auto receivedPacketSize = static_cast<size_t>(header.length);
+ const auto payloadOffset = sizeof(header);
+
+ // Query payload contains a "Requester Host Address" followed by a URL.
+ // Payload of other ICP packets (with opcode that we recognize) is a URL.
+ const auto urlOffset = payloadOffset + ((header.opcode == ICP_QUERY) ? sizeof(uint32_t) : 0);
+
+ // A URL field cannot be empty because it includes a terminating NUL char.
+ // Ensure that the packet has at least one URL field byte.
+ if (urlOffset >= receivedPacketSize) {
+ debugs(12, 3, "too small packet from " << from << ": " << urlOffset << " >= " << receivedPacketSize);
+ return nullptr;
+ }
+
+ // All ICP packets (with opcode that we recognize) _end_ with a URL field.
+ // RFC 2186 requires all URLs to be "Null-Terminated".
+ if (buf[receivedPacketSize - 1] != '\0') {
+ debugs(12, 3, "unterminated URL or trailing garbage from " << from);
+ return nullptr;
+ }
+
+ const auto url = buf + urlOffset; // a possibly empty c-string
+ if (urlOffset + strlen(url) + 1 != receivedPacketSize) {
+ debugs(12, 3, "URL with an embedded NUL or trailing garbage from " << from);
+ return nullptr;
+ }
+
+ return url;
+}
+
HttpRequest *
-icpGetRequest(char *url, int reqnum, int fd, Ip::Address &from)
+icpGetRequest(const char * const url, const int reqnum, const int fd, const Ip::Address &from)
{
if (strpbrk(url, w_space)) {
icpCreateAndSend(ICP_ERR, 0, rfc1738_escape(url), reqnum, 0, fd, from, nullptr);
@@ -471,13 +504,18 @@ icpGetRequest(char *url, int reqnum, int fd, Ip::Address &from)
}
static void
-doV2Query(int fd, Ip::Address &from, char *buf, icp_common_t header)
+doV2Query(const int fd, Ip::Address &from, const char * const buf, icp_common_t header)
{
int rtt = 0;
int src_rtt = 0;
uint32_t flags = 0;
- /* We have a valid packet */
- char *url = buf + sizeof(icp_common_t) + sizeof(uint32_t);
+
+ const auto url = icpGetUrl(from, buf, header);
+ if (!url) {
+ icpCreateAndSend(ICP_ERR, 0, "", header.reqnum, 0, fd, from, nullptr);
+ return;
+ }
+
HttpRequest *icp_request = icpGetRequest(url, header.reqnum, fd, from);
if (!icp_request)
@@ -544,7 +582,9 @@ icp_common_t::handleReply(char *buf, Ip::Address &from)
neighbors_do_private_keys = 0;
}
- char *url = buf + sizeof(icp_common_t);
+ const auto url = icpGetUrl(from, buf, *this);
+ if (!url)
+ return;
debugs(12, 3, "icpHandleIcpV2: " << icp_opcode_str[opcode] << " from " << from << " for '" << url << "'");
const cache_key *key = icpGetCacheKey(url, (int) reqnum);
@@ -679,7 +719,10 @@ icpHandleUdp(int sock, void *)
icp_version = (int) buf[1]; /* cheat! */
- if (icpOutgoingConn->local == from)
+ // XXX: The IP equality comparison below ignores port differences but
+ // should not. It also fails to detect loops when `local` is a wildcard
+ // address (e.g., [::]:3130) because `from` address is never a wildcard.
+ if (icpOutgoingConn && icpOutgoingConn->local == from)
// ignore ICP packets which loop back (multicast usually)
debugs(12, 4, "icpHandleUdp: Ignoring UDP packet sent by myself");
else if (icp_version == ICP_VERSION_2)
diff --git a/src/icp_v3.cc b/src/icp_v3.cc
index c912dd4c0..844768aa0 100644
--- a/src/icp_v3.cc
+++ b/src/icp_v3.cc
@@ -32,10 +32,14 @@ public:
/// \ingroup ServerProtocolICPInternal3
static void
-doV3Query(int fd, Ip::Address &from, char *buf, icp_common_t header)
+doV3Query(int fd, Ip::Address &from, const char * const buf, icp_common_t header)
{
- /* We have a valid packet */
- char *url = buf + sizeof(icp_common_t) + sizeof(uint32_t);
+ const auto url = icpGetUrl(from, buf, header);
+ if (!url) {
+ icpCreateAndSend(ICP_ERR, 0, "", header.reqnum, 0, fd, from, nullptr);
+ return;
+ }
+
HttpRequest *icp_request = icpGetRequest(url, header.reqnum, fd, from);
if (!icp_request)
diff --git a/src/tests/stub_icp.cc b/src/tests/stub_icp.cc
index 44091838b..d148ab66d 100644
--- a/src/tests/stub_icp.cc
+++ b/src/tests/stub_icp.cc
@@ -29,11 +29,12 @@ Comm::ConnectionPointer icpIncomingConn;
Comm::ConnectionPointer icpOutgoingConn;
Ip::Address theIcpPublicHostID;
-HttpRequest* icpGetRequest(char *, int, int, Ip::Address &) STUB_RETVAL(nullptr)
+const char *icpGetUrl(const Ip::Address &, const char *, const icp_common_t &) STUB_RETVAL(nullptr)
+HttpRequest* icpGetRequest(const char *, int, int, const Ip::Address &) STUB_RETVAL(nullptr)
bool icpAccessAllowed(Ip::Address &, HttpRequest *) STUB_RETVAL(false)
void icpCreateAndSend(icp_opcode, int, char const *, int, int, int, const Ip::Address &, AccessLogEntryPointer) STUB
icp_opcode icpGetCommonOpcode() STUB_RETVAL(ICP_INVALID)
-void icpDenyAccess(Ip::Address &, char *, int, int) STUB
+void icpDenyAccess(const Ip::Address &, const char *, int, int) STUB
void icpHandleIcpV3(int, Ip::Address &, char *, int) STUB
void icpConnectionShutdown(void) STUB
int icpSetCacheKey(const cache_key *) STUB_RETVAL(0)
@@ -23,6 +23,7 @@ SRC_URI = "https://github.com/squid-cache/${BPN}/releases/download/SQUID_${PV_U}
file://CVE-2025-59362.patch \
file://CVE-2025-62168.patch \
file://CVE-2026-33526.patch \
file://CVE-2026-33515.patch \
"
SRC_URI[sha256sum] = "9eafe06f58a199b918e79d33d8aa03afb9ae0c11d18974dca0b44c2669cab6dd"