aboutsummaryrefslogtreecommitdiff
path: root/src/backend/regex/regexec.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2015-10-02 13:45:39 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2015-10-02 13:45:39 -0400
commit9fe8fe9c9e5d7fc099acfc96e976ee72b2b49865 (patch)
treeee9ffcf4daea2872ad79f6dd0f9103ac270825cc /src/backend/regex/regexec.c
parent558d4ada1851274fe4dd3618f3f6561b63857e8f (diff)
downloadpostgresql-9fe8fe9c9e5d7fc099acfc96e976ee72b2b49865.tar.gz
postgresql-9fe8fe9c9e5d7fc099acfc96e976ee72b2b49865.zip
Add some more query-cancel checks to regular expression matching.
Commit 9662143f0c35d64d7042fbeaf879df8f0b54be32 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.
Diffstat (limited to 'src/backend/regex/regexec.c')
-rw-r--r--src/backend/regex/regexec.c27
1 files changed, 27 insertions, 0 deletions
diff --git a/src/backend/regex/regexec.c b/src/backend/regex/regexec.c
index 8505994747f..694a03965c0 100644
--- a/src/backend/regex/regexec.c
+++ b/src/backend/regex/regexec.c
@@ -450,6 +450,11 @@ cfindloop(struct vars * v,
{
MDEBUG(("\ncsearch at %ld\n", LOFF(close)));
close = shortest(v, s, close, close, v->stop, &cold, (int *) NULL);
+ if (ISERR())
+ {
+ *coldp = cold;
+ return v->err;
+ }
if (close == NULL)
break; /* NOTE BREAK */
assert(cold != NULL);
@@ -469,6 +474,11 @@ cfindloop(struct vars * v,
else
end = longest(v, d, begin, estop,
&hitend);
+ if (ISERR())
+ {
+ *coldp = cold;
+ return v->err;
+ }
if (hitend && cold == NULL)
cold = begin;
if (end == NULL)
@@ -681,6 +691,7 @@ ccondissect(struct vars * v,
/* pick a tentative midpoint */
mid = longest(v, d, begin, end, (int *) NULL);
+ NOERR();
if (mid == NULL)
return REG_NOMATCH;
MDEBUG(("tentative midpoint %ld\n", LOFF(mid)));
@@ -705,6 +716,7 @@ ccondissect(struct vars * v,
if (er != REG_NOMATCH)
return er;
}
+ NOERR();
/* that midpoint didn't work, find a new one */
if (mid == begin)
@@ -714,6 +726,7 @@ ccondissect(struct vars * v,
return REG_NOMATCH;
}
mid = longest(v, d, begin, mid - 1, (int *) NULL);
+ NOERR();
if (mid == NULL)
{
/* failed to find a new one */
@@ -756,6 +769,7 @@ crevcondissect(struct vars * v,
/* pick a tentative midpoint */
mid = shortest(v, d, begin, begin, end, (chr **) NULL, (int *) NULL);
+ NOERR();
if (mid == NULL)
return REG_NOMATCH;
MDEBUG(("tentative midpoint %ld\n", LOFF(mid)));
@@ -780,6 +794,7 @@ crevcondissect(struct vars * v,
if (er != REG_NOMATCH)
return er;
}
+ NOERR();
/* that midpoint didn't work, find a new one */
if (mid == end)
@@ -789,6 +804,7 @@ crevcondissect(struct vars * v,
return REG_NOMATCH;
}
mid = shortest(v, d, begin, mid + 1, end, (chr **) NULL, (int *) NULL);
+ NOERR();
if (mid == NULL)
{
/* failed to find a new one */
@@ -914,6 +930,7 @@ caltdissect(struct vars * v,
if (er != REG_NOMATCH)
return er;
}
+ NOERR();
t = t->right;
}
@@ -1005,6 +1022,11 @@ citerdissect(struct vars * v,
{
/* try to find an endpoint for the k'th sub-match */
endpts[k] = longest(v, d, endpts[k - 1], limit, (int *) NULL);
+ if (ISERR())
+ {
+ FREE(endpts);
+ return v->err;
+ }
if (endpts[k] == NULL)
{
/* no match possible, so see if we can shorten previous one */
@@ -1201,6 +1223,11 @@ creviterdissect(struct vars * v,
/* try to find an endpoint for the k'th sub-match */
endpts[k] = shortest(v, d, endpts[k - 1], limit, end,
(chr **) NULL, (int *) NULL);
+ if (ISERR())
+ {
+ FREE(endpts);
+ return v->err;
+ }
if (endpts[k] == NULL)
{
/* no match possible, so see if we can lengthen previous one */