Compare commits
10 Commits
9377674d72
...
3387afcddf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3387afcddf | ||
|
|
9ea73ab45e | ||
|
|
cbb3ba32d1 | ||
|
|
2e9b788a12 | ||
|
|
b3ffde3e0d | ||
|
|
e8a9d9d09d | ||
|
|
7101e68198 | ||
|
|
7614078a30 | ||
|
|
87ed4bdb7c | ||
|
|
e4498955c2 |
36
backport-CVE-2024-23638.patch
Normal file
36
backport-CVE-2024-23638.patch
Normal file
@ -0,0 +1,36 @@
|
||||
From 5bede3305cabb9ac19babecf3ebaf64f43f7b53e Mon Sep 17 00:00:00 2001
|
||||
From: Alex Rousskov <rousskov@measurement-factory.com>
|
||||
Date: Sun, 12 Nov 2023 09:33:20 +0000
|
||||
Subject: [PATCH] Do not update StoreEntry expiration after errorAppendEntry()
|
||||
(#1580)
|
||||
|
||||
errorAppendEntry() is responsible for setting entry expiration times,
|
||||
which it does by calling StoreEntry::storeErrorResponse() that calls
|
||||
StoreEntry::negativeCache().
|
||||
|
||||
This change was triggered by a vulnerability report by Joshua Rogers at
|
||||
https://megamansec.github.io/Squid-Security-Audit/cache-uaf.html where
|
||||
it was filed as "Use-After-Free in Cache Manager Errors". The reported
|
||||
"use after free" vulnerability was unknowingly addressed by 2022 commit
|
||||
1fa761a that removed excessively long "reentrant" store_client calls
|
||||
responsible for the disappearance of the properly locked StoreEntry in
|
||||
this (and probably other) contexts.
|
||||
|
||||
Conflict: context adapt
|
||||
Reference: https://github.com/squid-cache/squid/commit/5bede3305cabb9ac19babecf3ebaf64f43f7b53e
|
||||
---
|
||||
src/cache_manager.cc | 1 -
|
||||
1 file changed, 1 deletion(-)
|
||||
|
||||
diff --git a/src/cache_manager.cc b/src/cache_manager.cc
|
||||
index b5a9cbecd33..08445a517a9 100644
|
||||
--- a/src/cache_manager.cc
|
||||
+++ b/src/cache_manager.cc
|
||||
@@ -306,7 +306,6 @@ CacheManager::start(const Comm::ConnectionPointer &client, HttpRequest *request,
|
||||
const auto err = new ErrorState(ERR_INVALID_URL, Http::scNotFound, request);
|
||||
err->url = xstrdup(entry->url());
|
||||
errorAppendEntry(entry, err);
|
||||
- entry->expires = squid_curtime;
|
||||
return;
|
||||
}
|
||||
|
||||
394
backport-CVE-2024-25111.patch
Normal file
394
backport-CVE-2024-25111.patch
Normal file
@ -0,0 +1,394 @@
|
||||
From 4658d0fc049738c2e6cd25fc0af10e820cf4c11a Mon Sep 17 00:00:00 2001
|
||||
From: Markus Koschany <apo@debian.org>
|
||||
Date: Tue, 5 Mar 2024 16:35:19 +0100
|
||||
Subject: CVE-2024-25111
|
||||
|
||||
Origin: http://www.squid-cache.org/Versions/v6/SQUID-2024_1.patch
|
||||
|
||||
Conflict: NA
|
||||
Reference: https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/squid/4.10-1ubuntu1.10/squid_4.10-1ubuntu1.10.debian.tar.xz
|
||||
---
|
||||
src/SquidMath.h | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
|
||||
src/http.cc | 110 +++++++++++++++++++++++++----------
|
||||
src/http.h | 15 ++---
|
||||
3 files changed, 256 insertions(+), 44 deletions(-)
|
||||
|
||||
--- a/src/SquidMath.h
|
||||
+++ b/src/SquidMath.h
|
||||
@@ -1,13 +1,18 @@
|
||||
/*
|
||||
- * Copyright (C) 1996-2019 The Squid Software Foundation and contributors
|
||||
+ * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
|
||||
*
|
||||
* Squid software is distributed under GPLv2+ license and includes
|
||||
* contributions from numerous individuals and organizations.
|
||||
* Please see the COPYING and CONTRIBUTORS files for details.
|
||||
*/
|
||||
|
||||
-#ifndef _SQUID_SRC_SQUIDMATH_H
|
||||
-#define _SQUID_SRC_SQUIDMATH_H
|
||||
+#ifndef SQUID_SRC_SQUIDMATH_H
|
||||
+#define SQUID_SRC_SQUIDMATH_H
|
||||
+
|
||||
+#include <limits>
|
||||
+#include <optional>
|
||||
+
|
||||
+// TODO: Move to src/base/Math.h and drop the Math namespace
|
||||
|
||||
/* Math functions we define locally for Squid */
|
||||
namespace Math
|
||||
@@ -21,5 +26,164 @@ double doubleAverage(const double, const
|
||||
|
||||
} // namespace Math
|
||||
|
||||
-#endif /* _SQUID_SRC_SQUIDMATH_H */
|
||||
+// If Sum() performance becomes important, consider using GCC and clang
|
||||
+// built-ins like __builtin_add_overflow() instead of manual overflow checks.
|
||||
+
|
||||
+/// detects a pair of unsigned types
|
||||
+/// reduces code duplication in declarations further below
|
||||
+template <typename T, typename U>
|
||||
+using AllUnsigned = typename std::conditional<
|
||||
+ std::is_unsigned<T>::value && std::is_unsigned<U>::value,
|
||||
+ std::true_type,
|
||||
+ std::false_type
|
||||
+ >::type;
|
||||
+
|
||||
+// TODO: Replace with std::cmp_less() after migrating to C++20.
|
||||
+/// whether integer a is less than integer b, with correct overflow handling
|
||||
+template <typename A, typename B>
|
||||
+constexpr bool
|
||||
+Less(const A a, const B b) {
|
||||
+ // The casts below make standard C++ integer conversions explicit. They
|
||||
+ // quell compiler warnings about signed/unsigned comparison. The first two
|
||||
+ // lines exclude different-sign a and b, making the casts/comparison safe.
|
||||
+ using AB = typename std::common_type<A, B>::type;
|
||||
+ return
|
||||
+ (a >= 0 && b < 0) ? false :
|
||||
+ (a < 0 && b >= 0) ? true :
|
||||
+ /* (a >= 0) == (b >= 0) */ static_cast<AB>(a) < static_cast<AB>(b);
|
||||
+}
|
||||
+
|
||||
+/// ensure that T is supported by NaturalSum() and friends
|
||||
+template<typename T>
|
||||
+constexpr void
|
||||
+AssertNaturalType()
|
||||
+{
|
||||
+ static_assert(std::numeric_limits<T>::is_bounded, "std::numeric_limits<T>::max() is meaningful");
|
||||
+ static_assert(std::numeric_limits<T>::is_exact, "no silent loss of precision");
|
||||
+ static_assert(!std::is_enum<T>::value, "no silent creation of non-enumerated values");
|
||||
+}
|
||||
+
|
||||
+// TODO: Investigate whether this optimization can be expanded to [signed] types
|
||||
+// A and B when std::numeric_limits<decltype(A(0)+B(0))>::is_modulo is true.
|
||||
+/// This IncreaseSumInternal() overload is optimized for speed.
|
||||
+/// \returns a non-overflowing sum of the two unsigned arguments (or nothing)
|
||||
+/// \prec both argument types are unsigned
|
||||
+template <typename S, typename A, typename B, std::enable_if_t<AllUnsigned<A,B>::value, int> = 0>
|
||||
+std::optional<S>
|
||||
+IncreaseSumInternal(const A a, const B b) {
|
||||
+ // paranoid: AllUnsigned<A,B> precondition established that already
|
||||
+ static_assert(std::is_unsigned<A>::value, "AllUnsigned dispatch worked for A");
|
||||
+ static_assert(std::is_unsigned<B>::value, "AllUnsigned dispatch worked for B");
|
||||
+
|
||||
+ AssertNaturalType<S>();
|
||||
+ AssertNaturalType<A>();
|
||||
+ AssertNaturalType<B>();
|
||||
+
|
||||
+ // we should only be called by IncreaseSum(); it forces integer promotion
|
||||
+ static_assert(std::is_same<A, decltype(+a)>::value, "a will not be promoted");
|
||||
+ static_assert(std::is_same<B, decltype(+b)>::value, "b will not be promoted");
|
||||
+ // and without integer promotions, a sum of unsigned integers is unsigned
|
||||
+ static_assert(std::is_unsigned<decltype(a+b)>::value, "a+b is unsigned");
|
||||
+
|
||||
+ // with integer promotions ruled out, a or b can only undergo integer
|
||||
+ // conversion to the higher rank type (A or B, we do not know which)
|
||||
+ using AB = typename std::common_type<A, B>::type;
|
||||
+ static_assert(std::is_same<AB, A>::value || std::is_same<AB, B>::value, "no unexpected conversions");
|
||||
+ static_assert(std::is_same<AB, decltype(a+b)>::value, "lossless assignment");
|
||||
+ const AB sum = a + b;
|
||||
+
|
||||
+ static_assert(std::numeric_limits<AB>::is_modulo, "we can detect overflows");
|
||||
+ // 1. modulo math: overflowed sum is smaller than any of its operands
|
||||
+ // 2. the sum may overflow S (i.e. the return base type)
|
||||
+ // We do not need Less() here because we compare promoted unsigned types.
|
||||
+ return (sum >= a && sum <= std::numeric_limits<S>::max()) ?
|
||||
+ std::optional<S>(sum) : std::optional<S>();
|
||||
+}
|
||||
+
|
||||
+/// This IncreaseSumInternal() overload supports a larger variety of types.
|
||||
+/// \returns a non-overflowing sum of the two arguments (or nothing)
|
||||
+/// \returns nothing if at least one of the arguments is negative
|
||||
+/// \prec at least one of the argument types is signed
|
||||
+template <typename S, typename A, typename B, std::enable_if_t<!AllUnsigned<A,B>::value, int> = 0>
|
||||
+std::optional<S> constexpr
|
||||
+IncreaseSumInternal(const A a, const B b) {
|
||||
+ AssertNaturalType<S>();
|
||||
+ AssertNaturalType<A>();
|
||||
+ AssertNaturalType<B>();
|
||||
+
|
||||
+ // we should only be called by IncreaseSum() that does integer promotion
|
||||
+ static_assert(std::is_same<A, decltype(+a)>::value, "a will not be promoted");
|
||||
+ static_assert(std::is_same<B, decltype(+b)>::value, "b will not be promoted");
|
||||
+
|
||||
+ return
|
||||
+ // We could support a non-under/overflowing sum of negative numbers, but
|
||||
+ // our callers use negative values specially (e.g., for do-not-use or
|
||||
+ // do-not-limit settings) and are not supposed to do math with them.
|
||||
+ (a < 0 || b < 0) ? std::optional<S>() :
|
||||
+ // To avoid undefined behavior of signed overflow, we must not compute
|
||||
+ // the raw a+b sum if it may overflow. When A is not B, a or b undergoes
|
||||
+ // (safe for non-negatives) integer conversion in these expressions, so
|
||||
+ // we do not know the resulting a+b type AB and its maximum. We must
|
||||
+ // also detect subsequent casting-to-S overflows.
|
||||
+ // Overflow condition: (a + b > maxAB) or (a + b > maxS).
|
||||
+ // A is an integer promotion of S, so maxS <= maxA <= maxAB.
|
||||
+ // Since maxS <= maxAB, it is sufficient to just check: a + b > maxS,
|
||||
+ // which is the same as the overflow-safe condition here: maxS - a < b.
|
||||
+ // Finally, (maxS - a) cannot overflow because a is not negative and
|
||||
+ // cannot underflow because a is a promotion of s: 0 <= a <= maxS.
|
||||
+ Less(std::numeric_limits<S>::max() - a, b) ? std::optional<S>() :
|
||||
+ std::optional<S>(a + b);
|
||||
+}
|
||||
+
|
||||
+/// argument pack expansion termination for IncreaseSum<S, T, Args...>()
|
||||
+template <typename S, typename T>
|
||||
+std::optional<S>
|
||||
+IncreaseSum(const S s, const T t)
|
||||
+{
|
||||
+ // Force (always safe) integer promotions now, to give std::enable_if_t<>
|
||||
+ // promoted types instead of entering IncreaseSumInternal<AllUnsigned>(s,t)
|
||||
+ // but getting a _signed_ promoted value of s or t in s + t.
|
||||
+ return IncreaseSumInternal<S>(+s, +t);
|
||||
+}
|
||||
+
|
||||
+/// \returns a non-overflowing sum of the arguments (or nothing)
|
||||
+template <typename S, typename T, typename... Args>
|
||||
+std::optional<S>
|
||||
+IncreaseSum(const S sum, const T t, const Args... args) {
|
||||
+ if (const auto head = IncreaseSum(sum, t)) {
|
||||
+ return IncreaseSum(head.value(), args...);
|
||||
+ } else {
|
||||
+ // std::optional<S>() triggers bogus -Wmaybe-uninitialized warnings in GCC v10.3
|
||||
+ return std::nullopt;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// \returns an exact, non-overflowing sum of the arguments (or nothing)
|
||||
+template <typename SummationType, typename... Args>
|
||||
+std::optional<SummationType>
|
||||
+NaturalSum(const Args... args) {
|
||||
+ return IncreaseSum<SummationType>(0, args...);
|
||||
+}
|
||||
+
|
||||
+/// Safely resets the given variable to NaturalSum() of the given arguments.
|
||||
+/// If the sum overflows, resets to variable's maximum possible value.
|
||||
+/// \returns the new variable value (like an assignment operator would)
|
||||
+template <typename S, typename... Args>
|
||||
+S
|
||||
+SetToNaturalSumOrMax(S &var, const Args... args)
|
||||
+{
|
||||
+ var = NaturalSum<S>(args...).value_or(std::numeric_limits<S>::max());
|
||||
+ return var;
|
||||
+}
|
||||
+
|
||||
+/// converts a given non-negative integer into an integer of a given type
|
||||
+/// without loss of information or undefined behavior
|
||||
+template <typename Result, typename Source>
|
||||
+Result
|
||||
+NaturalCast(const Source s)
|
||||
+{
|
||||
+ return NaturalSum<Result>(s).value();
|
||||
+}
|
||||
+
|
||||
+#endif /* SQUID_SRC_SQUIDMATH_H */
|
||||
|
||||
--- a/src/http.cc
|
||||
+++ b/src/http.cc
|
||||
@@ -57,6 +57,7 @@
|
||||
#include "StrList.h"
|
||||
#include "tools.h"
|
||||
#include "util.h"
|
||||
+#include "SquidMath.h"
|
||||
|
||||
#if USE_AUTH
|
||||
#include "auth/UserRequest.h"
|
||||
@@ -1156,18 +1157,27 @@ HttpStateData::readReply(const CommIoCbP
|
||||
* Plus, it breaks our lame *HalfClosed() detection
|
||||
*/
|
||||
|
||||
- Must(maybeMakeSpaceAvailable(true));
|
||||
- CommIoCbParams rd(this); // will be expanded with ReadNow results
|
||||
- rd.conn = io.conn;
|
||||
- rd.size = entry->bytesWanted(Range<size_t>(0, inBuf.spaceSize()));
|
||||
+ const auto moreDataPermission = canBufferMoreReplyBytes();
|
||||
+ if (!moreDataPermission) {
|
||||
+ abortTransaction("ready to read required data, but the read buffer is full and cannot be drained");
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ const auto readSizeMax = maybeMakeSpaceAvailable(moreDataPermission.value());
|
||||
+ // TODO: Move this logic inside maybeMakeSpaceAvailable():
|
||||
+ const auto readSizeWanted = readSizeMax ? entry->bytesWanted(Range<size_t>(0, readSizeMax)) : 0;
|
||||
|
||||
- if (rd.size <= 0) {
|
||||
+ if (readSizeWanted <= 0) {
|
||||
assert(entry->mem_obj);
|
||||
AsyncCall::Pointer nilCall;
|
||||
entry->mem_obj->delayRead(DeferredRead(readDelayed, this, CommRead(io.conn, NULL, 0, nilCall)));
|
||||
return;
|
||||
}
|
||||
|
||||
+
|
||||
+ CommIoCbParams rd(this); // will be expanded with ReadNow results
|
||||
+ rd.conn = io.conn;
|
||||
+ rd.size = readSizeWanted;
|
||||
switch (Comm::ReadNow(rd, inBuf)) {
|
||||
case Comm::INPROGRESS:
|
||||
if (inBuf.isEmpty())
|
||||
@@ -1522,8 +1532,10 @@ HttpStateData::maybeReadVirginBody()
|
||||
if (!Comm::IsConnOpen(serverConnection) || fd_table[serverConnection->fd].closing())
|
||||
return;
|
||||
|
||||
- if (!maybeMakeSpaceAvailable(false))
|
||||
+ if (!canBufferMoreReplyBytes()) {
|
||||
+ abortTransaction("more response bytes required, but the read buffer is full and cannot be drained");
|
||||
return;
|
||||
+ }
|
||||
|
||||
// XXX: get rid of the do_next_read flag
|
||||
// check for the proper reasons preventing read(2)
|
||||
@@ -1541,40 +1553,78 @@ HttpStateData::maybeReadVirginBody()
|
||||
Comm::Read(serverConnection, call);
|
||||
}
|
||||
|
||||
-bool
|
||||
-HttpStateData::maybeMakeSpaceAvailable(bool doGrow)
|
||||
+/// Desired inBuf capacity based on various capacity preferences/limits:
|
||||
+/// * a smaller buffer may not hold enough for look-ahead header/body parsers;
|
||||
+/// * a smaller buffer may result in inefficient tiny network reads;
|
||||
+/// * a bigger buffer may waste memory;
|
||||
+/// * a bigger buffer may exceed SBuf storage capabilities (SBuf::maxSize);
|
||||
+size_t
|
||||
+HttpStateData::calcReadBufferCapacityLimit() const
|
||||
+{
|
||||
+ if (!flags.headers_parsed)
|
||||
+ return Config.maxReplyHeaderSize;
|
||||
+
|
||||
+ // XXX: Our inBuf is not used to maintain the read-ahead gap, and using
|
||||
+ // Config.readAheadGap like this creates huge read buffers for large
|
||||
+ // read_ahead_gap values. TODO: Switch to using tcp_recv_bufsize as the
|
||||
+ // primary read buffer capacity factor.
|
||||
+ //
|
||||
+ // TODO: Cannot reuse throwing NaturalCast() here. Consider removing
|
||||
+ // .value() dereference in NaturalCast() or add/use NaturalCastOrMax().
|
||||
+ const auto configurationPreferences = NaturalSum<size_t>(Config.readAheadGap).value_or(SBuf::maxSize);
|
||||
+
|
||||
+ // TODO: Honor TeChunkedParser look-ahead and trailer parsing requirements
|
||||
+ // (when explicit configurationPreferences are set too low).
|
||||
+
|
||||
+ return std::min<size_t>(configurationPreferences, SBuf::maxSize);
|
||||
+}
|
||||
+
|
||||
+/// The maximum number of virgin reply bytes we may buffer before we violate
|
||||
+/// the currently configured response buffering limits.
|
||||
+/// \retval std::nullopt means that no more virgin response bytes can be read
|
||||
+/// \retval 0 means that more virgin response bytes may be read later
|
||||
+/// \retval >0 is the number of bytes that can be read now (subject to other constraints)
|
||||
+std::optional<size_t>
|
||||
+HttpStateData::canBufferMoreReplyBytes() const
|
||||
{
|
||||
- // how much we are allowed to buffer
|
||||
- const int limitBuffer = (flags.headers_parsed ? Config.readAheadGap : Config.maxReplyHeaderSize);
|
||||
-
|
||||
- if (limitBuffer < 0 || inBuf.length() >= (SBuf::size_type)limitBuffer) {
|
||||
- // when buffer is at or over limit already
|
||||
- debugs(11, 7, "will not read up to " << limitBuffer << ". buffer has (" << inBuf.length() << "/" << inBuf.spaceSize() << ") from " << serverConnection);
|
||||
- debugs(11, DBG_DATA, "buffer has {" << inBuf << "}");
|
||||
- // Process next response from buffer
|
||||
- processReply();
|
||||
- return false;
|
||||
+#if USE_ADAPTATION
|
||||
+ // If we do not check this now, we may say the final "no" prematurely below
|
||||
+ // because inBuf.length() will decrease as adaptation drains buffered bytes.
|
||||
+ if (responseBodyBuffer) {
|
||||
+ debugs(11, 3, "yes, but waiting for adaptation to drain read buffer");
|
||||
+ return 0; // yes, we may be able to buffer more (but later)
|
||||
}
|
||||
+#endif
|
||||
|
||||
+ const auto maxCapacity = calcReadBufferCapacityLimit();
|
||||
+ if (inBuf.length() >= maxCapacity) {
|
||||
+ debugs(11, 3, "no, due to a full buffer: " << inBuf.length() << '/' << inBuf.spaceSize() << "; limit: " << maxCapacity);
|
||||
+ return std::nullopt; // no, configuration prohibits buffering more
|
||||
+ }
|
||||
+
|
||||
+ const auto maxReadSize = maxCapacity - inBuf.length(); // positive
|
||||
+ debugs(11, 7, "yes, may read up to " << maxReadSize << " into " << inBuf.length() << '/' << inBuf.spaceSize());
|
||||
+ return maxReadSize; // yes, can read up to this many bytes (subject to other constraints)
|
||||
+}
|
||||
+
|
||||
+/// prepare read buffer for reading
|
||||
+/// \return the maximum number of bytes the caller should attempt to read
|
||||
+/// \retval 0 means that the caller should delay reading
|
||||
+size_t
|
||||
+HttpStateData::maybeMakeSpaceAvailable(const size_t maxReadSize)
|
||||
+{
|
||||
// how much we want to read
|
||||
- const size_t read_size = calcBufferSpaceToReserve(inBuf.spaceSize(), (limitBuffer - inBuf.length()));
|
||||
+ const size_t read_size = calcBufferSpaceToReserve(inBuf.spaceSize(), maxReadSize);
|
||||
|
||||
- if (!read_size) {
|
||||
+ if (read_size < 2) {
|
||||
debugs(11, 7, "will not read up to " << read_size << " into buffer (" << inBuf.length() << "/" << inBuf.spaceSize() << ") from " << serverConnection);
|
||||
- return false;
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
- // just report whether we could grow or not, do not actually do it
|
||||
- if (doGrow)
|
||||
- return (read_size >= 2);
|
||||
-
|
||||
// we may need to grow the buffer
|
||||
inBuf.reserveSpace(read_size);
|
||||
- debugs(11, 8, (!flags.do_next_read ? "will not" : "may") <<
|
||||
- " read up to " << read_size << " bytes info buf(" << inBuf.length() << "/" << inBuf.spaceSize() <<
|
||||
- ") from " << serverConnection);
|
||||
-
|
||||
- return (inBuf.spaceSize() >= 2); // only read if there is 1+ bytes of space available
|
||||
+ debugs(11, 7, "may read up to " << read_size << " bytes info buffer (" << inBuf.length() << "/" << inBuf.spaceSize() << ") from " << serverConnection);
|
||||
+ return read_size;
|
||||
}
|
||||
|
||||
/// called after writing the very last request byte (body, last-chunk, etc)
|
||||
--- a/src/http.h
|
||||
+++ b/src/http.h
|
||||
@@ -15,6 +15,8 @@
|
||||
#include "http/StateFlags.h"
|
||||
#include "sbuf/SBuf.h"
|
||||
|
||||
+#include <optional>
|
||||
+
|
||||
class FwdState;
|
||||
class HttpHeader;
|
||||
|
||||
@@ -107,16 +109,9 @@ private:
|
||||
|
||||
void abortTransaction(const char *reason) { abortAll(reason); } // abnormal termination
|
||||
|
||||
- /**
|
||||
- * determine if read buffer can have space made available
|
||||
- * for a read.
|
||||
- *
|
||||
- * \param grow whether to actually expand the buffer
|
||||
- *
|
||||
- * \return whether the buffer can be grown to provide space
|
||||
- * regardless of whether the grow actually happened.
|
||||
- */
|
||||
- bool maybeMakeSpaceAvailable(bool grow);
|
||||
+ size_t calcReadBufferCapacityLimit() const;
|
||||
+ std::optional<size_t> canBufferMoreReplyBytes() const;
|
||||
+ size_t maybeMakeSpaceAvailable(size_t maxReadSize);
|
||||
|
||||
// consuming request body
|
||||
virtual void handleMoreRequestBodyAvailable();
|
||||
138
backport-CVE-2024-25617.patch
Normal file
138
backport-CVE-2024-25617.patch
Normal file
@ -0,0 +1,138 @@
|
||||
From 72a3bbd5e431597c3fdb56d752bc56b010ba3817 Mon Sep 17 00:00:00 2001
|
||||
From: Alex Rousskov <rousskov@measurement-factory.com>
|
||||
Date: Wed, 25 Oct 2023 11:47:19 +0000
|
||||
Subject: [PATCH] Improve handling of expanding HTTP header values (#1536)
|
||||
|
||||
Squid manipulations often increase HTTP header value length compared to
|
||||
the corresponding raw value received by Squid. Raw header length is
|
||||
checked against request_header_max_size and reply_header_max_size that
|
||||
default to 64KB, making the raw value safe to store in a String object
|
||||
(by default). However, when the increased length of a manipulated value
|
||||
exceeds String class limits, Squid leaks memory, asserts, or possibly
|
||||
stalls affected transactions. The long-term fix for this problem is a
|
||||
complete String elimination from Squid sources, but that takes time.
|
||||
|
||||
Known manipulations may effectively concatenate headers and/or increase
|
||||
header value length by 50%. This workaround makes such known increases
|
||||
safe by essentially tripling String class limits:
|
||||
|
||||
(64KB + 64KB) * 150% = 3 * 64KB
|
||||
|
||||
This bug was discovered and detailed by Joshua Rogers at
|
||||
https://megamansec.github.io/Squid-Security-Audit/response-memleaks.html
|
||||
where it was filed as "Memory Leak in HTTP Response Parsing".
|
||||
|
||||
Conflict: src/SquidString.h context adapt
|
||||
Reference: https://github.com/squid-cache/squid/commit/72a3bbd5e431597c3fdb56d752bc56b010ba3817
|
||||
---
|
||||
src/SquidString.h | 11 ++++++++++-
|
||||
src/cache_cf.cc | 12 ++++++++++++
|
||||
src/cf.data.pre | 26 ++++++++++++++++----------
|
||||
src/http.cc | 5 +++--
|
||||
4 files changed, 41 insertions(+), 13 deletions(-)
|
||||
|
||||
diff --git a/src/SquidString.h b/src/SquidString.h
|
||||
index 21fdde598cb..ffb215fa5e7 100644
|
||||
--- a/src/SquidString.h
|
||||
+++ b/src/SquidString.h
|
||||
@@ -140,7 +140,16 @@ class String
|
||||
|
||||
size_type len_; /* current length */
|
||||
|
||||
- static const size_type SizeMax_ = 65535; ///< 64K limit protects some fixed-size buffers
|
||||
+ /// An earlier 64KB limit was meant to protect some fixed-size buffers, but
|
||||
+ /// (a) we do not know where those buffers are (or whether they still exist)
|
||||
+ /// (b) too many String users unknowingly exceeded that limit and asserted.
|
||||
+ /// We are now using a larger limit to reduce the number of (b) cases,
|
||||
+ /// especially cases where "compact" lists of items grow 50% in size when we
|
||||
+ /// convert them to canonical form. The new limit is selected to withstand
|
||||
+ /// concatenation and ~50% expansion of two HTTP headers limited by default
|
||||
+ /// request_header_max_size and reply_header_max_size settings.
|
||||
+ static const size_type SizeMax_ = 3*64*1024 - 1;
|
||||
+
|
||||
/// returns true after increasing the first argument by extra if the sum does not exceed SizeMax_
|
||||
static bool SafeAdd(size_type &base, size_type extra) { if (extra <= SizeMax_ && base <= SizeMax_ - extra) { base += extra; return true; } return false; }
|
||||
|
||||
diff --git a/src/cache_cf.cc b/src/cache_cf.cc
|
||||
index 6a0d253b139..e4a296005fd 100644
|
||||
--- a/src/cache_cf.cc
|
||||
+++ b/src/cache_cf.cc
|
||||
@@ -1007,6 +1007,18 @@ configDoConfigure(void)
|
||||
(uint32_t)Config.maxRequestBufferSize, (uint32_t)Config.maxRequestHeaderSize);
|
||||
}
|
||||
|
||||
+ // Warn about the dangers of exceeding String limits when manipulating HTTP
|
||||
+ // headers. Technically, we do not concatenate _requests_, so we could relax
|
||||
+ // their check, but we keep the two checks the same for simplicity sake.
|
||||
+ const auto safeRawHeaderValueSizeMax = (String::SizeMaxXXX()+1)/3;
|
||||
+ // TODO: static_assert(safeRawHeaderValueSizeMax >= 64*1024); // no WARNINGs for default settings
|
||||
+ if (Config.maxRequestHeaderSize > safeRawHeaderValueSizeMax)
|
||||
+ debugs(3, DBG_CRITICAL, "WARNING: Increasing request_header_max_size beyond " << safeRawHeaderValueSizeMax <<
|
||||
+ " bytes makes Squid more vulnerable to denial-of-service attacks; configured value: " << Config.maxRequestHeaderSize << " bytes");
|
||||
+ if (Config.maxReplyHeaderSize > safeRawHeaderValueSizeMax)
|
||||
+ debugs(3, DBG_CRITICAL, "WARNING: Increasing reply_header_max_size beyond " << safeRawHeaderValueSizeMax <<
|
||||
+ " bytes makes Squid more vulnerable to denial-of-service attacks; configured value: " << Config.maxReplyHeaderSize << " bytes");
|
||||
+
|
||||
/*
|
||||
* Disable client side request pipelining if client_persistent_connections OFF.
|
||||
* Waste of resources queueing any pipelined requests when the first will close the connection.
|
||||
diff --git a/src/cf.data.pre b/src/cf.data.pre
|
||||
index 15e09c42378..986e31499a6 100644
|
||||
--- a/src/cf.data.pre
|
||||
+++ b/src/cf.data.pre
|
||||
@@ -6753,11 +6753,14 @@ TYPE: b_size_t
|
||||
DEFAULT: 64 KB
|
||||
LOC: Config.maxRequestHeaderSize
|
||||
DOC_START
|
||||
- This specifies the maximum size for HTTP headers in a request.
|
||||
- Request headers are usually relatively small (about 512 bytes).
|
||||
- Placing a limit on the request header size will catch certain
|
||||
- bugs (for example with persistent connections) and possibly
|
||||
- buffer-overflow or denial-of-service attacks.
|
||||
+ This directives limits the header size of a received HTTP request
|
||||
+ (including request-line). Increasing this limit beyond its 64 KB default
|
||||
+ exposes certain old Squid code to various denial-of-service attacks. This
|
||||
+ limit also applies to received FTP commands.
|
||||
+
|
||||
+ This limit has no direct affect on Squid memory consumption.
|
||||
+
|
||||
+ Squid does not check this limit when sending requests.
|
||||
DOC_END
|
||||
|
||||
NAME: reply_header_max_size
|
||||
@@ -6766,11 +6769,14 @@ TYPE: b_size_t
|
||||
DEFAULT: 64 KB
|
||||
LOC: Config.maxReplyHeaderSize
|
||||
DOC_START
|
||||
- This specifies the maximum size for HTTP headers in a reply.
|
||||
- Reply headers are usually relatively small (about 512 bytes).
|
||||
- Placing a limit on the reply header size will catch certain
|
||||
- bugs (for example with persistent connections) and possibly
|
||||
- buffer-overflow or denial-of-service attacks.
|
||||
+ This directives limits the header size of a received HTTP response
|
||||
+ (including status-line). Increasing this limit beyond its 64 KB default
|
||||
+ exposes certain old Squid code to various denial-of-service attacks. This
|
||||
+ limit also applies to FTP command responses.
|
||||
+
|
||||
+ Squid also checks this limit when loading hit responses from disk cache.
|
||||
+
|
||||
+ Squid does not check this limit when sending responses.
|
||||
DOC_END
|
||||
|
||||
NAME: request_body_max_size
|
||||
diff --git a/src/http.cc b/src/http.cc
|
||||
index f4e96aacd52..138c845c7b0 100644
|
||||
--- a/src/http.cc
|
||||
+++ b/src/http.cc
|
||||
@@ -1900,8 +1900,9 @@ HttpStateData::httpBuildRequestHeader(HttpRequest * request,
|
||||
|
||||
String strFwd = hdr_in->getList(Http::HdrType::X_FORWARDED_FOR);
|
||||
|
||||
- // if we cannot double strFwd size, then it grew past 50% of the limit
|
||||
- if (!strFwd.canGrowBy(strFwd.size())) {
|
||||
+ // Detect unreasonably long header values. And paranoidly check String
|
||||
+ // limits: a String ought to accommodate two reasonable-length values.
|
||||
+ if (strFwd.size() > 32*1024 || !strFwd.canGrowBy(strFwd.size())) {
|
||||
// There is probably a forwarding loop with Via detection disabled.
|
||||
// If we do nothing, String will assert on overflow soon.
|
||||
// TODO: Terminate all transactions with huge XFF?
|
||||
32
backport-CVE-2024-37894.patch
Normal file
32
backport-CVE-2024-37894.patch
Normal file
@ -0,0 +1,32 @@
|
||||
From 920563e7a080155fae3ced73d6198781e8b0ff04 Mon Sep 17 00:00:00 2001
|
||||
From: Francesco Chemolli <5175948+kinkie@users.noreply.github.com>
|
||||
Date: Sun, 2 Jun 2024 14:41:16 +0000
|
||||
Subject: [PATCH] Bug 5378: type mismatch in libTrie (#1830)
|
||||
|
||||
TrieNode::add() incorrectly computed an offset of an internal data
|
||||
structure, resulting in out-of-bounds memory accesses that could cause
|
||||
corruption or crashes.
|
||||
|
||||
This bug was discovered and detailed by Joshua Rogers at
|
||||
https://megamansec.github.io/Squid-Security-Audit/esi-underflow.html
|
||||
where it was filed as "Buffer Underflow in ESI".
|
||||
|
||||
Conflict: NA
|
||||
Reference: https://github.com/squid-cache/squid/commit/920563e7a080155fae3ced73d6198781e8b0ff04
|
||||
---
|
||||
lib/libTrie/TrieNode.cc | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/lib/libTrie/TrieNode.cc b/lib/libTrie/TrieNode.cc
|
||||
index 0f991a06d3e..d417e0f5448 100644
|
||||
--- a/lib/libTrie/TrieNode.cc
|
||||
+++ b/lib/libTrie/TrieNode.cc
|
||||
@@ -32,7 +32,7 @@ TrieNode::add(char const *aString, size_t theLength, void *privatedata, TrieChar
|
||||
/* We trust that privatedata and existant keys have already been checked */
|
||||
|
||||
if (theLength) {
|
||||
- int index = transform ? (*transform)(*aString): *aString;
|
||||
+ const unsigned char index = transform ? (*transform)(*aString): *aString;
|
||||
|
||||
if (!internal[index])
|
||||
internal[index] = new TrieNode;
|
||||
40
squid.spec
40
squid.spec
@ -2,7 +2,7 @@
|
||||
|
||||
Name: squid
|
||||
Version: 4.9
|
||||
Release: 18
|
||||
Release: 23
|
||||
Summary: The Squid proxy caching server
|
||||
Epoch: 7
|
||||
License: GPLv2+ and (LGPLv2+ and MIT and BSD and Public Domain)
|
||||
@ -52,6 +52,10 @@ Patch31:backport-CVE-2023-46728.patch
|
||||
Patch32:backport-CVE-2023-49285.patch
|
||||
Patch33:backport-CVE-2023-49286.patch
|
||||
Patch34:backport-CVE-2023-50269.patch
|
||||
Patch35:backport-CVE-2024-23638.patch
|
||||
Patch36:backport-CVE-2024-25617.patch
|
||||
Patch37:backport-CVE-2024-37894.patch
|
||||
Patch38:backport-CVE-2024-25111.patch
|
||||
|
||||
Buildroot: %{_tmppath}/squid-4.9-1-root-%(%{__id_u} -n)
|
||||
Requires: bash >= 2.0
|
||||
@ -75,7 +79,7 @@ non-blocking, I/O-driven process and keeps meta data and implements negative cac
|
||||
%build
|
||||
autoreconf
|
||||
automake
|
||||
CXXFLAGS="$RPM_OPT_FLAGS -fPIC"
|
||||
CXXFLAGS="$RPM_OPT_FLAGS -fPIC -std=c++17"
|
||||
CFLAGS="$RPM_OPT_FLAGS -fPIC"
|
||||
LDFLAGS="$RPM_LD_FLAGS -pie -Wl,-z,relro -Wl,-z,now -Wl,--warn-shared-textrel"
|
||||
|
||||
@ -99,7 +103,7 @@ LDFLAGS="$RPM_LD_FLAGS -pie -Wl,-z,relro -Wl,-z,now -Wl,--warn-shared-textrel"
|
||||
--enable-linux-netfilter --enable-removal-policies="heap,lru" \
|
||||
--enable-snmp --enable-ssl --enable-ssl-crtd \
|
||||
--enable-storeio="aufs,diskd,ufs,rock" --enable-diskio --enable-wccpv2 \
|
||||
--enable-esi --enable-ecap --with-aio --with-default-user="squid" \
|
||||
--disable-esi --enable-ecap --with-aio --with-default-user="squid" \
|
||||
--with-dl --with-openssl --with-pthreads --disable-arch-native \
|
||||
--with-pic --disable-security-cert-validators \
|
||||
--with-tdb
|
||||
@ -237,6 +241,36 @@ fi
|
||||
chgrp squid /var/cache/samba/winbindd_privileged >/dev/null 2>&1 || :
|
||||
|
||||
%changelog
|
||||
* Tue Oct 29 2024 xinghe <xinghe2@h-partners.com> - 7:4.9-23
|
||||
- Type:cves
|
||||
- ID:CVE-2024-45802
|
||||
- SUG:NA
|
||||
- DESC:fix CVE-2024-45802
|
||||
|
||||
* Thu Sep 19 2024 xinghe <xinghe2@h-partners.com> - 7:4.9-22
|
||||
- Type:cves
|
||||
- ID:CVE-2024-25111
|
||||
- SUG:NA
|
||||
- DESC:fix CVE-2024-25111
|
||||
|
||||
* Wed Jun 26 2024 xinghe <xinghe2@h-partners.com> - 7:4.9-21
|
||||
- Type:cves
|
||||
- ID:CVE-2024-37894
|
||||
- SUG:NA
|
||||
- DESC:fix CVE-2024-37894
|
||||
|
||||
* Tue Feb 20 2024 xinghe <xinghe2@h-partners.com> - 7:4.9-20
|
||||
- Type:cves
|
||||
- ID:CVE-2024-25617
|
||||
- SUG:NA
|
||||
- DESC:fix CVE-2024-25617
|
||||
|
||||
* Thu Jan 25 2024 xinghe <xinghe2@h-partners.com> - 7:4.9-19
|
||||
- Type:cves
|
||||
- ID:CVE-2024-23638
|
||||
- SUG:NA
|
||||
- DESC:fix CVE-2024-23638
|
||||
|
||||
* Fri Dec 15 2023 xinghe <xinghe2@h-partners.com> - 7:4.9-18
|
||||
- Type:cves
|
||||
- ID:CVE-2023-50269
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user