diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 2b742f087..eabe9691d 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" cargo-fuzz = true [dependencies] -libfuzzer-sys = "0.4.1" +libfuzzer-sys = { version = "0.4.1", features = ["arbitrary-derive"] } regex = { path = ".." } regex-automata = { path = "../regex-automata" } regex-lite = { path = "../regex-lite" } diff --git a/fuzz/fuzz_targets/fuzz_regex_lite_match.rs b/fuzz/fuzz_targets/fuzz_regex_lite_match.rs index c4e61ccd7..41e822af9 100644 --- a/fuzz/fuzz_targets/fuzz_regex_lite_match.rs +++ b/fuzz/fuzz_targets/fuzz_regex_lite_match.rs @@ -1,23 +1,60 @@ #![no_main] -use libfuzzer_sys::fuzz_target; +use libfuzzer_sys::{arbitrary, fuzz_target}; -fuzz_target!(|data: &[u8]| { - let _ = run(data); -}); +#[derive(arbitrary::Arbitrary)] +struct FuzzCase<'a> { + pattern: &'a str, + haystack: &'a str, + case_insensitive: bool, + multi_line: bool, + crlf: bool, + dot_matches_new_line: bool, + swap_greed: bool, + ignore_whitespace: bool, +} + +impl std::fmt::Debug for FuzzCase<'_> { + fn fmt( + &self, + fmt: &mut std::fmt::Formatter, + ) -> Result<(), std::fmt::Error> { + let FuzzCase { + pattern, + case_insensitive, + multi_line, + crlf, + dot_matches_new_line, + swap_greed, + ignore_whitespace, + haystack, + } = self; -fn run(data: &[u8]) -> Option<()> { - if data.len() < 2 { - return None; + write!( + fmt, + r#" +let Ok(re) = regex_lite::RegexBuilder::new({pattern:?}) + .case_insensitive({case_insensitive:?}) + .multi_line({multi_line:?}) + .crlf({crlf:?}) + .dot_matches_new_line({dot_matches_new_line:?}) + .swap_greed({swap_greed:?}) + .ignore_whitespace({ignore_whitespace:?}) + .build() else {{ return }}; +re.is_match({haystack:?}); + "# + ) } - let mut split_at = usize::from(data[0]); - let data = std::str::from_utf8(&data[1..]).ok()?; - // Split data into a regex and haystack to search. - let len = usize::try_from(data.chars().count()).ok()?; - split_at = std::cmp::max(split_at, 1) % len; - let char_index = data.char_indices().nth(split_at)?.0; - let (pattern, input) = data.split_at(char_index); - let re = regex_lite::Regex::new(pattern).ok()?; - re.is_match(input); - Some(()) } + +fuzz_target!(|case: FuzzCase| { + let Ok(re) = regex_lite::RegexBuilder::new(case.pattern) + .case_insensitive(case.case_insensitive) + .multi_line(case.multi_line) + .crlf(case.crlf) + .dot_matches_new_line(case.dot_matches_new_line) + .swap_greed(case.swap_greed) + .ignore_whitespace(case.ignore_whitespace) + .build() else { return }; + re.is_match(case.haystack); +}); diff --git a/fuzz/fuzz_targets/fuzz_regex_match.rs b/fuzz/fuzz_targets/fuzz_regex_match.rs index 5e9333f46..a348edb94 100644 --- a/fuzz/fuzz_targets/fuzz_regex_match.rs +++ b/fuzz/fuzz_targets/fuzz_regex_match.rs @@ -1,23 +1,64 @@ #![no_main] -use libfuzzer_sys::fuzz_target; +use libfuzzer_sys::{arbitrary, fuzz_target}; -fuzz_target!(|data: &[u8]| { - let _ = run(data); -}); +#[derive(arbitrary::Arbitrary)] +struct FuzzCase<'a> { + pattern: &'a str, + haystack: &'a str, + case_insensitive: bool, + multi_line: bool, + dot_matches_new_line: bool, + swap_greed: bool, + ignore_whitespace: bool, + unicode: bool, + octal: bool, +} + +impl std::fmt::Debug for FuzzCase<'_> { + fn fmt( + &self, + fmt: &mut std::fmt::Formatter, + ) -> Result<(), std::fmt::Error> { + let FuzzCase { + pattern, + case_insensitive, + multi_line, + dot_matches_new_line, + swap_greed, + ignore_whitespace, + unicode, + octal, + haystack, + } = self; -fn run(data: &[u8]) -> Option<()> { - if data.len() < 2 { - return None; + write!( + fmt, + r#" +let Ok(re) = regex::RegexBuilder::new({pattern:?}) + .case_insensitive({case_insensitive:?}) + .multi_line({multi_line:?}) + .dot_matches_new_line({dot_matches_new_line:?}) + .swap_greed({swap_greed:?}) + .ignore_whitespace({ignore_whitespace:?}) + .unicode({unicode:?}) + .octal({octal:?}) + .build() else {{ return }}; +re.is_match({haystack:?}); + "# + ) } - let mut split_at = usize::from(data[0]); - let data = std::str::from_utf8(&data[1..]).ok()?; - // Split data into a regex and haystack to search. - let len = usize::try_from(data.chars().count()).ok()?; - split_at = std::cmp::max(split_at, 1) % len; - let char_index = data.char_indices().nth(split_at)?.0; - let (pattern, input) = data.split_at(char_index); - let re = regex::Regex::new(pattern).ok()?; - re.is_match(input); - Some(()) } + +fuzz_target!(|case: FuzzCase| { + let Ok(re) = regex::RegexBuilder::new(case.pattern) + .case_insensitive(case.case_insensitive) + .multi_line(case.multi_line) + .dot_matches_new_line(case.dot_matches_new_line) + .swap_greed(case.swap_greed) + .ignore_whitespace(case.ignore_whitespace) + .unicode(case.unicode) + .octal(case.octal) + .build() else { return }; + re.is_match(case.haystack); +});