From d47f1d774d71cba2236e23697684cb8f205376f1 Mon Sep 17 00:00:00 2001 From: AlmaLinux RelEng Bot Date: Tue, 19 May 2026 19:06:21 -0400 Subject: [PATCH] import UBI squid-6.10-12.el10_2 --- squid-6.10-dont-stuck-respmod.patch | 342 ++++++++++++++++++++++++++ squid-6.10-provider-keys-digest.patch | 59 +++++ squid.spec | 29 ++- 3 files changed, 421 insertions(+), 9 deletions(-) create mode 100644 squid-6.10-dont-stuck-respmod.patch create mode 100644 squid-6.10-provider-keys-digest.patch diff --git a/squid-6.10-dont-stuck-respmod.patch b/squid-6.10-dont-stuck-respmod.patch new file mode 100644 index 0000000..4cfb03a --- /dev/null +++ b/squid-6.10-dont-stuck-respmod.patch @@ -0,0 +1,342 @@ +From e0b1163f19ad7f63e92e6ea9ee94d51d5eebeacf Mon Sep 17 00:00:00 2001 +From: Alex Rousskov +Date: Thu, 27 Nov 2025 10:51:27 +0100 +Subject: [PATCH] Bug 5352: Do not get stuck when RESPMOD is slower than + read(2) (#1777) + + ... RESPMOD BodyPipe buffer becomes full ... + maybeMakeSpaceAvailable: will not read up to 0 + The AsyncCall Client::noteDelayAwareReadChance constructed + + ... RESPMOD consumes some buffered virgin body data ... + entering BodyProducer::noteMoreBodySpaceAvailable + leaving BodyProducer::noteMoreBodySpaceAvailable + + ... read_timeout seconds later ... + http.cc(148) httpTimeout + FwdState.cc(471) fail: ERR_READ_TIMEOUT "Gateway Timeout" + +When RESPMOD does not empty its adaptation BodyPipe buffer fast enough, +readReply() may eventually fill that buffer and call delayRead(), +anticipating a noteDelayAwareReadChance() callback from Store or Server +delay pools. That callback never happens if Store and Server are not +getting any data -- they do not even start working until RESPMOD service +starts releasing adapted/echoed response back to Squid! Meanwhile, our +flags.do_next_read (cleared by readReply() caller) remains false. + +When/if RESPMOD service eventually frees some BodyPipe buffer space, +triggering noteMoreBodySpaceAvailable() notification, nothing changes +because maybeReadVirginBody() quits when flags.do_next_read is false. + +noteMoreBodySpaceAvailable() could not just make flags.do_next_read true +because that flag may be false for a variety of other/permanent reasons. +Instead, we replaced that one-size-fits-all flag with more specific +checks so that reading can resume if it is safe to resume it. This +change addresses a couple of flag-related XXXs. + +The bug was introduced in 2023 commit 50c5af88. Prior that that change, +delayRead() was not called when RESPMOD BodyPipe buffer became full +because maybeMakeSpaceAvailable() returned false in that case, blocking +maybeReadVirginBody() from triggering readReply() via Comm::Read(). We +missed flags.do_next_read dependency and that Store-specific delayRead() +cannot be used to wait for adaptation buffer space to become available. + +XXX: To reduce risks, this change duplicates a part of +calcBufferSpaceToReserve() logic. Removing that duplication requires +significant (and risky) refactoring of several related methods. +--- + src/adaptation/icap/ModXact.cc | 8 +--- + src/clients/Client.cc | 3 ++ + src/clients/Client.h | 3 ++ + src/clients/FtpClient.cc | 2 + + src/http.cc | 70 ++++++++++++++++++++++------------ + src/http.h | 3 ++ + src/http/StateFlags.h | 1 - + 7 files changed, 59 insertions(+), 31 deletions(-) + +diff --git a/src/adaptation/icap/ModXact.cc b/src/adaptation/icap/ModXact.cc +index d2f9529..9331215 100644 +--- a/src/adaptation/icap/ModXact.cc ++++ b/src/adaptation/icap/ModXact.cc +@@ -435,14 +435,10 @@ void Adaptation::Icap::ModXact::virginConsume() + BodyPipe &bp = *virgin.body_pipe; + const bool wantToPostpone = isRepeatable || canStartBypass || protectGroupBypass; + +- // Why > 2? HttpState does not use the last bytes in the buffer +- // because Client::delayRead() is arguably broken. See +- // HttpStateData::maybeReadVirginBody for more details. +- if (wantToPostpone && bp.buf().spaceSize() > 2) { ++ if (wantToPostpone && bp.buf().potentialSpaceSize() > 0) { + // Postponing may increase memory footprint and slow the HTTP side + // down. Not postponing may increase the number of ICAP errors +- // if the ICAP service fails. We may also use "potential" space to +- // postpone more aggressively. Should the trade-off be configurable? ++ // if the ICAP service fails. Should the trade-off be configurable? + debugs(93, 8, "postponing consumption from " << bp.status()); + return; + } +diff --git a/src/clients/Client.cc b/src/clients/Client.cc +index cbf5693..7de6274 100644 +--- a/src/clients/Client.cc ++++ b/src/clients/Client.cc +@@ -1028,6 +1028,9 @@ Client::adjustBodyBytesRead(const int64_t delta) + void + Client::delayRead() + { ++ Assure(!waitingForDelayAwareReadChance); ++ waitingForDelayAwareReadChance = true; ++ + using DeferredReadDialer = NullaryMemFunT; + AsyncCall::Pointer call = asyncCall(11, 5, "Client::noteDelayAwareReadChance", + DeferredReadDialer(this, &Client::noteDelayAwareReadChance)); +diff --git a/src/clients/Client.h b/src/clients/Client.h +index f60d8ce..6a53315 100644 +--- a/src/clients/Client.h ++++ b/src/clients/Client.h +@@ -194,6 +194,9 @@ protected: + #endif + bool receivedWholeRequestBody = false; ///< handleRequestBodyProductionEnded called + ++ /// whether we are waiting for MemObject::delayRead() to call us back ++ bool waitingForDelayAwareReadChance = false; ++ + /// whether we should not be talking to FwdState; XXX: clear fwd instead + /// points to a string literal which is used only for debugging + const char *doneWithFwd = nullptr; +diff --git a/src/clients/FtpClient.cc b/src/clients/FtpClient.cc +index 3630766..6f9d8fa 100644 +--- a/src/clients/FtpClient.cc ++++ b/src/clients/FtpClient.cc +@@ -907,6 +907,8 @@ Ftp::Client::dataConnection() const + void + Ftp::Client::noteDelayAwareReadChance() + { ++ // TODO: Merge with HttpStateData::noteDelayAwareReadChance() ++ waitingForDelayAwareReadChance = false; + data.read_pending = false; + maybeReadVirginBody(); + } +diff --git a/src/http.cc b/src/http.cc +index 4916c15..e3f9b4c 100644 +--- a/src/http.cc ++++ b/src/http.cc +@@ -1168,17 +1168,15 @@ HttpStateData::persistentConnStatus() const + void + HttpStateData::noteDelayAwareReadChance() + { +- flags.do_next_read = true; ++ waitingForDelayAwareReadChance = false; + maybeReadVirginBody(); + } + + void + HttpStateData::readReply(const CommIoCbParams &io) + { +- Must(!flags.do_next_read); // XXX: should have been set false by mayReadVirginBody() +- flags.do_next_read = false; +- + debugs(11, 5, io.conn); ++ waitingForCommRead = false; + + // Bail out early on Comm::ERR_CLOSING - close handlers will tidy up for us + if (io.flag == Comm::ERR_CLOSING) { +@@ -1212,7 +1210,27 @@ HttpStateData::readReply(const CommIoCbParams &io) + const auto readSizeWanted = readSizeMax ? entry->bytesWanted(Range(0, readSizeMax)) : 0; + + if (readSizeWanted <= 0) { +- delayRead(); ++ // XXX: If we avoid Comm::ReadNow(), we should not Comm::Read() again ++ // when the wait is over. We should go straight to readReply() instead. ++ ++#if USE_ADAPTATION ++ // XXX: We are duplicating Client::calcBufferSpaceToReserve() logic. ++ // XXX: Some other delayRead() cases may lack kickReads() guarantees. ++ // TODO: Refactor maybeMakeSpaceAvailable() to properly treat each ++ // no-read case instead of calling delayRead() for the remaining cases. ++ ++ if (responseBodyBuffer) { ++ debugs(11, 5, "avoid delayRead() to give adaptation a chance to drain overflow buffer: " << responseBodyBuffer->contentSize()); ++ return; // wait for Client::noteMoreBodySpaceAvailable() ++ } ++ ++ if (virginBodyDestination && !virginBodyDestination->buf().hasPotentialSpace()) { ++ debugs(11, 5, "avoid delayRead() to give adaptation a chance to drain body pipe buffer: " << virginBodyDestination->buf().contentSize()); ++ return; // wait for Client::noteMoreBodySpaceAvailable() ++ } ++#endif ++ ++ delayRead(); /// wait for Client::noteDelayAwareReadChance() + return; + } + +@@ -1223,7 +1241,6 @@ HttpStateData::readReply(const CommIoCbParams &io) + case Comm::INPROGRESS: + if (inBuf.isEmpty()) + debugs(33, 2, io.conn << ": no data to process, " << xstrerr(rd.xerrno)); +- flags.do_next_read = true; + maybeReadVirginBody(); + return; + +@@ -1253,7 +1270,6 @@ HttpStateData::readReply(const CommIoCbParams &io) + + case Comm::ENDFILE: // close detected by 0-byte read + eof = 1; +- flags.do_next_read = false; + + /* Continue to process previously read data */ + break; +@@ -1264,7 +1280,6 @@ HttpStateData::readReply(const CommIoCbParams &io) + const auto err = new ErrorState(ERR_READ_ERROR, Http::scBadGateway, fwd->request, fwd->al); + err->xerrno = rd.xerrno; + fwd->fail(err); +- flags.do_next_read = false; + closeServer(); + mustStop("HttpStateData::readReply"); + return; +@@ -1318,7 +1333,6 @@ HttpStateData::continueAfterParsingHeader() + + if (!flags.headers_parsed && !eof) { + debugs(11, 9, "needs more at " << inBuf.length()); +- flags.do_next_read = true; + /** \retval false If we have not finished parsing the headers and may get more data. + * Schedules more reads to retrieve the missing data. + */ +@@ -1369,7 +1383,6 @@ HttpStateData::continueAfterParsingHeader() + assert(error != ERR_NONE); + entry->reset(); + fwd->fail(new ErrorState(error, Http::scBadGateway, fwd->request, fwd->al)); +- flags.do_next_read = false; + closeServer(); + mustStop("HttpStateData::continueAfterParsingHeader"); + return false; // quit on error +@@ -1455,7 +1468,6 @@ HttpStateData::decodeAndWriteReplyBody() + addVirginReplyBody(decodedData.content(), decodedData.contentSize()); + if (doneParsing) { + lastChunk = 1; +- flags.do_next_read = false; + markParsedVirginReplyAsWhole("http parsed last-chunk"); + } else if (eof) { + markPrematureReplyBodyEofFailure(); +@@ -1479,7 +1491,6 @@ void + HttpStateData::processReplyBody() + { + if (!flags.headers_parsed) { +- flags.do_next_read = true; + maybeReadVirginBody(); + return; + } +@@ -1499,7 +1510,6 @@ HttpStateData::processReplyBody() + if (entry->isAccepting()) { + if (flags.chunked) { + if (!decodeAndWriteReplyBody()) { +- flags.do_next_read = false; + serverComplete(); + return; + } +@@ -1525,8 +1535,6 @@ HttpStateData::processReplyBody() + } else { + commSetConnTimeout(serverConnection, Config.Timeout.read, nil); + } +- +- flags.do_next_read = true; + } + break; + +@@ -1536,7 +1544,6 @@ HttpStateData::processReplyBody() + // TODO: Remove serverConnectionSaved but preserve exception safety. + + commUnsetConnTimeout(serverConnection); +- flags.do_next_read = false; + + comm_remove_close_handler(serverConnection->fd, closeHandler); + closeHandler = nullptr; +@@ -1596,29 +1603,45 @@ HttpStateData::mayReadVirginReplyBody() const + void + HttpStateData::maybeReadVirginBody() + { +- // too late to read +- if (!Comm::IsConnOpen(serverConnection) || fd_table[serverConnection->fd].closing()) ++ if (!Comm::IsConnOpen(serverConnection) || fd_table[serverConnection->fd].closing()) { ++ debugs(11, 3, "no, peer connection gone"); + return; ++ } ++ ++ if (eof) { ++ // tolerate hypothetical calls between Comm::ENDFILE and closeServer() ++ debugs(11, 3, "no, saw EOF"); ++ return; ++ } ++ ++ if (lastChunk) { ++ // tolerate hypothetical calls between setting lastChunk and clearing serverConnection ++ debugs(11, 3, "no, saw last-chunk"); ++ return; ++ } + + 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) +- if (!flags.do_next_read) ++ if (waitingForDelayAwareReadChance) { ++ debugs(11, 5, "no, still waiting for noteDelayAwareReadChance()"); + return; ++ } + +- flags.do_next_read = false; ++ if (waitingForCommRead) { ++ debugs(11, 3, "no, already waiting for readReply()"); ++ return; ++ } + +- // must not already be waiting for read(2) ... + assert(!Comm::MonitorsRead(serverConnection->fd)); + + // wait for read(2) to be possible. + typedef CommCbMemFunT Dialer; + AsyncCall::Pointer call = JobCallback(11, 5, Dialer, this, HttpStateData::readReply); + Comm::Read(serverConnection, call); ++ waitingForCommRead = true; + } + + /// Desired inBuf capacity based on various capacity preferences/limits: +@@ -2406,7 +2429,6 @@ HttpStateData::sendRequest() + AsyncCall::Pointer timeoutCall = JobCallback(11, 5, + TimeoutDialer, this, HttpStateData::httpTimeout); + commSetConnTimeout(serverConnection, Config.Timeout.lifetime, timeoutCall); +- flags.do_next_read = true; + maybeReadVirginBody(); + + if (request->body_pipe != nullptr) { +diff --git a/src/http.h b/src/http.h +index cb4ae06..a15f103 100644 +--- a/src/http.h ++++ b/src/http.h +@@ -152,6 +152,9 @@ private: + /// positive when we read more than we wanted + int64_t payloadTruncated = 0; + ++ /// whether we are waiting for our Comm::Read() handler to be called ++ bool waitingForCommRead = false; ++ + /// Whether we received a Date header older than that of a matching + /// cached response. + bool sawDateGoBack = false; +diff --git a/src/http/StateFlags.h b/src/http/StateFlags.h +index ae3ca99..6d26b3e 100644 +--- a/src/http/StateFlags.h ++++ b/src/http/StateFlags.h +@@ -58,7 +58,6 @@ public: + bool keepalive_broken = false; + bool abuse_detected = false; + bool request_sent = false; +- bool do_next_read = false; + bool chunked = false; ///< reading a chunked response; TODO: rename + bool chunked_request = false; ///< writing a chunked request + bool sentLastChunk = false; ///< do not try to write last-chunk again +-- +2.44.0 + diff --git a/squid-6.10-provider-keys-digest.patch b/squid-6.10-provider-keys-digest.patch new file mode 100644 index 0000000..961a506 --- /dev/null +++ b/squid-6.10-provider-keys-digest.patch @@ -0,0 +1,59 @@ +diff --git a/src/ssl/gadgets.cc b/src/ssl/gadgets.cc +index 1f8ac9d..3f54e3d 100644 +--- a/src/ssl/gadgets.cc ++++ b/src/ssl/gadgets.cc +@@ -13,6 +13,42 @@ + #include "security/Io.h" + #include "ssl/gadgets.h" + ++/// whether to supply a digest algorithm name when calling X509_sign() with the given key ++static bool ++signWithDigest(const Security::PrivateKeyPointer &key) { ++ Assure(key); // TODO: Add and use Security::PrivateKey (here and in caller). ++ const auto pkey = key.get(); ++ ++ // OpenSSL does not define a maximum name size, but does terminate longer ++ // names without returning an error to the caller. Many similar callers in ++ // OpenSSL sources use 80-byte buffers. ++ char defaultDigestName[80] = ""; ++ const auto nameGetterResult = EVP_PKEY_get_default_digest_name(pkey, defaultDigestName, sizeof(defaultDigestName)); ++ debugs(83, 3, "nameGetterResult=" << nameGetterResult << " defaultDigestName=" << defaultDigestName); ++ if (nameGetterResult <= 0) { ++ debugs(83, 3, "ERROR: EVP_PKEY_get_default_digest_name() failure: " << Ssl::ReportAndForgetErrors); ++ // Backward compatibility: On error, assume digest should be used. ++ // TODO: Return false for -2 nameGetterResult as it "indicates the ++ // operation is not supported by the public key algorithm"? ++ return true; ++ } ++ ++ // The name "UNDEF" signifies that a digest must (for return value 2) or may ++ // (for return value 1) be left unspecified. ++ if (nameGetterResult == 2 && strcmp(defaultDigestName, "UNDEF") == 0) ++ return false; ++ ++ // Defined mandatory algorithms and "may be left unspecified" cases mentioned above. ++ return true; ++} ++ ++/// OpenSSL X509_sign() wrapper ++static auto ++Sign(Security::Certificate &cert, const Security::PrivateKeyPointer &key, const EVP_MD &availableDigest) { ++ const auto digestOrNil = signWithDigest(key) ? &availableDigest : nullptr; ++ return X509_sign(&cert, key.get(), digestOrNil); ++} ++ + void + Ssl::ForgetErrors() + { +@@ -618,9 +654,9 @@ static bool generateFakeSslCertificate(Security::CertPointer & certToStore, Secu + assert(hash); + /*Now sign the request */ + if (properties.signAlgorithm != Ssl::algSignSelf && properties.signWithPkey.get()) +- ret = X509_sign(cert.get(), properties.signWithPkey.get(), hash); ++ ret = Sign(*cert, properties.signWithPkey, *hash); + else //else sign with self key (self signed request) +- ret = X509_sign(cert.get(), pkey.get(), hash); ++ ret = Sign(*cert, pkey, *hash); + + if (!ret) + return false; diff --git a/squid.spec b/squid.spec index 99b7309..68710c2 100644 --- a/squid.spec +++ b/squid.spec @@ -2,7 +2,7 @@ Name: squid Version: 6.10 -Release: 6%{?dist}.3 +Release: 12%{?dist} Summary: The Squid proxy caching server Epoch: 7 # See CREDITS for breakdown of non GPLv2+ code @@ -45,6 +45,10 @@ Patch207: squid-6.10-large-upload-buffer-dies.patch # Upstream commit: https://github.com/squid-cache/squid/commit/2e7dea3cedd3ef2f071dee82867c4147f17376dd # https://issues.redhat.com/browse/RHEL-86817 Patch208: squid-6.10-cache-peer-connect-errors.patch +# https://issues.redhat.com/browse/RHEL-107994 +Patch209: squid-6.10-provider-keys-digest.patch +# https://issues.redhat.com/browse/RHEL-129457 +Patch210: squid-6.10-dont-stuck-respmod.patch # Security patches # https://bugzilla.redhat.com/show_bug.cgi?id=2404736 @@ -337,17 +341,24 @@ fi %changelog -* Mon Mar 30 2026 Tomas Korbar - 7:6.10-6.3 -- Resolves: RHEL-160667 - squid: Squid: Denial of Service via - crafted ICP traffic (CVE-2026-32748) +* Thu Mar 26 2026 Tomas Korbar - 7:6.10-12 +- Resolves: RHEL-160669 - squid: Squid: Denial of Service via + crafted ICP traffic -* Mon Mar 30 2026 Tomas Korbar - 7:6.10-6.2 -- Resolves: RHEL-160665 - squid: Squid: Denial of Service via +* Thu Mar 26 2026 Tomas Korbar - 7:6.10-11 +- Resolves: RHEL-160668 - squid: Squid: Denial of Service via heap Use-After-Free vulnerability in ICP handling (CVE-2026-33526) -* Mon Oct 20 2025 Luboš Uhliarik - 7:6.10-6.1 -- Resolves: RHEL-122480 - CVE-2025-62168 squid: Squid vulnerable to information - disclosure via authentication credential leakage in error handling +* Thu Nov 27 2025 Luboš Uhliarik - 7:6.10-10 +- Resolves: RHEL-129457 - "ICAP_ERR_OTHER/408" occurs in icap.log when + downloading a file on RHEL9 + +* Mon Nov 10 2025 Luboš Uhliarik - 7:6.10-9 +- Resolves: RHEL-122481 - squid: Squid vulnerable to information disclosure via + authentication credential leakage in error handling (CVE-2025-62168) + +* Fri Sep 12 2025 Luboš Uhliarik - 7:6.10-8 +- Resolves: RHEL-107994 - squid does not work with post-quantum crypto * Thu Apr 10 2025 Luboš Uhliarik - 7:6.10-6 - Resolves: RHEL-86817 - ”TCP connection to XX.XX.XX.XX/XXXX failed” message is