Skip to content

Commit 01a8cf4

Browse files
5225225BurntSushi
authored andcommitted
fuzz: use structured fuzzer input
This makes a couple of the fuzzer targets a bit nicer by just asking for structured data instead of trying to manifest it ourselves out of a &[u8]. Closes #821
1 parent 5bec54a commit 01a8cf4

File tree

3 files changed

+113
-35
lines changed

3 files changed

+113
-35
lines changed

fuzz/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ edition = "2021"
99
cargo-fuzz = true
1010

1111
[dependencies]
12-
libfuzzer-sys = "0.4.1"
12+
libfuzzer-sys = { version = "0.4.1", features = ["arbitrary-derive"] }
1313
regex = { path = ".." }
1414
regex-automata = { path = "../regex-automata" }
1515
regex-lite = { path = "../regex-lite" }
+54-17
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,60 @@
11
#![no_main]
22

3-
use libfuzzer_sys::fuzz_target;
3+
use libfuzzer_sys::{arbitrary, fuzz_target};
44

5-
fuzz_target!(|data: &[u8]| {
6-
let _ = run(data);
7-
});
5+
#[derive(arbitrary::Arbitrary)]
6+
struct FuzzCase<'a> {
7+
pattern: &'a str,
8+
haystack: &'a str,
9+
case_insensitive: bool,
10+
multi_line: bool,
11+
crlf: bool,
12+
dot_matches_new_line: bool,
13+
swap_greed: bool,
14+
ignore_whitespace: bool,
15+
}
16+
17+
impl std::fmt::Debug for FuzzCase<'_> {
18+
fn fmt(
19+
&self,
20+
fmt: &mut std::fmt::Formatter,
21+
) -> Result<(), std::fmt::Error> {
22+
let FuzzCase {
23+
pattern,
24+
case_insensitive,
25+
multi_line,
26+
crlf,
27+
dot_matches_new_line,
28+
swap_greed,
29+
ignore_whitespace,
30+
haystack,
31+
} = self;
832

9-
fn run(data: &[u8]) -> Option<()> {
10-
if data.len() < 2 {
11-
return None;
33+
write!(
34+
fmt,
35+
r#"
36+
let Ok(re) = regex_lite::RegexBuilder::new({pattern:?})
37+
.case_insensitive({case_insensitive:?})
38+
.multi_line({multi_line:?})
39+
.crlf({crlf:?})
40+
.dot_matches_new_line({dot_matches_new_line:?})
41+
.swap_greed({swap_greed:?})
42+
.ignore_whitespace({ignore_whitespace:?})
43+
.build() else {{ return }};
44+
re.is_match({haystack:?});
45+
"#
46+
)
1247
}
13-
let mut split_at = usize::from(data[0]);
14-
let data = std::str::from_utf8(&data[1..]).ok()?;
15-
// Split data into a regex and haystack to search.
16-
let len = usize::try_from(data.chars().count()).ok()?;
17-
split_at = std::cmp::max(split_at, 1) % len;
18-
let char_index = data.char_indices().nth(split_at)?.0;
19-
let (pattern, input) = data.split_at(char_index);
20-
let re = regex_lite::Regex::new(pattern).ok()?;
21-
re.is_match(input);
22-
Some(())
2348
}
49+
50+
fuzz_target!(|case: FuzzCase| {
51+
let Ok(re) = regex_lite::RegexBuilder::new(case.pattern)
52+
.case_insensitive(case.case_insensitive)
53+
.multi_line(case.multi_line)
54+
.crlf(case.crlf)
55+
.dot_matches_new_line(case.dot_matches_new_line)
56+
.swap_greed(case.swap_greed)
57+
.ignore_whitespace(case.ignore_whitespace)
58+
.build() else { return };
59+
re.is_match(case.haystack);
60+
});

fuzz/fuzz_targets/fuzz_regex_match.rs

+58-17
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,64 @@
11
#![no_main]
22

3-
use libfuzzer_sys::fuzz_target;
3+
use libfuzzer_sys::{arbitrary, fuzz_target};
44

5-
fuzz_target!(|data: &[u8]| {
6-
let _ = run(data);
7-
});
5+
#[derive(arbitrary::Arbitrary)]
6+
struct FuzzCase<'a> {
7+
pattern: &'a str,
8+
haystack: &'a str,
9+
case_insensitive: bool,
10+
multi_line: bool,
11+
dot_matches_new_line: bool,
12+
swap_greed: bool,
13+
ignore_whitespace: bool,
14+
unicode: bool,
15+
octal: bool,
16+
}
17+
18+
impl std::fmt::Debug for FuzzCase<'_> {
19+
fn fmt(
20+
&self,
21+
fmt: &mut std::fmt::Formatter,
22+
) -> Result<(), std::fmt::Error> {
23+
let FuzzCase {
24+
pattern,
25+
case_insensitive,
26+
multi_line,
27+
dot_matches_new_line,
28+
swap_greed,
29+
ignore_whitespace,
30+
unicode,
31+
octal,
32+
haystack,
33+
} = self;
834

9-
fn run(data: &[u8]) -> Option<()> {
10-
if data.len() < 2 {
11-
return None;
35+
write!(
36+
fmt,
37+
r#"
38+
let Ok(re) = regex::RegexBuilder::new({pattern:?})
39+
.case_insensitive({case_insensitive:?})
40+
.multi_line({multi_line:?})
41+
.dot_matches_new_line({dot_matches_new_line:?})
42+
.swap_greed({swap_greed:?})
43+
.ignore_whitespace({ignore_whitespace:?})
44+
.unicode({unicode:?})
45+
.octal({octal:?})
46+
.build() else {{ return }};
47+
re.is_match({haystack:?});
48+
"#
49+
)
1250
}
13-
let mut split_at = usize::from(data[0]);
14-
let data = std::str::from_utf8(&data[1..]).ok()?;
15-
// Split data into a regex and haystack to search.
16-
let len = usize::try_from(data.chars().count()).ok()?;
17-
split_at = std::cmp::max(split_at, 1) % len;
18-
let char_index = data.char_indices().nth(split_at)?.0;
19-
let (pattern, input) = data.split_at(char_index);
20-
let re = regex::Regex::new(pattern).ok()?;
21-
re.is_match(input);
22-
Some(())
2351
}
52+
53+
fuzz_target!(|case: FuzzCase| {
54+
let Ok(re) = regex::RegexBuilder::new(case.pattern)
55+
.case_insensitive(case.case_insensitive)
56+
.multi_line(case.multi_line)
57+
.dot_matches_new_line(case.dot_matches_new_line)
58+
.swap_greed(case.swap_greed)
59+
.ignore_whitespace(case.ignore_whitespace)
60+
.unicode(case.unicode)
61+
.octal(case.octal)
62+
.build() else { return };
63+
re.is_match(case.haystack);
64+
});

0 commit comments

Comments
 (0)