Skip to content

Commit fee6bde

Browse files
Refactor gix-config fuzzer
- Add mutable API's to fuzzer. - Reading key's first and then using them to drive everying. this should improve fuzzing performance significantly.
1 parent edb12ff commit fee6bde

File tree

1 file changed

+114
-70
lines changed

1 file changed

+114
-70
lines changed
Lines changed: 114 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,133 @@
11
#![no_main]
22

33
use anyhow::Result;
4-
use arbitrary::Arbitrary;
5-
use bstr::BStr;
4+
5+
use bstr::{BStr, BString};
6+
67
use gix_config::{
78
file::{init::Options, Metadata},
89
File,
910
};
1011
use libfuzzer_sys::fuzz_target;
1112
use std::hint::black_box;
12-
13-
#[derive(Arbitrary, Debug)]
14-
struct Ctx<'a> {
15-
input: &'a [u8],
16-
sections_by_name: &'a str,
17-
section_subsection_key_triples: Vec<(&'a str, Option<&'a [u8]>, &'a str)>,
13+
use std::str;
14+
15+
fn fuzz_immutable(file: &File, section_name: &str, subsection_name: &Option<BString>, key: &str) {
16+
// Can't use the map here is the borrow checker chucks a flip converting between
17+
// &Option<BString> and Option<&BStr>.
18+
let subsection_name: Option<&BStr> = if let Some(n) = subsection_name {
19+
Some(n.as_ref())
20+
} else {
21+
None
22+
};
23+
// Singular strings.
24+
_ = black_box(file.string(section_name, subsection_name, key));
25+
_ = black_box(file.string_by_key(key));
26+
_ = black_box(file.string_filter(section_name, subsection_name, key, &mut |_| false));
27+
_ = black_box(file.string_filter_by_key(key, &mut |_| false));
28+
29+
// Plural strings.
30+
_ = black_box(file.strings(section_name, subsection_name, key));
31+
_ = black_box(file.strings_by_key(key));
32+
_ = black_box(file.strings_filter(section_name, subsection_name, key, &mut |_| false));
33+
_ = black_box(file.strings_filter_by_key(key, &mut |_| false));
34+
35+
// Singular path.
36+
_ = black_box(file.path(section_name, subsection_name, key));
37+
_ = black_box(file.path_by_key(key));
38+
_ = black_box(file.path_filter(section_name, subsection_name, key, &mut |_| false));
39+
_ = black_box(file.path_filter_by_key(key, &mut |_| false));
40+
41+
// Singular bool.
42+
_ = black_box(file.boolean(section_name, subsection_name, key));
43+
_ = black_box(file.boolean_by_key(key));
44+
_ = black_box(file.boolean_filter(section_name, subsection_name, key, &mut |_| false));
45+
_ = black_box(file.boolean_filter_by_key(key, &mut |_| false));
46+
// NOTE: no plural bool.
47+
48+
// Singular integer.
49+
_ = black_box(file.integer(section_name, subsection_name, key));
50+
_ = black_box(file.integer_by_key(key));
51+
_ = black_box(file.integer_filter(section_name, subsection_name, key, &mut |_| false));
52+
_ = black_box(file.integer_filter_by_key(key, &mut |_| false));
53+
54+
// Plural integers.
55+
_ = black_box(file.integers(section_name, subsection_name, key));
56+
_ = black_box(file.integers_by_key(key));
57+
_ = black_box(file.integers_filter(section_name, subsection_name, key, &mut |_| false));
58+
_ = black_box(file.integers_filter_by_key(key, &mut |_| false));
1859
}
1960

20-
const DEFAULT_TRIPLE: (&str, Option<&'static [u8]>, &str) = ("section", Some(b"subsection"), "key");
61+
fn fuzz_mutable(file: &mut File, section_name: &str, subsection_name: &Option<BString>, key: &str) -> Result<()> {
62+
// TODO: It might make sense to make fuzzed modifications.
63+
64+
// Can't use the map here is the borrow checker chucks a flip converting between
65+
// &Option<BString> and Option<&BStr>.
66+
let subsection_name: Option<&BStr> = if let Some(n) = subsection_name {
67+
Some(n.as_ref())
68+
} else {
69+
None
70+
};
71+
72+
// Mutate section.
73+
{
74+
let mut section = file.section_mut(section_name, subsection_name)?;
75+
section.push_newline();
76+
section.set(key.to_string().try_into()?, BStr::new("Set value"));
77+
section.push_newline();
78+
let kv_pair = section.pop().map(|(key, value)| (key.to_owned(), value.to_owned()));
79+
if let Some((key, value)) = kv_pair {
80+
section.push_with_comment(key, Some(&value), "Popped");
81+
}
82+
}
83+
84+
// Singular raw.
85+
_ = black_box(
86+
file.raw_value_mut(section_name, subsection_name, key)?
87+
.set_string("raw_values"),
88+
);
89+
90+
// Plural raw.
91+
_ = black_box(file.raw_values_mut(section_name, subsection_name, key)?.len());
92+
Ok(())
93+
}
2194

22-
fn fuzz(ctx: Ctx) -> Result<()> {
95+
fn fuzz(input: &[u8]) -> Result<()> {
2396
let meta = Metadata::default();
2497
let options = Options::default();
25-
let file = File::from_bytes_no_includes(&ctx.input, meta.clone(), options.clone())?;
26-
27-
let mut triples = ctx.section_subsection_key_triples.iter();
28-
29-
let (section_name, subsection_name, key) = triples.next().unwrap_or(&DEFAULT_TRIPLE);
30-
_ = black_box(file.string(section_name, subsection_name.map(|x| BStr::new(x)), key));
31-
_ = black_box(file.string_by_key(BStr::new(key)));
32-
_ = black_box(file.string_filter(section_name, subsection_name.map(|x| BStr::new(x)), key, &mut |_| false));
33-
_ = black_box(file.string_filter_by_key(BStr::new(key), &mut |_| false));
34-
35-
let (section_name, subsection_name, key) = triples.next().unwrap_or(&DEFAULT_TRIPLE);
36-
_ = black_box(file.path(section_name, subsection_name.map(|x| BStr::new(x)), key));
37-
_ = black_box(file.path_by_key(BStr::new(key)));
38-
_ = black_box(file.path_filter(section_name, subsection_name.map(|x| BStr::new(x)), key, &mut |_| false));
39-
_ = black_box(file.path_filter_by_key(BStr::new(key), &mut |_| false));
40-
41-
let (section_name, subsection_name, key) = triples.next().unwrap_or(&DEFAULT_TRIPLE);
42-
_ = black_box(file.boolean(section_name, subsection_name.map(|x| BStr::new(x)), key));
43-
_ = black_box(file.boolean_by_key(BStr::new(key)));
44-
_ = black_box(file.boolean_filter(section_name, subsection_name.map(|x| BStr::new(x)), key, &mut |_| false));
45-
_ = black_box(file.boolean_filter_by_key(BStr::new(key), &mut |_| false));
46-
47-
let (section_name, subsection_name, key) = triples.next().unwrap_or(&DEFAULT_TRIPLE);
48-
_ = black_box(file.integer(section_name, subsection_name.map(|x| BStr::new(x)), key));
49-
_ = black_box(file.integer_by_key(BStr::new(key)));
50-
_ = black_box(file.integer_filter(section_name, subsection_name.map(|x| BStr::new(x)), key, &mut |_| false));
51-
_ = black_box(file.integer_filter_by_key(BStr::new(key), &mut |_| false));
52-
53-
let (section_name, subsection_name, key) = triples.next().unwrap_or(&DEFAULT_TRIPLE);
54-
_ = black_box(file.strings(section_name, subsection_name.map(|x| BStr::new(x)), key));
55-
_ = black_box(file.strings_by_key(BStr::new(key)));
56-
_ = black_box(file.strings_filter(section_name, subsection_name.map(|x| BStr::new(x)), key, &mut |_| false));
57-
_ = black_box(file.strings_filter_by_key(BStr::new(key), &mut |_| false));
58-
59-
let (section_name, subsection_name, key) = triples.next().unwrap_or(&DEFAULT_TRIPLE);
60-
_ = black_box(file.integers(section_name, subsection_name.map(|x| BStr::new(x)), key));
61-
_ = black_box(file.integers_by_key(BStr::new(key)));
62-
_ = black_box(file.integers_filter(section_name, subsection_name.map(|x| BStr::new(x)), key, &mut |_| false));
63-
_ = black_box(file.integers_filter_by_key(BStr::new(key), &mut |_| false));
64-
65-
let (section_name, subsection_name, key) = triples.next().unwrap_or(&DEFAULT_TRIPLE);
66-
_ = black_box(file.integers(section_name, subsection_name.map(|x| BStr::new(x)), key));
67-
_ = black_box(file.integers_by_key(BStr::new(key)));
68-
_ = black_box(file.integers_filter(section_name, subsection_name.map(|x| BStr::new(x)), key, &mut |_| false));
69-
_ = black_box(file.integers_filter_by_key(BStr::new(key), &mut |_| false));
70-
71-
_ = black_box(file.sections().count());
98+
let mut file = File::from_bytes_no_includes(input, meta.clone(), options.clone())?;
99+
100+
let section_triples: Vec<_> = file
101+
.sections()
102+
.flat_map(|sec| {
103+
sec.keys().map(|key| {
104+
let section_name = str::from_utf8(sec.header().name()).unwrap();
105+
let subsection_name = sec.header().subsection_name();
106+
let key = str::from_utf8(&key).unwrap();
107+
return (
108+
section_name.to_owned(),
109+
subsection_name.map(|x| x.to_owned()),
110+
key.to_owned(),
111+
);
112+
})
113+
})
114+
.collect();
115+
116+
for section_triple in section_triples.iter() {
117+
let (section_name, subsection_name, key) = section_triple;
118+
black_box(fuzz_immutable(&file, &section_name, &subsection_name, &key));
119+
}
120+
121+
for section_triple in section_triples.iter() {
122+
let (section_name, subsection_name, key) = section_triple;
123+
_ = black_box(fuzz_mutable(&mut file, &section_name, &subsection_name, &key));
124+
}
125+
72126
_ = black_box(file.sections_and_ids().count());
73127
_ = black_box(file.sections_and_postmatter().count());
74-
_ = black_box(file.sections_by_name(ctx.sections_by_name).map(|x| x.count()));
128+
_ = black_box(file.sections_by_name("section").map(|x| x.count()));
75129
_ = black_box(file.frontmatter());
76130

77-
for section in file.sections() {
78-
for key in section.keys() {
79-
_ = black_box(
80-
section
81-
.value_implicit(key.as_ref())
82-
.expect("The key exists, so should the value."),
83-
);
84-
}
85-
}
86-
87131
let roundtrip_as_string: Vec<u8> = file.to_bstring().into();
88132
_ = black_box(File::from_bytes_no_includes(
89133
&roundtrip_as_string,
@@ -93,6 +137,6 @@ fn fuzz(ctx: Ctx) -> Result<()> {
93137
Ok(())
94138
}
95139

96-
fuzz_target!(|ctx: Ctx| {
97-
_ = black_box(fuzz(ctx));
140+
fuzz_target!(|input: &[u8]| {
141+
_ = black_box(fuzz(input));
98142
});

0 commit comments

Comments
 (0)