Commit 9fe8fe9c authored by Tom Lane's avatar Tom Lane

Add some more query-cancel checks to regular expression matching.

Commit 9662143f added infrastructure to
allow regular-expression operations to be terminated early in the event
of SIGINT etc.  However, fuzz testing by Greg Stark disclosed that there
are still cases where regex compilation could run for a long time without
noticing a cancel request.  Specifically, the fixempties() phase never
adds new states, only new arcs, so it doesn't hit the cancel check I'd put
in newstate().  Add one to newarc() as well to cover that.

Some experimentation of my own found that regex execution could also run
for a long time despite a pending cancel.  We'd put a high-level cancel
check into cdissect(), but there was none inside the core text-matching
routines longest() and shortest().  Ordinarily those inner loops are very
very fast ... but in the presence of lookahead constraints, not so much.
As a compromise, stick a cancel check into the stateset cache-miss
function, which is enough to guarantee a cancel check at least once per
lookahead constraint test.

Making this work required more attention to error handling throughout the
regex executor.  Henry Spencer had apparently originally intended longest()
and shortest() to be incapable of incurring errors while running, so
neither they nor their subroutines had well-defined error reporting
behaviors.  However, that was already broken by the lookahead constraint
feature, since lacon() can surely suffer an out-of-memory failure ---
which, in the code as it stood, might never be reported to the user at all,
but just silently be treated as a non-match of the lookahead constraint.
Normalize all that by inserting explicit error tests as needed.  I took the
opportunity to add some more comments to the code, too.

Back-patch to all supported branches, like the previous patch.
parent 558d4ada
...@@ -180,7 +180,7 @@ newstate(struct nfa * nfa) ...@@ -180,7 +180,7 @@ newstate(struct nfa * nfa)
/* /*
* This is a handy place to check for operation cancel during regex * This is a handy place to check for operation cancel during regex
* compilation, since no code path will go very long without making a new * compilation, since no code path will go very long without making a new
* state. * state or arc.
*/ */
if (CANCEL_REQUESTED(nfa->v->re)) if (CANCEL_REQUESTED(nfa->v->re))
{ {
...@@ -333,6 +333,17 @@ newarc(struct nfa * nfa, ...@@ -333,6 +333,17 @@ newarc(struct nfa * nfa,
assert(from != NULL && to != NULL); assert(from != NULL && to != NULL);
/*
* This is a handy place to check for operation cancel during regex
* compilation, since no code path will go very long without making a new
* state or arc.
*/
if (CANCEL_REQUESTED(nfa->v->re))
{
NERR(REG_CANCEL);
return;
}
/* check for duplicates */ /* check for duplicates */
for (a = from->outs; a != NULL; a = a->outchain) for (a = from->outs; a != NULL; a = a->outchain)
if (a->to == to && a->co == co && a->type == t) if (a->to == to && a->co == co && a->type == t)
......
This diff is collapsed.
...@@ -450,6 +450,11 @@ cfindloop(struct vars * v, ...@@ -450,6 +450,11 @@ cfindloop(struct vars * v,
{ {
MDEBUG(("\ncsearch at %ld\n", LOFF(close))); MDEBUG(("\ncsearch at %ld\n", LOFF(close)));
close = shortest(v, s, close, close, v->stop, &cold, (int *) NULL); close = shortest(v, s, close, close, v->stop, &cold, (int *) NULL);
if (ISERR())
{
*coldp = cold;
return v->err;
}
if (close == NULL) if (close == NULL)
break; /* NOTE BREAK */ break; /* NOTE BREAK */
assert(cold != NULL); assert(cold != NULL);
...@@ -469,6 +474,11 @@ cfindloop(struct vars * v, ...@@ -469,6 +474,11 @@ cfindloop(struct vars * v,
else else
end = longest(v, d, begin, estop, end = longest(v, d, begin, estop,
&hitend); &hitend);
if (ISERR())
{
*coldp = cold;
return v->err;
}
if (hitend && cold == NULL) if (hitend && cold == NULL)
cold = begin; cold = begin;
if (end == NULL) if (end == NULL)
...@@ -681,6 +691,7 @@ ccondissect(struct vars * v, ...@@ -681,6 +691,7 @@ ccondissect(struct vars * v,
/* pick a tentative midpoint */ /* pick a tentative midpoint */
mid = longest(v, d, begin, end, (int *) NULL); mid = longest(v, d, begin, end, (int *) NULL);
NOERR();
if (mid == NULL) if (mid == NULL)
return REG_NOMATCH; return REG_NOMATCH;
MDEBUG(("tentative midpoint %ld\n", LOFF(mid))); MDEBUG(("tentative midpoint %ld\n", LOFF(mid)));
...@@ -705,6 +716,7 @@ ccondissect(struct vars * v, ...@@ -705,6 +716,7 @@ ccondissect(struct vars * v,
if (er != REG_NOMATCH) if (er != REG_NOMATCH)
return er; return er;
} }
NOERR();
/* that midpoint didn't work, find a new one */ /* that midpoint didn't work, find a new one */
if (mid == begin) if (mid == begin)
...@@ -714,6 +726,7 @@ ccondissect(struct vars * v, ...@@ -714,6 +726,7 @@ ccondissect(struct vars * v,
return REG_NOMATCH; return REG_NOMATCH;
} }
mid = longest(v, d, begin, mid - 1, (int *) NULL); mid = longest(v, d, begin, mid - 1, (int *) NULL);
NOERR();
if (mid == NULL) if (mid == NULL)
{ {
/* failed to find a new one */ /* failed to find a new one */
...@@ -756,6 +769,7 @@ crevcondissect(struct vars * v, ...@@ -756,6 +769,7 @@ crevcondissect(struct vars * v,
/* pick a tentative midpoint */ /* pick a tentative midpoint */
mid = shortest(v, d, begin, begin, end, (chr **) NULL, (int *) NULL); mid = shortest(v, d, begin, begin, end, (chr **) NULL, (int *) NULL);
NOERR();
if (mid == NULL) if (mid == NULL)
return REG_NOMATCH; return REG_NOMATCH;
MDEBUG(("tentative midpoint %ld\n", LOFF(mid))); MDEBUG(("tentative midpoint %ld\n", LOFF(mid)));
...@@ -780,6 +794,7 @@ crevcondissect(struct vars * v, ...@@ -780,6 +794,7 @@ crevcondissect(struct vars * v,
if (er != REG_NOMATCH) if (er != REG_NOMATCH)
return er; return er;
} }
NOERR();
/* that midpoint didn't work, find a new one */ /* that midpoint didn't work, find a new one */
if (mid == end) if (mid == end)
...@@ -789,6 +804,7 @@ crevcondissect(struct vars * v, ...@@ -789,6 +804,7 @@ crevcondissect(struct vars * v,
return REG_NOMATCH; return REG_NOMATCH;
} }
mid = shortest(v, d, begin, mid + 1, end, (chr **) NULL, (int *) NULL); mid = shortest(v, d, begin, mid + 1, end, (chr **) NULL, (int *) NULL);
NOERR();
if (mid == NULL) if (mid == NULL)
{ {
/* failed to find a new one */ /* failed to find a new one */
...@@ -914,6 +930,7 @@ caltdissect(struct vars * v, ...@@ -914,6 +930,7 @@ caltdissect(struct vars * v,
if (er != REG_NOMATCH) if (er != REG_NOMATCH)
return er; return er;
} }
NOERR();
t = t->right; t = t->right;
} }
...@@ -1005,6 +1022,11 @@ citerdissect(struct vars * v, ...@@ -1005,6 +1022,11 @@ citerdissect(struct vars * v,
{ {
/* try to find an endpoint for the k'th sub-match */ /* try to find an endpoint for the k'th sub-match */
endpts[k] = longest(v, d, endpts[k - 1], limit, (int *) NULL); endpts[k] = longest(v, d, endpts[k - 1], limit, (int *) NULL);
if (ISERR())
{
FREE(endpts);
return v->err;
}
if (endpts[k] == NULL) if (endpts[k] == NULL)
{ {
/* no match possible, so see if we can shorten previous one */ /* no match possible, so see if we can shorten previous one */
...@@ -1201,6 +1223,11 @@ creviterdissect(struct vars * v, ...@@ -1201,6 +1223,11 @@ creviterdissect(struct vars * v,
/* try to find an endpoint for the k'th sub-match */ /* try to find an endpoint for the k'th sub-match */
endpts[k] = shortest(v, d, endpts[k - 1], limit, end, endpts[k] = shortest(v, d, endpts[k - 1], limit, end,
(chr **) NULL, (int *) NULL); (chr **) NULL, (int *) NULL);
if (ISERR())
{
FREE(endpts);
return v->err;
}
if (endpts[k] == NULL) if (endpts[k] == NULL)
{ {
/* no match possible, so see if we can lengthen previous one */ /* no match possible, so see if we can lengthen previous one */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment