[Pcre-svn] [642] code/trunk: Avoid false positive for infini…

トップ ページ
このメッセージを削除
著者: Subversion repository
日付:  
To: pcre-svn
題目: [Pcre-svn] [642] code/trunk: Avoid false positive for infinite recursion by not checking conditionals at
Revision: 642
          http://vcs.pcre.org/viewvc?view=rev&revision=642
Author:   ph10
Date:     2011-07-28 19:59:40 +0100 (Thu, 28 Jul 2011)


Log Message:
-----------
Avoid false positive for infinite recursion by not checking conditionals at
compile time, but add tests at runtime that also catch infinite mutual
recursion.

Modified Paths:
--------------
    code/trunk/ChangeLog
    code/trunk/doc/pcreapi.3
    code/trunk/pcre.h.in
    code/trunk/pcre_compile.c
    code/trunk/pcre_dfa_exec.c
    code/trunk/pcre_exec.c
    code/trunk/pcre_internal.h
    code/trunk/pcretest.c
    code/trunk/testdata/testinput11
    code/trunk/testdata/testinput2
    code/trunk/testdata/testinput7
    code/trunk/testdata/testoutput11
    code/trunk/testdata/testoutput2
    code/trunk/testdata/testoutput7


Modified: code/trunk/ChangeLog
===================================================================
--- code/trunk/ChangeLog    2011-07-25 16:56:54 UTC (rev 641)
+++ code/trunk/ChangeLog    2011-07-28 18:59:40 UTC (rev 642)
@@ -195,7 +195,21 @@


38. Add minix to OS list not supporting the -S option in pcretest.

+39. PCRE tries to detect cases of infinite recursion at compile time, but it
+    cannot analyze patterns in sufficient detail to catch mutual recursions
+    such as ((?1))((?2)). There is now a runtime test that gives an error if a 
+    subgroup is called recursively as a subpattern for a second time at the 
+    same position in the subject string. In previous releases this might have
+    been caught by the recursion limit, or it might have run out of stack. 
+    
+40. A pattern such as /(?(R)a+|(?R)b)/ is quite safe, as the recursion can
+    happen only once. PCRE was, however incorrectly giving a compile time error 
+    "recursive call could loop indefinitely" because it cannot analyze the 
+    pattern in sufficient detail. The compile time test no longer happens when 
+    PCRE is compiling a conditional subpattern, but actual runaway loops are 
+    now caught at runtime (see 39 above).   


+
Version 8.12 15-Jan-2011
------------------------


Modified: code/trunk/doc/pcreapi.3
===================================================================
--- code/trunk/doc/pcreapi.3    2011-07-25 16:56:54 UTC (rev 641)
+++ code/trunk/doc/pcreapi.3    2011-07-28 18:59:40 UTC (rev 642)
@@ -1862,6 +1862,16 @@
 fact sufficient to detect this case, but this special error code for
 PCRE_PARTIAL_HARD precedes the implementation of returned information; it is
 retained for backwards compatibility.
+.sp
+  PCRE_ERROR_RECURSELOOP    (-26)
+.sp
+This error is returned when \fBpcre_exec()\fP detects a recursion loop within 
+the pattern. Specifically, it means that either the whole pattern or a 
+subpattern has been called recursively for the second time at the same position 
+in the subject string. Some simple patterns that might do this are detected and
+faulted at compile time, but more complicated cases, in particular mutual
+recursions between two different subpatterns, cannot be detected until run
+time.
 .P
 Error numbers -16 to -20 and -22 are not used by \fBpcre_exec()\fP.
 .
@@ -2369,6 +2379,6 @@
 .rs
 .sp
 .nf
-Last updated: 07 May 2011
+Last updated: 28 July 2011
 Copyright (c) 1997-2011 University of Cambridge.
 .fi


Modified: code/trunk/pcre.h.in
===================================================================
--- code/trunk/pcre.h.in    2011-07-25 16:56:54 UTC (rev 641)
+++ code/trunk/pcre.h.in    2011-07-28 18:59:40 UTC (rev 642)
@@ -163,6 +163,7 @@
 #define PCRE_ERROR_BADNEWLINE     (-23)
 #define PCRE_ERROR_BADOFFSET      (-24)
 #define PCRE_ERROR_SHORTUTF8      (-25)
+#define PCRE_ERROR_RECURSELOOP    (-26)


/* Specific error codes for UTF-8 validity checks */


Modified: code/trunk/pcre_compile.c
===================================================================
--- code/trunk/pcre_compile.c    2011-07-25 16:56:54 UTC (rev 641)
+++ code/trunk/pcre_compile.c    2011-07-28 18:59:40 UTC (rev 642)
@@ -546,8 +546,8 @@
 /* Definition to allow mutual recursion */


 static BOOL
-  compile_regex(int, uschar **, const uschar **, int *, BOOL, BOOL, int, int *,
-    int *, branch_chain *, compile_data *, int *);
+  compile_regex(int, uschar **, const uschar **, int *, BOOL, BOOL, int, int,
+    int *, int *, branch_chain *, compile_data *, int *);




@@ -3035,6 +3035,7 @@
   firstbyteptr   set to initial literal character, or < 0 (REQ_UNSET, REQ_NONE)
   reqbyteptr     set to the last literal character required, else < 0
   bcptr          points to current branch chain
+  cond_depth     conditional nesting depth 
   cd             contains pointers to tables etc.
   lengthptr      NULL during the real compile phase
                  points to length accumulator during pre-compile phase
@@ -3046,7 +3047,7 @@
 static BOOL
 compile_branch(int *optionsptr, uschar **codeptr, const uschar **ptrptr,
   int *errorcodeptr, int *firstbyteptr, int *reqbyteptr, branch_chain *bcptr,
-  compile_data *cd, int *lengthptr)
+  int cond_depth, compile_data *cd, int *lengthptr)
 {
 int repeat_type, op_type;
 int repeat_min = 0, repeat_max = 0;      /* To please picky compilers */
@@ -5759,9 +5760,14 @@


             /* If not a forward reference, and the subpattern is still open,
             this is a recursive call. We check to see if this is a left
-            recursion that could loop for ever, and diagnose that case. */
+            recursion that could loop for ever, and diagnose that case. We
+            must not, however, do this check if we are in a conditional 
+            subpattern because the condition might be testing for recursion in
+            a pattern such as /(?(R)a+|(?R)b)/, which is perfectly valid. 
+            Forever loops are also detected at runtime, so those that occur in 
+            conditional subpatterns will be picked up then. */


-            else if (GET(called, 1) == 0 &&
+            else if (GET(called, 1) == 0 && cond_depth <= 0 &&
                      could_be_empty(called, code, bcptr, utf8, cd))
               {
               *errorcodeptr = ERR40;
@@ -5893,27 +5899,29 @@
     repeated. We copy code into a non-register variable (tempcode) in order to
     be able to pass its address because some compilers complain otherwise. */


-    previous = code;                   /* For handling repetition */
-    *code = bravalue;
-    tempcode = code;
-    tempreqvary = cd->req_varyopt;     /* Save value before bracket */
-    length_prevgroup = 0;              /* Initialize for pre-compile phase */
-
-    if (!compile_regex(
-         newoptions,                   /* The complete new option state */
-         &tempcode,                    /* Where to put code (updated) */
-         &ptr,                         /* Input pointer (updated) */
-         errorcodeptr,                 /* Where to put an error message */
+    previous = code;                      /* For handling repetition */
+    *code = bravalue;                     
+    tempcode = code;                      
+    tempreqvary = cd->req_varyopt;        /* Save value before bracket */
+    length_prevgroup = 0;                 /* Initialize for pre-compile phase */
+                                          
+    if (!compile_regex(                   
+         newoptions,                      /* The complete new option state */
+         &tempcode,                       /* Where to put code (updated) */
+         &ptr,                            /* Input pointer (updated) */
+         errorcodeptr,                    /* Where to put an error message */
          (bravalue == OP_ASSERTBACK ||
           bravalue == OP_ASSERTBACK_NOT), /* TRUE if back assert */
-         reset_bracount,               /* True if (?| group */
-         skipbytes,                    /* Skip over bracket number */
-         &subfirstbyte,                /* For possible first char */
-         &subreqbyte,                  /* For possible last char */
-         bcptr,                        /* Current branch chain */
-         cd,                           /* Tables block */
-         (lengthptr == NULL)? NULL :   /* Actual compile phase */
-           &length_prevgroup           /* Pre-compile phase */
+         reset_bracount,                  /* True if (?| group */
+         skipbytes,                       /* Skip over bracket number */
+         cond_depth + 
+           ((bravalue == OP_COND)?1:0),   /* Depth of condition subpatterns */
+         &subfirstbyte,                   /* For possible first char */
+         &subreqbyte,                     /* For possible last char */
+         bcptr,                           /* Current branch chain */
+         cd,                              /* Tables block */
+         (lengthptr == NULL)? NULL :      /* Actual compile phase */
+           &length_prevgroup              /* Pre-compile phase */
          ))
       goto FAILED;


@@ -6371,6 +6379,7 @@
   lookbehind     TRUE if this is a lookbehind assertion
   reset_bracount TRUE to reset the count for each branch
   skipbytes      skip this many bytes at start (for brackets and OP_COND)
+  cond_depth     depth of nesting for conditional subpatterns 
   firstbyteptr   place to put the first required character, or a negative number
   reqbyteptr     place to put the last required character, or a negative number
   bcptr          pointer to the chain of currently open branches
@@ -6384,8 +6393,8 @@
 static BOOL
 compile_regex(int options, uschar **codeptr, const uschar **ptrptr,
   int *errorcodeptr, BOOL lookbehind, BOOL reset_bracount, int skipbytes,
-  int *firstbyteptr, int *reqbyteptr, branch_chain *bcptr, compile_data *cd,
-  int *lengthptr)
+  int cond_depth, int *firstbyteptr, int *reqbyteptr, branch_chain *bcptr, 
+  compile_data *cd, int *lengthptr)
 {
 const uschar *ptr = *ptrptr;
 uschar *code = *codeptr;
@@ -6464,7 +6473,8 @@
   into the length. */


   if (!compile_branch(&options, &code, &ptr, errorcodeptr, &branchfirstbyte,
-        &branchreqbyte, &bc, cd, (lengthptr == NULL)? NULL : &length))
+        &branchreqbyte, &bc, cond_depth, cd, 
+        (lengthptr == NULL)? NULL : &length))
     {
     *ptrptr = ptr;
     return FALSE;
@@ -7190,7 +7200,7 @@
 code = cworkspace;
 *code = OP_BRA;
 (void)compile_regex(cd->external_options, &code, &ptr, &errorcode, FALSE, 
-  FALSE, 0, &firstbyte, &reqbyte, NULL, cd, &length);
+  FALSE, 0, 0, &firstbyte, &reqbyte, NULL, cd, &length);
 if (errorcode != 0) goto PCRE_EARLY_ERROR_RETURN;


DPRINTF(("end pre-compile: length=%d workspace=%d\n", length,
@@ -7263,7 +7273,7 @@
ptr = (const uschar *)pattern + skipatstart;
code = (uschar *)codestart;
*code = OP_BRA;
-(void)compile_regex(re->options, &code, &ptr, &errorcode, FALSE, FALSE, 0,
+(void)compile_regex(re->options, &code, &ptr, &errorcode, FALSE, FALSE, 0, 0,
&firstbyte, &reqbyte, NULL, cd, NULL);
re->top_bracket = cd->bracount;
re->top_backref = cd->top_backref;

Modified: code/trunk/pcre_dfa_exec.c
===================================================================
--- code/trunk/pcre_dfa_exec.c    2011-07-25 16:56:54 UTC (rev 641)
+++ code/trunk/pcre_dfa_exec.c    2011-07-28 18:59:40 UTC (rev 642)
@@ -328,7 +328,6 @@
   workspace         vector of workspace
   wscount           size of same
   rlevel            function call recursion level
-  recursing         regex recursive call level


 Returns:            > 0 => number of match offset pairs placed in offsets
                     = 0 => offsets overflowed; longest matches are present
@@ -392,8 +391,7 @@
   int offsetcount,
   int *workspace,
   int wscount,
-  int  rlevel,
-  int  recursing)
+  int  rlevel)
 {
 stateblock *active_states, *new_states, *temp_states;
 stateblock *next_active_state, *next_new_state;
@@ -402,6 +400,8 @@
 const uschar *ptr;
 const uschar *end_code, *first_op;


+dfa_recursion_info new_recursive;
+
int active_count, new_count, match_count;

 /* Some fields in the md block are frequently referenced, so we load them into
@@ -425,8 +425,8 @@
           (2 * INTS_PER_STATEBLOCK);


DPRINTF(("\n%.*s---------------------\n"
- "%.*sCall to internal_dfa_exec f=%d r=%d\n",
- rlevel*2-2, SP, rlevel*2-2, SP, rlevel, recursing));
+ "%.*sCall to internal_dfa_exec f=%d\n",
+ rlevel*2-2, SP, rlevel*2-2, SP, rlevel));

 ctypes = md->tables + ctypes_offset;
 lcc = md->tables + lcc_offset;
@@ -2521,8 +2521,7 @@
           sizeof(local_offsets)/sizeof(int),    /* size of same */
           local_workspace,                      /* workspace vector */
           sizeof(local_workspace)/sizeof(int),  /* size of same */
-          rlevel,                               /* function recursion level */
-          recursing);                           /* pass on regex recursion */
+          rlevel);                              /* function recursion level */


         if (rc == PCRE_ERROR_DFA_UITEM) return rc;
         if ((rc >= 0) == (codevalue == OP_ASSERT || codevalue == OP_ASSERTBACK))
@@ -2587,7 +2586,7 @@
           {
           int value = GET2(code, LINK_SIZE+2);
           if (value != RREF_ANY) return PCRE_ERROR_DFA_UCOND;
-          if (recursing > 0)
+          if (md->recursive != NULL) 
             { ADD_ACTIVE(state_offset + LINK_SIZE + 4, 0); }
           else { ADD_ACTIVE(state_offset + codelink + LINK_SIZE + 1, 0); }
           }
@@ -2611,8 +2610,7 @@
             sizeof(local_offsets)/sizeof(int),    /* size of same */
             local_workspace,                      /* workspace vector */
             sizeof(local_workspace)/sizeof(int),  /* size of same */
-            rlevel,                               /* function recursion level */
-            recursing);                           /* pass on regex recursion */
+            rlevel);                              /* function recursion level */


           if (rc == PCRE_ERROR_DFA_UITEM) return rc;
           if ((rc >= 0) ==
@@ -2627,28 +2625,48 @@
       /*-----------------------------------------------------------------*/
       case OP_RECURSE:
         {
+        dfa_recursion_info *ri; 
         int local_offsets[1000];
         int local_workspace[1000];
+        const uschar *callpat = start_code + GET(code, 1);
+        int recno = (callpat == md->start_code)? 0 :                            
+          GET2(callpat, 1 + LINK_SIZE);   
         int rc;


-        DPRINTF(("%.*sStarting regex recursion %d\n", rlevel*2-2, SP,
-          recursing + 1));
+        DPRINTF(("%.*sStarting regex recursion\n", rlevel*2-2, SP));
+        
+        /* Check for repeating a recursion without advancing the subject
+        pointer. This should catch convoluted mutual recursions. (Some simple
+        cases are caught at compile time.) */
+        
+        for (ri = md->recursive; ri != NULL; ri = ri->prevrec)           
+          if (recno == ri->group_num && ptr == ri->subject_position)      
+            return PCRE_ERROR_RECURSELOOP;     


+        /* Remember this recursion and where we started it so as to 
+        catch infinite loops. */
+         
+        new_recursive.group_num = recno;
+        new_recursive.subject_position = ptr;
+        new_recursive.prevrec = md->recursive;
+        md->recursive = &new_recursive;   
+
         rc = internal_dfa_exec(
           md,                                   /* fixed match data */
-          start_code + GET(code, 1),            /* this subexpression's code */
+          callpat,                              /* this subexpression's code */
           ptr,                                  /* where we currently are */
           (int)(ptr - start_subject),           /* start offset */
           local_offsets,                        /* offset vector */
           sizeof(local_offsets)/sizeof(int),    /* size of same */
           local_workspace,                      /* workspace vector */
           sizeof(local_workspace)/sizeof(int),  /* size of same */
-          rlevel,                               /* function recursion level */
-          recursing + 1);                       /* regex recurse level */
+          rlevel);                              /* function recursion level */


-        DPRINTF(("%.*sReturn from regex recursion %d: rc=%d\n", rlevel*2-2, SP,
-          recursing + 1, rc));
+        md->recursive = new_recursive.prevrec;  /* Done this recursion */


+        DPRINTF(("%.*sReturn from regex recursion: rc=%d\n", rlevel*2-2, SP, 
+          rc));
+
         /* Ran out of internal offsets */


         if (rc == 0) return PCRE_ERROR_DFA_RECURSE;
@@ -2714,8 +2732,7 @@
             sizeof(local_offsets)/sizeof(int),    /* size of same */
             local_workspace,                      /* workspace vector */
             sizeof(local_workspace)/sizeof(int),  /* size of same */
-            rlevel,                               /* function recursion level */
-            recursing);                           /* pass on regex recursion */
+            rlevel);                              /* function recursion level */


           /* Failed to match */


@@ -2784,8 +2801,7 @@
           sizeof(local_offsets)/sizeof(int),    /* size of same */
           local_workspace,                      /* workspace vector */
           sizeof(local_workspace)/sizeof(int),  /* size of same */
-          rlevel,                               /* function recursion level */
-          recursing);                           /* pass on regex recursion */
+          rlevel);                              /* function recursion level */


         if (rc >= 0)
           {
@@ -3377,6 +3393,7 @@
   /* OK, now we can do the business */


md->start_used_ptr = current_subject;
+ md->recursive = NULL;

   rc = internal_dfa_exec(
     md,                                /* fixed match data */
@@ -3387,8 +3404,7 @@
     offsetcount,                       /* size of same */
     workspace,                         /* workspace vector */
     wscount,                           /* size of same */
-    0,                                 /* function recurse level */
-    0);                                /* regex recurse level */
+    0);                                /* function recurse level */


/* Anything other than "no match" means we are done, always; otherwise, carry
on only if not anchored. */

Modified: code/trunk/pcre_exec.c
===================================================================
--- code/trunk/pcre_exec.c    2011-07-25 16:56:54 UTC (rev 641)
+++ code/trunk/pcre_exec.c    2011-07-28 18:59:40 UTC (rev 642)
@@ -1501,12 +1501,25 @@


     case OP_RECURSE:
       {
+      recursion_info *ri;
+      int recno;
+        
       callpat = md->start_code + GET(ecode, 1);
-      new_recursive.group_num = (callpat == md->start_code)? 0 :
-        GET2(callpat, 1 + LINK_SIZE);
+      recno = (callpat == md->start_code)? 0 :
+        GET2(callpat, 1 + LINK_SIZE);              
+      
+      /* Check for repeating a recursion without advancing the subject pointer. 
+      This should catch convoluted mutual recursions. (Some simple cases are
+      caught at compile time.) */  
+       
+      for (ri = md->recursive; ri != NULL; ri = ri->prevrec)
+        if (recno == ri->group_num && eptr == ri->subject_position) 
+          RRETURN(PCRE_ERROR_RECURSELOOP);


       /* Add to "recursing stack" */


+      new_recursive.group_num = recno;
+      new_recursive.subject_position = eptr;
       new_recursive.prevrec = md->recursive;
       md->recursive = &new_recursive;



Modified: code/trunk/pcre_internal.h
===================================================================
--- code/trunk/pcre_internal.h    2011-07-25 16:56:54 UTC (rev 641)
+++ code/trunk/pcre_internal.h    2011-07-28 18:59:40 UTC (rev 642)
@@ -7,7 +7,7 @@
 and semantics are as close as possible to those of the Perl 5 language.


                        Written by Philip Hazel
-           Copyright (c) 1997-2010 University of Cambridge
+           Copyright (c) 1997-2011 University of Cambridge


-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -1753,7 +1753,7 @@
} compile_data;

/* Structure for maintaining a chain of pointers to the currently incomplete
-branches, for testing for left recursion. */
+branches, for testing for left recursion while compiling. */

typedef struct branch_chain {
struct branch_chain *outer;
@@ -1761,18 +1761,28 @@
} branch_chain;

/* Structure for items in a linked list that represents an explicit recursive
-call within the pattern. */
+call within the pattern; used by pcre_exec(). */

 typedef struct recursion_info {
   struct recursion_info *prevrec; /* Previous recursion record (or NULL) */
   int group_num;                  /* Number of group that was called */
   int *offset_save;               /* Pointer to start of saved offsets */
   int saved_max;                  /* Number of saved offsets */
+  USPTR subject_position;         /* Position at start of recursion */ 
 } recursion_info;


+/* A similar structure for pcre_dfa_exec(). */
+
+typedef struct dfa_recursion_info {
+ struct dfa_recursion_info *prevrec;
+ int group_num;
+ USPTR subject_position;
+} dfa_recursion_info;
+
/* Structure for building a chain of data for holding the values of the subject
pointer at the start of each subpattern, so as to detect when an empty string
-has been matched by a subpattern - to break infinite loops. */
+has been matched by a subpattern - to break infinite loops; used by
+pcre_exec(). */

typedef struct eptrblock {
struct eptrblock *epb_prev;
@@ -1832,18 +1842,19 @@
functions. */

 typedef struct dfa_match_data {
-  const uschar *start_code;     /* Start of the compiled pattern */
-  const uschar *start_subject;  /* Start of the subject string */
-  const uschar *end_subject;    /* End of subject string */
-  const uschar *start_used_ptr; /* Earliest consulted character */
-  const uschar *tables;         /* Character tables */
-  int   start_offset;           /* The start offset value */
-  int   moptions;               /* Match options */
-  int   poptions;               /* Pattern options */
-  int    nltype;                /* Newline type */
-  int    nllen;                 /* Newline string length */
-  uschar nl[4];                 /* Newline string when fixed */
-  void  *callout_data;          /* To pass back to callouts */
+  const uschar *start_code;      /* Start of the compiled pattern */
+  const uschar *start_subject;   /* Start of the subject string */
+  const uschar *end_subject;     /* End of subject string */
+  const uschar *start_used_ptr;  /* Earliest consulted character */
+  const uschar *tables;          /* Character tables */
+  int   start_offset;            /* The start offset value */
+  int   moptions;                /* Match options */
+  int   poptions;                /* Pattern options */
+  int    nltype;                 /* Newline type */
+  int    nllen;                  /* Newline string length */
+  uschar nl[4];                  /* Newline string when fixed */
+  void  *callout_data;           /* To pass back to callouts */
+  dfa_recursion_info *recursive; /* Linked list of recursion data */ 
 } dfa_match_data;


/* Bit definitions for entries in the pcre_ctypes table. */

Modified: code/trunk/pcretest.c
===================================================================
--- code/trunk/pcretest.c    2011-07-25 16:56:54 UTC (rev 641)
+++ code/trunk/pcretest.c    2011-07-28 18:59:40 UTC (rev 642)
@@ -225,7 +225,8 @@
   "not used - internal error",
   "invalid combination of newline options",
   "bad offset value",
-  NULL  /* SHORTUTF8 is handled specially */
+  NULL,  /* SHORTUTF8 is handled specially */
+  "nested recursion at the same subject position"
 };




Modified: code/trunk/testdata/testinput11
===================================================================
--- code/trunk/testdata/testinput11    2011-07-25 16:56:54 UTC (rev 641)
+++ code/trunk/testdata/testinput11    2011-07-28 18:59:40 UTC (rev 642)
@@ -654,4 +654,16 @@
 /^\N{1,}/
     abc\ndef 


+/(?(R)a+|(?R)b)/
+    aaaabcde
+
+/(?(R)a+|((?R))b)/
+    aaaabcde
+
+/((?(R)a+|(?1)b))/
+    aaaabcde
+
+/((?(R1)a+|(?1)b))/
+    aaaabcde
+
 /-- End of testinput11 --/


Modified: code/trunk/testdata/testinput2
===================================================================
--- code/trunk/testdata/testinput2    2011-07-25 16:56:54 UTC (rev 641)
+++ code/trunk/testdata/testinput2    2011-07-28 18:59:40 UTC (rev 642)
@@ -3818,4 +3818,15 @@


/[:a[:abc]b:]/

+/((?2))((?1))/
+    abc
+
+/((?(R2)a+|(?1)b))/
+    aaaabcde
+
+/(?(R)a*(?1)|((?R))b)/
+    aaaabcde
+
+/(a+|(?R)b)/
+
 /-- End of testinput2 --/


Modified: code/trunk/testdata/testinput7
===================================================================
--- code/trunk/testdata/testinput7    2011-07-25 16:56:54 UTC (rev 641)
+++ code/trunk/testdata/testinput7    2011-07-28 18:59:40 UTC (rev 642)
@@ -4681,4 +4681,22 @@
     abcxyz
     pqrxyz 


+/((?2))((?1))/
+    abc
+
+/(?(R)a+|(?R)b)/
+    aaaabcde
+
+/(?(R)a+|((?R))b)/
+    aaaabcde
+
+/((?(R)a+|(?1)b))/
+    aaaabcde
+
+/((?(R2)a+|(?1)b))/
+    aaaabcde
+
+/(?(R)a*(?1)|((?R))b)/
+    aaaabcde
+
 /-- End of testinput7 --/


Modified: code/trunk/testdata/testoutput11
===================================================================
--- code/trunk/testdata/testoutput11    2011-07-25 16:56:54 UTC (rev 641)
+++ code/trunk/testdata/testoutput11    2011-07-28 18:59:40 UTC (rev 642)
@@ -1233,4 +1233,23 @@
     abc\ndef 
  0: abc


+/(?(R)a+|(?R)b)/
+    aaaabcde
+ 0: aaaab
+
+/(?(R)a+|((?R))b)/
+    aaaabcde
+ 0: aaaab
+ 1: aaaa
+
+/((?(R)a+|(?1)b))/
+    aaaabcde
+ 0: aaaab
+ 1: aaaab
+
+/((?(R1)a+|(?1)b))/
+    aaaabcde
+ 0: aaaab
+ 1: aaaab
+
 /-- End of testinput11 --/


Modified: code/trunk/testdata/testoutput2
===================================================================
--- code/trunk/testdata/testoutput2    2011-07-25 16:56:54 UTC (rev 641)
+++ code/trunk/testdata/testoutput2    2011-07-28 18:59:40 UTC (rev 642)
@@ -12139,4 +12139,19 @@
 /[:a[:abc]b:]/
 Failed: unknown POSIX class name at offset 5


+/((?2))((?1))/
+    abc
+Error -26 (nested recursion at the same subject position)
+
+/((?(R2)a+|(?1)b))/
+    aaaabcde
+Error -26 (nested recursion at the same subject position)
+
+/(?(R)a*(?1)|((?R))b)/
+    aaaabcde
+Error -26 (nested recursion at the same subject position)
+
+/(a+|(?R)b)/
+Failed: recursive call could loop indefinitely at offset 7
+
 /-- End of testinput2 --/


Modified: code/trunk/testdata/testoutput7
===================================================================
--- code/trunk/testdata/testoutput7    2011-07-25 16:56:54 UTC (rev 641)
+++ code/trunk/testdata/testoutput7    2011-07-28 18:59:40 UTC (rev 642)
@@ -7822,4 +7822,28 @@
     pqrxyz 
  0: xyz


+/((?2))((?1))/
+    abc
+Error -26 (nested recursion at the same subject position)
+
+/(?(R)a+|(?R)b)/
+    aaaabcde
+ 0: aaaab
+
+/(?(R)a+|((?R))b)/
+    aaaabcde
+ 0: aaaab
+
+/((?(R)a+|(?1)b))/
+    aaaabcde
+ 0: aaaab
+
+/((?(R2)a+|(?1)b))/
+    aaaabcde
+Error -17 (backreference condition or recursion test not supported for DFA matching)
+
+/(?(R)a*(?1)|((?R))b)/
+    aaaabcde
+Error -26 (nested recursion at the same subject position)
+
 /-- End of testinput7 --/