On 11/01/2017 04:50 μμ, Amos Jeffries wrote:
On 11/01/2017 10:55 p.m., Christos Tsantilas wrote:
We observed such problems on squid shutdown procedure and during regular
squid operation. Any clientStreams redesign should take care of such
problems.

The underlying problem has been known since r13480: If a
ClientHttpRequest job ends without Http::Stream (and ConnStateData)
knowledge, then Squid is likely to segfault or assert.

This patch does not resolve the underlying issue (a proper fix would
require architectural changes in a consensus-lacking area) but makes an
unexpected ClientHttpRequest job destruction less likely.

BodyPipe and Adaptation-related exceptions are the major causes of
unexpected ClientHttpRequest job destruction. This patch handles them by
closing the client connection. Connection closure should trigger an
orderly top-down cleanup, including Http::Stream, ConnStateData, and
ClientHttpRequest destruction.

If there is no connection to close, then the exception is essentially
ignored with a level-1 error message disclosing the problem. The side
effects of ignoring such exceptions are unknown, but without a client
connection, it is our hope that they would be relatively benign.

This is a Measurement Factory project


+1. Looks like a reasonable temporary fix. Thank you.

The t5 patch which has minor fixes over the posted patch applied to squid-5 branch as r15004.

The same patch apply as is to squid-4 branch.
I am also attaching the patch for squid-3.5



Amos

Reduce crashes due to unexpected ClientHttpRequest termination.

The underlying problem has been known since r13480: If a
ClientHttpRequest job ends without Http::Stream (and ConnStateData)
knowledge, then Squid is likely to segfault or assert. This patch does
not resolve the underlying issue (a proper fix would require
architectural changes in a consensus-lacking area) but makes an
unexpected ClientHttpRequest job destruction less likely.

BodyPipe and Adaptation-related exceptions are the major causes of
unexpected ClientHttpRequest job destruction. This patch handles them by
closing the client connection. Connection closure should trigger an
orderly top-down cleanup, including Http::Stream, ConnStateData, and
ClientHttpRequest destruction.

If there is no connection to close, then the exception is essentially
ignored with a level-1 error message disclosing the problem. The side
effects of ignoring such exceptions are unknown, but without a client
connection, it is our hope that they would be relatively benign.

This is a Measurement Factory project.

=== modified file 'src/client_side_request.cc'
--- src/client_side_request.cc	2017-01-01 00:16:45 +0000
+++ src/client_side_request.cc	2017-01-11 18:15:01 +0000
@@ -2063,22 +2063,36 @@
         noAddr.setNoAddr();
         ConnStateData * c = getConn();
         calloutContext->error = clientBuildError(ERR_ICAP_FAILURE, Http::scInternalServerError,
                                 NULL,
                                 c != NULL ? c->clientConnection->remote : noAddr,
                                 request
                                                 );
 #if USE_AUTH
         calloutContext->error->auth_user_request =
             c != NULL && c->getAuth() != NULL ? c->getAuth() : request->auth_user_request;
 #endif
         calloutContext->error->detailError(errDetail);
         calloutContext->readNextRequest = true;
         if (c != NULL)
             c->expectNoForwarding();
         doCallouts();
     }
     //else if(calloutContext == NULL) is it possible?
 }
 
+void
+ClientHttpRequest::callException(const std::exception &ex)
+{
+    const Comm::ConnectionPointer clientConn = getConn() ? getConn()->clientConnection : NULL;
+    if (Comm::IsConnOpen(clientConn)) {
+        debugs(85, 3, "closing after exception: " << ex.what());
+        clientConn->close(); // initiate orderly top-to-bottom cleanup
+        return;
+    }
+    debugs(85, DBG_IMPORTANT, "ClientHttpRequest exception without connection. Ignoring " << ex.what());
+    // XXX: Normally, we mustStop() but we cannot do that here because it is
+    // likely to leave Http::Stream and ConnStateData with a dangling http
+    // pointer. See r13480 or XXX in Http::Stream class description.
+}
 #endif
 

=== modified file 'src/client_side_request.h'
--- src/client_side_request.h	2017-01-01 00:16:45 +0000
+++ src/client_side_request.h	2017-01-11 17:53:13 +0000
@@ -96,40 +96,41 @@
     } flags;
 
     struct {
         Http::StatusCode status;
         char *location;
     } redirect;
 
     dlink_node active;
     dlink_list client_stream;
     int mRangeCLen();
 
     ClientRequestContext *calloutContext;
     void doCallouts();
 
 #if USE_ADAPTATION
     // AsyncJob virtual methods
     virtual bool doneAll() const {
         return Initiator::doneAll() &&
                BodyConsumer::doneAll() && false;
     }
+    virtual void callException(const std::exception &ex);
 #endif
 
 private:
     int64_t maxReplyBodySize_;
     StoreEntry *entry_;
     StoreEntry *loggingEntry_;
     ConnStateData * conn_;
 
 #if USE_OPENSSL
     /// whether (and how) the request needs to be bumped
     Ssl::BumpMode sslBumpNeed_;
 
 public:
     /// returns raw sslBump mode value
     Ssl::BumpMode sslBumpNeed() const { return sslBumpNeed_; }
     /// returns true if and only if the request needs to be bumped
     bool sslBumpNeeded() const { return sslBumpNeed_ == Ssl::bumpServerFirst || sslBumpNeed_ == Ssl::bumpClientFirst || sslBumpNeed_ == Ssl::bumpBump || sslBumpNeed_ == Ssl::bumpPeek || sslBumpNeed_ == Ssl::bumpStare; }
     /// set the sslBumpNeeded state
     void sslBumpNeed(Ssl::BumpMode mode);
     void sslBumpStart();


Reduce crashes due to unexpected ClientHttpRequest termination.

The underlying problem has been known since r13480: If a
ClientHttpRequest job ends without Http::Stream (and ConnStateData)
knowledge, then Squid is likely to segfault or assert. This patch does
not resolve the underlying issue (a proper fix would require
architectural changes in a consensus-lacking area) but makes an
unexpected ClientHttpRequest job destruction less likely.

BodyPipe and Adaptation-related exceptions are the major causes of
unexpected ClientHttpRequest job destruction. This patch handles them by
closing the client connection. Connection closure should trigger an
orderly top-down cleanup, including Http::Stream, ConnStateData, and
ClientHttpRequest destruction.

If there is no connection to close, then the exception is essentially
ignored with a level-1 error message disclosing the problem. The side
effects of ignoring such exceptions are unknown, but without a client
connection, it is our hope that they would be relatively benign.

This is a Measurement Factory project.

=== modified file 'src/client_side_request.cc'
--- src/client_side_request.cc	2017-01-08 05:12:44 +0000
+++ src/client_side_request.cc	2017-01-11 18:17:47 +0000
@@ -2048,40 +2048,55 @@
 
     if (bypassable && !usedStore && !usedPipe) {
         debugs(85,3, HERE << "ICAP REQMOD callout failed, bypassing: " << calloutContext);
         if (calloutContext)
             doCallouts();
         return;
     }
 
     debugs(85,3, HERE << "ICAP REQMOD callout failed, responding with error");
 
     clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
     clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
     assert(repContext);
 
     calloutsError(ERR_ICAP_FAILURE, errDetail);
 
     if (calloutContext)
         doCallouts();
 }
 
+void
+ClientHttpRequest::callException(const std::exception &ex)
+{
+    if (const auto clientConn = getConn() ? getConn()->clientConnection : nullptr) {
+        if (Comm::IsConnOpen(clientConn)) {
+            debugs(85, 3, "closing after exception: " << ex.what());
+            clientConn->close(); // initiate orderly top-to-bottom cleanup
+            return;
+        }
+    }
+    debugs(85, DBG_IMPORTANT, "ClientHttpRequest exception without connection. Ignoring " << ex.what());
+    // XXX: Normally, we mustStop() but we cannot do that here because it is
+    // likely to leave Http::Stream and ConnStateData with a dangling http
+    // pointer. See r13480 or XXX in Http::Stream class description.
+}
 #endif
 
 // XXX: modify and use with ClientRequestContext::clientAccessCheckDone too.
 void
 ClientHttpRequest::calloutsError(const err_type error, const int errDetail)
 {
     // The original author of the code also wanted to pass an errno to
     // setReplyToError, but it seems unlikely that the errno reflects the
     // true cause of the error at this point, so I did not pass it.
     if (calloutContext) {
         Ip::Address noAddr;
         noAddr.setNoAddr();
         ConnStateData * c = getConn();
         calloutContext->error = clientBuildError(error, Http::scInternalServerError,
                                 NULL,
                                 c != NULL ? c->clientConnection->remote : noAddr,
                                 request
                                                 );
 #if USE_AUTH
         calloutContext->error->auth_user_request =

=== modified file 'src/client_side_request.h'
--- src/client_side_request.h	2017-01-08 05:12:44 +0000
+++ src/client_side_request.h	2017-01-11 18:17:47 +0000
@@ -114,40 +114,41 @@
         Http::StatusCode status;
         char *location;
     } redirect;
 
     dlink_node active;
     dlink_list client_stream;
     int mRangeCLen();
 
     ClientRequestContext *calloutContext;
     void doCallouts();
 
     /// Build an error reply. For use with the callouts.
     void calloutsError(const err_type error, const int errDetail);
 
 #if USE_ADAPTATION
     // AsyncJob virtual methods
     virtual bool doneAll() const {
         return Initiator::doneAll() &&
                BodyConsumer::doneAll() && false;
     }
+    virtual void callException(const std::exception &ex);
 #endif
 
 private:
     int64_t maxReplyBodySize_;
     StoreEntry *entry_;
     StoreEntry *loggingEntry_;
     ConnStateData * conn_;
 
 #if USE_OPENSSL
     /// whether (and how) the request needs to be bumped
     Ssl::BumpMode sslBumpNeed_;
 
 public:
     /// returns raw sslBump mode value
     Ssl::BumpMode sslBumpNeed() const { return sslBumpNeed_; }
     /// returns true if and only if the request needs to be bumped
     bool sslBumpNeeded() const { return sslBumpNeed_ == Ssl::bumpServerFirst || sslBumpNeed_ == Ssl::bumpClientFirst || sslBumpNeed_ == Ssl::bumpBump || sslBumpNeed_ == Ssl::bumpPeek || sslBumpNeed_ == Ssl::bumpStare; }
     /// set the sslBumpNeeded state
     void sslBumpNeed(Ssl::BumpMode mode);
     void sslBumpStart();


_______________________________________________
squid-dev mailing list
squid-dev@lists.squid-cache.org
http://lists.squid-cache.org/listinfo/squid-dev

Reply via email to