[perl #84746] Accessing $2 causes the interpreter to crash
authorFather Chrysostomos <sprout@cpan.org>
Sat, 26 Feb 2011 04:45:08 +0000 (20:45 -0800)
committerFather Chrysostomos <sprout@cpan.org>
Sat, 26 Feb 2011 04:45:37 +0000 (20:45 -0800)
Actually, it doesn’t. The original test case was:

#!/usr/bin/perl

my $rx = qr'\$ (?| {(.+?)} | (.+?); | (.+?)(\s) )'x;
my $test = '/home/$USERNAME ';
die unless $test =~ $rx;
print "1: $1\n";
print "2: $2\n" if defined $2;

This crashes even if I put an ‘exit’ right after the pattern match.

What’s happening is that regcomp miscounts the number of capturing
parenthesis pairs (cf. [perl #59734]), so the execution of the regular
expression causes a buffer overflow which overwrites the op_sibling
field of the regcreset op, causing a crash when the op is freed. (The
exact failure may differ between builds, platforms, etc., of course.)

S_reg in regcomp.c keeps a count of the parenthesised groups in a
(?|...) construct, which it updates after each branch, if that branch
has more captures than any previous branch. But it was not updating
the count after the last branch.

So this bug would occur if the last branch had more capturing paren-
theses than any previous branch.

Commit ee91d26, which fixed bug #59734, only solved the problem when
there was just one branch (by updating the count before the loop that
deals with subsequent branches was entered).

This commit changes the code at the end of S_reg to take into account
that RExC_npar (the current paren count) might have been increased by
the last branch.

Since the loop to deal with subsequent branches resets the count
*before* each branch, the code that commit ee91d26 added is no longer
necessary, so this commit removes it.

regcomp.c
t/re/re_tests

index 96a825c..932da1a 100644 (file)
--- a/regcomp.c
+++ b/regcomp.c
@@ -7097,12 +7097,6 @@ S_reg(pTHX_ RExC_state_t *pRExC_state, I32 paren, I32 *flagp,U32 depth)
     parse_start = RExC_parse;   /* MJD */
     br = regbranch(pRExC_state, &flags, 1,depth+1);
 
-    if (freeze_paren) {
-        if (RExC_npar > after_freeze)
-            after_freeze = RExC_npar;
-        RExC_npar = freeze_paren;
-    }
-
     /*     branch_len = (paren != 0); */
 
     if (br == NULL)
@@ -7246,7 +7240,7 @@ S_reg(pTHX_ RExC_state_t *pRExC_state, I32 paren, I32 *flagp,U32 depth)
     if (RExC_in_lookbehind) {
        RExC_in_lookbehind--;
     }
-    if (after_freeze)
+    if (after_freeze > RExC_npar)
         RExC_npar = after_freeze;
     return(ret);
 }
index 5441437..924434c 100644 (file)
@@ -1329,6 +1329,9 @@ X(\w+)(?=\s)|X(\w+)       Xab     y       [$1-$2] [-ab]
 (?|(?<foo>x)|(?<bar>y))        x       yM      $+{foo} x       miniperl cannot load Tie::Hash::NamedCapture
 (?|(?<bar>y)|(?<foo>x))        x       yM      $+{foo} x       miniperl cannot load Tie::Hash::NamedCapture
 (?<bar>)(?|(?<foo>x))  x       yM      $+{foo} x       miniperl cannot load Tie::Hash::NamedCapture
+# Used to crash, because the last branch was ignored when the parens
+# were counted:
+(?|(b)|()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()(a))    a       y       $&      a
 
 #Bug #41492
 (?(DEFINE)(?<A>(?&B)+)(?<B>a))(?&A)    a       y       $&      a