712647
diff --git a/src/esi/Context.h b/src/esi/Context.h
712647
index be49742..34b1fd0 100644
712647
--- a/src/esi/Context.h
712647
+++ b/src/esi/Context.h
712647
@@ -12,6 +12,7 @@
712647
 #include "clientStream.h"
712647
 #include "err_type.h"
712647
 #include "esi/Element.h"
712647
+#include "esi/Esi.h"
712647
 #include "esi/Parser.h"
712647
 #include "http/StatusCode.h"
712647
 #include "HttpReply.h"
712647
@@ -112,7 +113,7 @@ public:
712647
     {
712647
 
712647
     public:
712647
-        ESIElement::Pointer stack[10]; /* a stack of esi elements that are open */
712647
+        ESIElement::Pointer stack[ESI_STACK_DEPTH_LIMIT]; /* a stack of esi elements that are open */
712647
         int stackdepth; /* self explanatory */
712647
         ESIParser::Pointer theParser;
712647
         ESIElement::Pointer top();
712647
diff --git a/src/esi/Esi.cc b/src/esi/Esi.cc
712647
index 1816c76..674bae2 100644
712647
--- a/src/esi/Esi.cc
712647
+++ b/src/esi/Esi.cc
712647
@@ -29,6 +29,7 @@
712647
 #include "esi/Expression.h"
712647
 #include "esi/Segment.h"
712647
 #include "esi/VarState.h"
712647
+#include "FadingCounter.h"
712647
 #include "HttpHdrSc.h"
712647
 #include "HttpHdrScTarget.h"
712647
 #include "HttpReply.h"
712647
@@ -943,13 +944,18 @@ void
712647
 ESIContext::addStackElement (ESIElement::Pointer element)
712647
 {
712647
     /* Put on the stack to allow skipping of 'invalid' markup */
712647
-    assert (parserState.stackdepth <11);
712647
+
712647
+    // throw an error if the stack location would be invalid
712647
+    if (parserState.stackdepth >= ESI_STACK_DEPTH_LIMIT)
712647
+        throw Esi::Error("ESI Too many nested elements");
712647
+    if (parserState.stackdepth < 0)
712647
+        throw Esi::Error("ESI elements stack error, probable error in ESI template");
712647
+
712647
     assert (!failed());
712647
     debugs(86, 5, "ESIContext::addStackElement: About to add ESI Node " << element.getRaw());
712647
 
712647
     if (!parserState.top()->addElement(element)) {
712647
-        debugs(86, DBG_IMPORTANT, "ESIContext::addStackElement: failed to add esi node, probable error in ESI template");
712647
-        flags.error = 1;
712647
+        throw Esi::Error("ESIContext::addStackElement failed, probable error in ESI template");
712647
     } else {
712647
         /* added ok, push onto the stack */
712647
         parserState.stack[parserState.stackdepth] = element;
712647
@@ -1201,13 +1207,10 @@ ESIContext::addLiteral (const char *s, int len)
712647
     assert (len);
712647
     debugs(86, 5, "literal length is " << len);
712647
     /* give a literal to the current element */
712647
-    assert (parserState.stackdepth <11);
712647
     ESIElement::Pointer element (new esiLiteral (this, s, len));
712647
 
712647
-    if (!parserState.top()->addElement(element)) {
712647
-        debugs(86, DBG_IMPORTANT, "ESIContext::addLiteral: failed to add esi node, probable error in ESI template");
712647
-        flags.error = 1;
712647
-    }
712647
+    if (!parserState.top()->addElement(element))
712647
+        throw Esi::Error("ESIContext::addLiteral failed, probable error in ESI template");
712647
 }
712647
 
712647
 void
712647
@@ -1269,8 +1272,24 @@ ESIContext::parse()
712647
 
712647
         PROF_start(esiParsing);
712647
 
712647
-        while (buffered.getRaw() && !flags.error)
712647
-            parseOneBuffer();
712647
+        try {
712647
+            while (buffered.getRaw() && !flags.error)
712647
+                parseOneBuffer();
712647
+
712647
+        } catch (Esi::ErrorDetail &errMsg) { // FIXME: non-const for c_str()
712647
+            // level-2: these are protocol/syntax errors from upstream
712647
+            debugs(86, 2, "WARNING: ESI syntax error: " << errMsg);
712647
+            setError();
712647
+            setErrorMessage(errMsg.c_str());
712647
+
712647
+        } catch (...) {
712647
+            // DBG_IMPORTANT because these are local issues the admin needs to fix
712647
+            static FadingCounter logEntries; // TODO: set horizon less than infinity
712647
+            if (logEntries.count(1) < 100)
712647
+                debugs(86, DBG_IMPORTANT, "ERROR: ESI parser: unhandled exception");
712647
+            setError();
712647
+            setErrorMessage("ESI parser error");
712647
+        }
712647
 
712647
         PROF_stop(esiParsing);
712647
 
712647
diff --git a/src/esi/Esi.h b/src/esi/Esi.h
712647
index bbdb566..85f80f7 100644
712647
--- a/src/esi/Esi.h
712647
+++ b/src/esi/Esi.h
712647
@@ -10,6 +10,11 @@
712647
 #define SQUID_ESI_H
712647
 
712647
 #include "clientStream.h"
712647
+#include "SBuf.h"
712647
+
712647
+#if !defined(ESI_STACK_DEPTH_LIMIT)
712647
+#define ESI_STACK_DEPTH_LIMIT 20
712647
+#endif
712647
 
712647
 /* ESI.c */
712647
 extern CSR esiStreamRead;
712647
@@ -18,5 +23,14 @@ extern CSD esiStreamDetach;
712647
 extern CSS esiStreamStatus;
712647
 int esiEnableProcessing (HttpReply *);
712647
 
712647
+namespace Esi
712647
+{
712647
+
712647
+typedef SBuf ErrorDetail;
712647
+/// prepare an Esi::ErrorDetail for throw on ESI parser internal errors
712647
+inline Esi::ErrorDetail Error(const char *msg) { return ErrorDetail(msg); }
712647
+
712647
+} // namespace Esi
712647
+
712647
 #endif /* SQUID_ESI_H */
712647
 
712647
diff --git a/src/esi/Expression.cc b/src/esi/Expression.cc
712647
index 8a1d3e9..a65edfb 100644
712647
--- a/src/esi/Expression.cc
712647
+++ b/src/esi/Expression.cc
712647
@@ -10,6 +10,7 @@
712647
 
712647
 #include "squid.h"
712647
 #include "Debug.h"
712647
+#include "esi/Esi.h"
712647
 #include "esi/Expression.h"
712647
 #include "profiler/Profiler.h"
712647
 
712647
@@ -97,6 +98,17 @@ stackpop(stackmember * s, int *depth)
712647
     cleanmember(&s[*depth]);
712647
 }
712647
 
712647
+static void
712647
+stackpush(stackmember *stack, stackmember &item, int *depth)
712647
+{
712647
+    if (*depth < 0)
712647
+        throw Esi::Error("ESIExpression stack has negative size");
712647
+    if (*depth >= ESI_STACK_DEPTH_LIMIT)
712647
+        throw Esi::Error("ESIExpression stack is full, cannot push");
712647
+
712647
+    stack[(*depth)++] = item;
712647
+}
712647
+
712647
 static evaluate evalnegate;
712647
 static evaluate evalliteral;
712647
 static evaluate evalor;
712647
@@ -208,6 +220,11 @@ evalnegate(stackmember * stack, int *depth, int whereAmI, stackmember * candidat
712647
         /* invalid stack */
712647
         return 1;
712647
 
712647
+    if (whereAmI < 0)
712647
+        throw Esi::Error("negate expression location too small");
712647
+    if (*depth >= ESI_STACK_DEPTH_LIMIT)
712647
+        throw Esi::Error("negate expression too complex");
712647
+
712647
     if (stack[whereAmI + 1].valuetype != ESI_EXPR_EXPR)
712647
         /* invalid operand */
712647
         return 1;
712647
@@ -280,7 +297,7 @@ evalor(stackmember * stack, int *depth, int whereAmI, stackmember * candidate)
712647
 
712647
     srv.precedence = 1;
712647
 
712647
-    stack[(*depth)++] = srv;
712647
+    stackpush(stack, srv, depth);
712647
 
712647
     /* we're out of way, try adding now */
712647
     if (!addmember(stack, depth, candidate))
712647
@@ -327,7 +344,7 @@ evaland(stackmember * stack, int *depth, int whereAmI, stackmember * candidate)
712647
 
712647
     srv.precedence = 1;
712647
 
712647
-    stack[(*depth)++] = srv;
712647
+    stackpush(stack, srv, depth);
712647
 
712647
     /* we're out of way, try adding now */
712647
     if (!addmember(stack, depth, candidate))
712647
@@ -373,7 +390,7 @@ evallesseq(stackmember * stack, int *depth, int whereAmI, stackmember * candidat
712647
 
712647
     srv.precedence = 1;
712647
 
712647
-    stack[(*depth)++] = srv;
712647
+    stackpush(stack, srv, depth);
712647
 
712647
     /* we're out of way, try adding now */
712647
     if (!addmember(stack, depth, candidate))
712647
@@ -421,7 +438,7 @@ evallessthan(stackmember * stack, int *depth, int whereAmI, stackmember * candid
712647
 
712647
     srv.precedence = 1;
712647
 
712647
-    stack[(*depth)++] = srv;
712647
+    stackpush(stack, srv, depth);
712647
 
712647
     /* we're out of way, try adding now */
712647
     if (!addmember(stack, depth, candidate))
712647
@@ -469,7 +486,7 @@ evalmoreeq(stackmember * stack, int *depth, int whereAmI, stackmember * candidat
712647
 
712647
     srv.precedence = 1;
712647
 
712647
-    stack[(*depth)++] = srv;
712647
+    stackpush(stack, srv, depth);
712647
 
712647
     /* we're out of way, try adding now */
712647
     if (!addmember(stack, depth, candidate))
712647
@@ -517,7 +534,7 @@ evalmorethan(stackmember * stack, int *depth, int whereAmI, stackmember * candid
712647
 
712647
     srv.precedence = 1;
712647
 
712647
-    stack[(*depth)++] = srv;
712647
+    stackpush(stack, srv, depth);
712647
 
712647
     /* we're out of way, try adding now */
712647
     if (!addmember(stack, depth, candidate))
712647
@@ -566,7 +583,7 @@ evalequals(stackmember * stack, int *depth, int whereAmI,
712647
 
712647
     srv.precedence = 1;
712647
 
712647
-    stack[(*depth)++] = srv;
712647
+    stackpush(stack, srv, depth);
712647
 
712647
     /* we're out of way, try adding now */
712647
     if (!addmember(stack, depth, candidate))
712647
@@ -613,7 +630,7 @@ evalnotequals(stackmember * stack, int *depth, int whereAmI, stackmember * candi
712647
 
712647
     srv.precedence = 1;
712647
 
712647
-    stack[(*depth)++] = srv;
712647
+    stackpush(stack, srv, depth);
712647
 
712647
     /* we're out of way, try adding now */
712647
     if (!addmember(stack, depth, candidate))
712647
@@ -953,6 +970,9 @@ addmember(stackmember * stack, int *stackdepth, stackmember * candidate)
712647
         /* !(!(a==b))) is why thats safe */
712647
         /* strictly less than until we unwind */
712647
 
712647
+        if (*stackdepth >= ESI_STACK_DEPTH_LIMIT)
712647
+            throw Esi::Error("ESI expression too complex to add member");
712647
+
712647
         if (candidate->precedence < stack[*stackdepth - 1].precedence ||
712647
                 candidate->precedence < stack[*stackdepth - 2].precedence) {
712647
             /* must be an operator */
712647
@@ -968,10 +988,10 @@ addmember(stackmember * stack, int *stackdepth, stackmember * candidate)
712647
                 return 0;
712647
             }
712647
         } else {
712647
-            stack[(*stackdepth)++] = *candidate;
712647
+            stackpush(stack, *candidate, stackdepth);
712647
         }
712647
     } else if (candidate->valuetype != ESI_EXPR_INVALID)
712647
-        stack[(*stackdepth)++] = *candidate;
712647
+        stackpush(stack, *candidate, stackdepth);
712647
 
712647
     return 1;
712647
 }
712647
@@ -979,7 +999,7 @@ addmember(stackmember * stack, int *stackdepth, stackmember * candidate)
712647
 int
712647
 ESIExpression::Evaluate(char const *s)
712647
 {
712647
-    stackmember stack[20];
712647
+    stackmember stack[ESI_STACK_DEPTH_LIMIT];
712647
     int stackdepth = 0;
712647
     char const *end;
712647
     PROF_start(esiExpressionEval);