diff --git a/ChangeLog b/ChangeLog index a84f6eb..e455569 100644 --- a/ChangeLog +++ b/ChangeLog @@ -63,6 +63,10 @@ This bug was discovered by the LLVM fuzzer. it should have been signed. Some other "int" variables, having been checked, have either been changed to uint32_t or commented as "must be signed". +16. A mutual recursion within a lookbehind assertion such as (?<=((?2))((?1))) +caused a stack overflow instead of the diagnosis of a non-fixed length +lookbehind assertion. This bug was discovered by the LLVM fuzzer. + Version 10.10 06-March-2015 --------------------------- diff --git a/src/pcre2_compile.c b/src/pcre2_compile.c index abe3484..89f53d6 100644 --- a/src/pcre2_compile.c +++ b/src/pcre2_compile.c @@ -75,8 +75,8 @@ static int const uint32_t *, unsigned int); static BOOL - compile_regex(uint32_t, PCRE2_UCHAR **, PCRE2_SPTR *, int *, BOOL, BOOL, - uint32_t, int, uint32_t *, int32_t *, uint32_t *, int32_t *, + compile_regex(uint32_t, PCRE2_UCHAR **, PCRE2_SPTR *, int *, BOOL, BOOL, + uint32_t, int, uint32_t *, int32_t *, uint32_t *, int32_t *, branch_chain *, compile_block *, size_t *); @@ -677,6 +677,15 @@ static const uint8_t opcode_possessify[] = { }; +/* Structure for checking for mutual recursion when scanning compiled code. */ + +typedef struct recurse_check { + struct recurse_check *prev; + PCRE2_SPTR group; +} recurse_check; + + + /************************************************* * Free compiled code * *************************************************/ @@ -785,6 +794,7 @@ Arguments: utf TRUE in UTF mode atend TRUE if called when the pattern is complete cb the "compile data" structure + recurses chain of recurse_check to catch mutual recursion Returns: the fixed length, or -1 if there is no fixed length, @@ -794,10 +804,11 @@ Returns: the fixed length, */ static int -find_fixedlength(PCRE2_UCHAR *code, BOOL utf, BOOL atend, compile_block *cb) +find_fixedlength(PCRE2_UCHAR *code, BOOL utf, BOOL atend, compile_block *cb, + recurse_check *recurses) { int length = -1; - +recurse_check this_recurse; register int branchlength = 0; register PCRE2_UCHAR *cc = code + 1 + LINK_SIZE; @@ -822,7 +833,8 @@ for (;;) case OP_ONCE: case OP_ONCE_NC: case OP_COND: - d = find_fixedlength(cc + ((op == OP_CBRA)? IMM2_SIZE : 0), utf, atend, cb); + d = find_fixedlength(cc + ((op == OP_CBRA)? IMM2_SIZE : 0), utf, atend, cb, + recurses); if (d < 0) return d; branchlength += d; do cc += GET(cc, 1); while (*cc == OP_ALT); @@ -853,10 +865,18 @@ for (;;) case OP_RECURSE: if (!atend) return -3; - cs = ce = (PCRE2_UCHAR *)cb->start_code + GET(cc, 1); /* Start subpattern */ + cs = ce = (PCRE2_UCHAR *)cb->start_code + GET(cc, 1); /* Start subpattern */ do ce += GET(ce, 1); while (*ce == OP_ALT); /* End subpattern */ if (cc > cs && cc < ce) return -1; /* Recursion */ - d = find_fixedlength(cs + IMM2_SIZE, utf, atend, cb); + else /* Check for mutual recursion */ + { + recurse_check *r = recurses; + for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break; + if (r != NULL) return -1; /* Mutual recursion */ + } + this_recurse.prev = recurses; + this_recurse.group = cs; + d = find_fixedlength(cs + IMM2_SIZE, utf, atend, cb, &this_recurse); if (d < 0) return d; branchlength += d; cc += 1 + LINK_SIZE; @@ -1196,11 +1216,6 @@ Arguments: Returns: TRUE if what is matched could be empty */ -typedef struct recurse_check { - struct recurse_check *prev; - PCRE2_SPTR group; -} recurse_check; - static BOOL could_be_empty_branch(PCRE2_SPTR code, PCRE2_SPTR endcode, BOOL utf, compile_block *cb, recurse_check *recurses) @@ -7037,7 +7052,7 @@ for (;;) int fixed_length; *code = OP_END; fixed_length = find_fixedlength(last_branch, (options & PCRE2_UTF) != 0, - FALSE, cb); + FALSE, cb, NULL); if (fixed_length == -3) { cb->check_lookbehind = TRUE; @@ -8075,7 +8090,7 @@ if (errorcode == 0 && cb.check_lookbehind) PCRE2_UCHAR *be = cc - 1 - LINK_SIZE + GET(cc, -LINK_SIZE); int end_op = *be; *be = OP_END; - fixed_length = find_fixedlength(cc, utf, TRUE, &cb); + fixed_length = find_fixedlength(cc, utf, TRUE, &cb, NULL); *be = end_op; if (fixed_length < 0) { diff --git a/testdata/testinput2 b/testdata/testinput2 index eaa445c..ee83ba2 100644 --- a/testdata/testinput2 +++ b/testdata/testinput2 @@ -4253,4 +4253,6 @@ a random value. /Ix /(?<=\bABQ(3(?+7)))/ +";(?<=()((?3))((?2)))" + # End of testinput2 diff --git a/testdata/testoutput2 b/testdata/testoutput2 index 80bdadf..f8a103e 100644 --- a/testdata/testoutput2 +++ b/testdata/testoutput2 @@ -14257,4 +14257,7 @@ Failed: error 115 at offset 15: reference to non-existent subpattern /(?<=\bABQ(3(?+7)))/ Failed: error 115 at offset 15: reference to non-existent subpattern +";(?<=()((?3))((?2)))" +Failed: error 125 at offset 20: lookbehind assertion is not fixed length + # End of testinput2