]> granicus.if.org Git - postgresql/commitdiff
Fix array overrun in regex code.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 24 May 2012 17:56:16 +0000 (13:56 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 24 May 2012 17:56:16 +0000 (13:56 -0400)
zaptreesubs() was coded to unconditionally reset a capture subre's
corresponding pmatch[] entry.  However, in regexes without backrefs, that
array is caller-supplied and might not have as many entries as the regex
has capturing parens.  So check the array length and do nothing if there
is no corresponding entry, much as subset() does.  Failure to check this
resulted in a stack clobber in the case reported by Marko Kreen.

This bug appears to have been latent in the regex library from the
beginning.  It was not exposed because find() called dissect() not
cdissect(), and the dissect() code path didn't ever call zaptreesubs()
(formerly zapmem()).  When I unified dissect() and cdissect() in commit
4dd78bf37aa29d04b3f358b08c4a2fa43cf828e7, the problem was exposed.

Now that I've seen this, I'm rather suspicious that we might need to
back-patch it; but will refrain for now, for lack of evidence that
the case can be hit in the previous coding.

src/backend/regex/regexec.c
src/test/regress/expected/regex.out
src/test/regress/sql/regex.sql

index c9c73e978b65e62b17d9402f2d3c3ea358981a4f..5d7415b3c1ab973d86411206593f2f0a085199c0 100644 (file)
@@ -531,9 +531,14 @@ zaptreesubs(struct vars * v,
 {
        if (t->op == '(')
        {
-               assert(t->subno > 0);
-               v->pmatch[t->subno].rm_so = -1;
-               v->pmatch[t->subno].rm_eo = -1;
+               int             n = t->subno;
+
+               assert(n > 0);
+               if ((size_t) n < v->nmatch)
+               {
+                       v->pmatch[n].rm_so = -1;
+                       v->pmatch[n].rm_eo = -1;
+               }
        }
 
        if (t->left != NULL)
@@ -543,7 +548,7 @@ zaptreesubs(struct vars * v,
 }
 
 /*
- * subset - set any subexpression relevant to a successful subre
+ * subset - set subexpression match data for a successful subre
  */
 static void
 subset(struct vars * v,
index 4acc4a47a03bca1ad6303765c519f414fdbf5c42..dc0c713b408e4ab46b429e08a855472d0a91ee8c 100644 (file)
@@ -71,3 +71,22 @@ select 'abc abc abd' ~ '^(.+)( \1)+$' as f;
  f
 (1 row)
 
+-- Test some cases that crashed in 9.2beta1 due to pmatch[] array overrun
+select substring('asd TO foo' from ' TO (([a-z0-9._]+|"([^"]+|"")+")+)');
+ substring 
+-----------
+ foo
+(1 row)
+
+select substring('a' from '((a))+');
+ substring 
+-----------
+ a
+(1 row)
+
+select substring('a' from '((a)+)');
+ substring 
+-----------
+ a
+(1 row)
+
index b5315a3df6ddda340d2bf6f27137aba1210267f1..9fdcb2f5bd5bfd130018dec96ee792a9a4dfafc2 100644 (file)
@@ -19,3 +19,8 @@ select 'abc abc abd' ~ '^(\w+)( \1)+$' as f;
 select 'abc abc abc' ~ '^(.+)( \1)+$' as t;
 select 'abc abd abc' ~ '^(.+)( \1)+$' as f;
 select 'abc abc abd' ~ '^(.+)( \1)+$' as f;
+
+-- Test some cases that crashed in 9.2beta1 due to pmatch[] array overrun
+select substring('asd TO foo' from ' TO (([a-z0-9._]+|"([^"]+|"")+")+)');
+select substring('a' from '((a))+');
+select substring('a' from '((a)+)');