Repository: fancy-regex/fancy-regex (language: rust)

Issue:
Fuzzing results in index out of bounds
#### Description
Hi, Running fuzz_parser results in following error.
`thread '<unnamed>' panicked at 'index out of bounds: the len is 5 but the index is 6', /home/maxx/dev/security/oss-fuzz-projects/fancy-regex/src/parse.rs:774:19`

#### Steps to reproduce
```rust
#[test]
fn fuzz_3() {
    p(r"(?()^");
}
```

```sh
❯ cargo test parse::tests::fuzz_3
```

```
Compiling fancy-regex v0.12.0 (/home/maxx/dev/security/oss-fuzz-projects/fancy-regex)
Finished test [unoptimized + debuginfo] target(s) in 2.78s
Running unittests src/lib.rs (target/debug/deps/fancy_regex-fcae9c14f84f520b)

running 1 test
test parse::tests::fuzz_3 ... FAILED

failures:

---- parse::tests::fuzz_3 stdout ----
thread 'parse::tests::fuzz_3' panicked at 'index out of bounds: the len is 5 but the index is 6', src/parse.rs:774:19
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    parse::tests::fuzz_3

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 73 filtered out; finished in 0.00s

error: test failed, to rerun pass `--lib`

```



Discussion / hints:
I have also come across 2 such instances 

The first occurs when using control characters eg: 
```
let regex = Regex::try_from(r#"  (FF\0\0\0\0\0\0()!\u{f}t\t(?(\n{),:?\u{f}\n                "#);
```

I believe this second example is due to an incomplete capturing group. 

```
let regex = Regex::try_from(r#"!w(?()\"Kuz>"#);
```

Fix patch:
diff --git a/src/parse.rs b/src/parse.rs
index 31b868d..74c10ea 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -743,7 +743,8 @@ impl<'a> Parser<'a> {
         if end == next {
             // Backreference validity checker
             if let Expr::Backref(group) = condition {
-                return Ok((end + 1, Expr::BackrefExistsCondition(group)));
+                let after = self.check_for_close_paren(end)?;
+                return Ok((after, Expr::BackrefExistsCondition(group)));
             } else {
                 return Err(Error::ParseError(
                     end,
@@ -775,8 +776,9 @@ impl<'a> Parser<'a> {
             condition
         };
 
+        let after = self.check_for_close_paren(end)?;
         Ok((
-            end + 1,
+            after,
             if if_true == Expr::Empty && if_false == Expr::Empty {
                 inner_condition
             } else {
@@ -930,7 +932,11 @@ mod tests {
     #[cfg_attr(feature = "track_caller", track_caller)]
     fn assert_error(re: &str, expected_error: &str) {
         let result = Expr::parse_tree(re);
-        assert!(result.is_err());
+        assert!(
+            result.is_err(),
+            "Expected parse error, but was: {:?}",
+            result
+        );
         assert_eq!(&format!("{}", result.err().unwrap()), expected_error);
     }
 
@@ -1797,4 +1803,15 @@ mod tests {
     fn fuzz_2() {
         p(r"\pä");
     }
+
+    #[test]
+    fn fuzz_3() {
+        fail(r"(?()^");
+        fail(r#"!w(?()\"Kuz>"#);
+    }
+
+    #[test]
+    fn fuzz_4() {
+        fail(r"\u{2}(?(2)");
+    }
 }


Test patch: