Commit a7b61d4f authored by Tom Lane's avatar Tom Lane

Fix infinite-loop risk in fixempties() stage of regex compilation.

The previous coding of this function could get into situations where it
would never terminate, because successive passes would re-add EMPTY arcs
that had been removed by the previous pass.  Rewrite the function
completely using a new algorithm that is guaranteed to terminate, and
also seems to be usually faster than the old one.  Per Tcl bugs 3604074
and 3606683.

Tom Lane and Don Porter
parent 7ccefe86
This diff is collapsed.
...@@ -122,12 +122,15 @@ static void destroystate(struct nfa *, struct state *); ...@@ -122,12 +122,15 @@ static void destroystate(struct nfa *, struct state *);
static void newarc(struct nfa *, int, pcolor, struct state *, struct state *); static void newarc(struct nfa *, int, pcolor, struct state *, struct state *);
static struct arc *allocarc(struct nfa *, struct state *); static struct arc *allocarc(struct nfa *, struct state *);
static void freearc(struct nfa *, struct arc *); static void freearc(struct nfa *, struct arc *);
static int hasnonemptyout(struct state *);
static int nonemptyouts(struct state *);
static int nonemptyins(struct state *);
static struct arc *findarc(struct state *, int, pcolor); static struct arc *findarc(struct state *, int, pcolor);
static void cparc(struct nfa *, struct arc *, struct state *, struct state *); static void cparc(struct nfa *, struct arc *, struct state *, struct state *);
static void moveins(struct nfa *, struct state *, struct state *); static void moveins(struct nfa *, struct state *, struct state *);
static void copyins(struct nfa *, struct state *, struct state *); static void copyins(struct nfa *, struct state *, struct state *, int);
static void moveouts(struct nfa *, struct state *, struct state *); static void moveouts(struct nfa *, struct state *, struct state *);
static void copyouts(struct nfa *, struct state *, struct state *); static void copyouts(struct nfa *, struct state *, struct state *, int);
static void cloneouts(struct nfa *, struct state *, struct state *, struct state *, int); static void cloneouts(struct nfa *, struct state *, struct state *, struct state *, int);
static void delsub(struct nfa *, struct state *, struct state *); static void delsub(struct nfa *, struct state *, struct state *);
static void deltraverse(struct nfa *, struct state *, struct state *); static void deltraverse(struct nfa *, struct state *, struct state *);
...@@ -146,7 +149,8 @@ static int push(struct nfa *, struct arc *); ...@@ -146,7 +149,8 @@ static int push(struct nfa *, struct arc *);
#define COMPATIBLE 3 /* compatible but not satisfied yet */ #define COMPATIBLE 3 /* compatible but not satisfied yet */
static int combine(struct arc *, struct arc *); static int combine(struct arc *, struct arc *);
static void fixempties(struct nfa *, FILE *); static void fixempties(struct nfa *, FILE *);
static int unempty(struct nfa *, struct arc *); static struct state *emptyreachable(struct state *, struct state *);
static void replaceempty(struct nfa *, struct state *, struct state *);
static void cleanup(struct nfa *); static void cleanup(struct nfa *);
static void markreachable(struct nfa *, struct state *, struct state *, struct state *); static void markreachable(struct nfa *, struct state *, struct state *, struct state *);
static void markcanreach(struct nfa *, struct state *, struct state *, struct state *); static void markcanreach(struct nfa *, struct state *, struct state *, struct state *);
...@@ -583,7 +587,7 @@ makesearch(struct vars * v, ...@@ -583,7 +587,7 @@ makesearch(struct vars * v,
for (s = slist; s != NULL; s = s2) for (s = slist; s != NULL; s = s2)
{ {
s2 = newstate(nfa); s2 = newstate(nfa);
copyouts(nfa, s, s2); copyouts(nfa, s, s2, 1);
for (a = s->ins; a != NULL; a = b) for (a = s->ins; a != NULL; a = b)
{ {
b = a->inchain; b = a->inchain;
......
...@@ -153,3 +153,23 @@ explain (costs off) select * from pg_proc where proname ~ '^(abc)?d'; ...@@ -153,3 +153,23 @@ explain (costs off) select * from pg_proc where proname ~ '^(abc)?d';
Filter: (proname ~ '^(abc)?d'::text) Filter: (proname ~ '^(abc)?d'::text)
(2 rows) (2 rows)
-- Test for infinite loop in pullback() (CVE-2007-4772)
select 'a' ~ '($|^)*';
?column?
----------
t
(1 row)
-- Test for infinite loop in fixempties() (Tcl bugs 3604074, 3606683)
select 'a' ~ '((((((a)*)*)*)*)*)*';
?column?
----------
t
(1 row)
select 'a' ~ '((((((a+|)+|)+|)+|)+|)+|)';
?column?
----------
t
(1 row)
...@@ -34,3 +34,10 @@ explain (costs off) select * from pg_proc where proname ~ '^abc+d'; ...@@ -34,3 +34,10 @@ explain (costs off) select * from pg_proc where proname ~ '^abc+d';
explain (costs off) select * from pg_proc where proname ~ '^(abc)(def)'; explain (costs off) select * from pg_proc where proname ~ '^(abc)(def)';
explain (costs off) select * from pg_proc where proname ~ '^(abc)$'; explain (costs off) select * from pg_proc where proname ~ '^(abc)$';
explain (costs off) select * from pg_proc where proname ~ '^(abc)?d'; explain (costs off) select * from pg_proc where proname ~ '^(abc)?d';
-- Test for infinite loop in pullback() (CVE-2007-4772)
select 'a' ~ '($|^)*';
-- Test for infinite loop in fixempties() (Tcl bugs 3604074, 3606683)
select 'a' ~ '((((((a)*)*)*)*)*)*';
select 'a' ~ '((((((a+|)+|)+|)+|)+|)+|)';
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