ported from:

commit 7aa0184a720fd216191474e079f4fe87de7c4f5a (refs/remotes/origin/v4)
Author: Eduard Bagdasaryan <eduard.bagdasaryan@measurement-factory.com>
Date:   2019-09-15 15:32:30 +0000

    Fix URN response handling (#472)
    
    urnHandleReply() may be called several times while copying the entry
    from the store. Each time it must use the buffer length that is left
    (from the previous call).
    
    Also do not abandon a urn entry, still having clients attached.
    
    Also allow urnHandleReply() to produce a reply if it receives a
    zero-sized buffer. This may happen after the entry has been fully
    stored.
    
    CID 1453857: Error handling issues (UNCAUGHT_EXCEPT)
    
    Due to various Store deficiencies, storeUnregister() might call swapout
    code which Broadcast()s and throws Ipc::OneToOneUniQueue::ItemTooLarge.

Index: squid-3.5.21/src/urn.cc
===================================================================
--- squid-3.5.21.orig/src/urn.cc
+++ squid-3.5.21/src/urn.cc
@@ -9,6 +9,7 @@
 /* DEBUG: section 52    URN Parsing */
 
 #include "squid.h"
+#include "base/TextException.h"
 #include "cbdata.h"
 #include "errorpage.h"
 #include "FwdState.h"
@@ -28,6 +29,36 @@
 
 #define URN_REQBUF_SZ   4096
 
+std::ostream &
+CurrentException(std::ostream &os)
+{
+    if (std::current_exception()) {
+        try {
+            throw; // re-throw to recognize the exception type
+        }
+        catch (const std::exception &ex) {
+            os << ex.what();
+        }
+        catch (...) {
+            os << "[unknown exception type]";
+        }
+    } else {
+        os << "[no active exception]";
+    }
+    return os;
+}
+
+/// Reports and swallows all exceptions to prevent compiler warnings and runtime
+/// errors related to throwing class destructors. Should be used for most dtors.
+#define SWALLOW_EXCEPTIONS(code) \
+    try { \
+        code \
+    } catch (...) { \
+        debugs(0, DBG_IMPORTANT, "BUG: ignoring exception;\n" << \
+               "    bug location: " << __FILE__ << ":" << __LINE__ << "\n" << \
+               "    ignored exception: " << CurrentException); \
+    }
+
 class UrnState : public StoreClient
 {
 
@@ -79,7 +110,18 @@ CBDATA_CLASS_INIT(UrnState);
 
 UrnState::~UrnState()
 {
-    safe_free(urlres);
+    SWALLOW_EXCEPTIONS({
+        if (urlres_e) {
+            if (sc)
+                storeUnregister(sc, urlres_e, this);
+            urlres_e->unlock("~UrnState+res");
+        }
+
+        if (entry)
+            entry->unlock("~UrnState+prime");
+
+        safe_free(urlres);
+    });
 }
 
 static url_entry *
@@ -260,14 +302,6 @@ url_entry_sort(const void *A, const void
         return u1->rtt - u2->rtt;
 }
 
-static void
-urnHandleReplyError(UrnState *urnState, StoreEntry *urlres_e)
-{
-    urlres_e->unlock("urnHandleReplyError+res");
-    urnState->entry->unlock("urnHandleReplyError+prime");
-    delete urnState;
-}
-
 /* TODO: use the clientStream support for this */
 static void
 urnHandleReply(void *data, StoreIOBuffer result)
@@ -290,8 +324,8 @@ urnHandleReply(void *data, StoreIOBuffer
 
     debugs(52, 3, "urnHandleReply: Called with size=" << result.length << ".");
 
-    if (EBIT_TEST(urlres_e->flags, ENTRY_ABORTED) || result.length == 0 || result.flags.error) {
-        urnHandleReplyError(urnState, urlres_e);
+    if (EBIT_TEST(urlres_e->flags, ENTRY_ABORTED) || result.flags.error) {
+        delete urnState;
         return;
     }
 
@@ -300,15 +334,15 @@ urnHandleReply(void *data, StoreIOBuffer
 
     /* Handle reqofs being bigger than normal */
     if (urnState->reqofs >= URN_REQBUF_SZ) {
-        urnHandleReplyError(urnState, urlres_e);
+        delete urnState;
         return;
     }
 
     /* If we haven't received the entire object (urn), copy more */
-    if (urlres_e->store_status == STORE_PENDING &&
-            urnState->reqofs < URN_REQBUF_SZ) {
+    if (urlres_e->store_status == STORE_PENDING) {
+        Must(result.length > 0); // zero length ought to imply STORE_OK
         tempBuffer.offset = urnState->reqofs;
-        tempBuffer.length = URN_REQBUF_SZ;
+        tempBuffer.length = URN_REQBUF_SZ - urnState->reqofs;
         tempBuffer.data = urnState->reqbuf + urnState->reqofs;
         storeClientCopy(urnState->sc, urlres_e,
                         tempBuffer,
@@ -322,7 +356,7 @@ urnHandleReply(void *data, StoreIOBuffer
 
     if (0 == k) {
         debugs(52, DBG_IMPORTANT, "urnHandleReply: didn't find end-of-headers for " << e->url()  );
-        urnHandleReplyError(urnState, urlres_e);
+        delete urnState;
         return;
     }
 
@@ -338,7 +372,7 @@ urnHandleReply(void *data, StoreIOBuffer
         err->url = xstrdup(e->url());
         errorAppendEntry(e, err);
         delete rep;
-        urnHandleReplyError(urnState, urlres_e);
+        delete urnState;
         return;
     }
 
@@ -359,7 +393,7 @@ urnHandleReply(void *data, StoreIOBuffer
         err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, urnState->request.getRaw());
         err->url = xstrdup(e->url());
         errorAppendEntry(e, err);
-        urnHandleReplyError(urnState, urlres_e);
+        delete urnState;
         return;
     }
 
@@ -416,10 +450,7 @@ urnHandleReply(void *data, StoreIOBuffer
     }
 
     safe_free(urls);
-    /* mb was absorbed in httpBodySet call, so we must not clean it */
-    storeUnregister(urnState->sc, urlres_e, urnState);
-
-    urnHandleReplyError(urnState, urlres_e);
+    delete urnState;
 }
 
 static url_entry *
