Coverage Report

Created: 2026-03-31 07:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gitoxide/gix-config/fuzz/fuzz_targets/fuzz_file.rs
Line
Count
Source
1
#![no_main]
2
3
use anyhow::Result;
4
5
use bstr::{BStr, BString};
6
7
use gix_config::file::{init::Options, Metadata};
8
use libfuzzer_sys::fuzz_target;
9
use std::borrow::Cow;
10
use std::collections::BTreeSet;
11
use std::convert::TryInto;
12
use std::hint::black_box;
13
use std::str;
14
15
22.4k
fn fuzz_immutable_section(section: &gix_config::file::Section<'_>, buf: &mut Vec<u8>) {
16
7.59M
    for (key, value) in section.body().clone() {
17
7.59M
        let _ = black_box((key, value));
18
7.59M
    }
19
22.4k
    let mut seen = BTreeSet::new();
20
7.59M
    for key in section.value_names() {
21
7.59M
        if seen.insert(key) {
22
44.6k
            let _ = black_box(section.values(key.as_ref()));
23
7.55M
        }
24
    }
25
22.4k
    buf.clear();
26
22.4k
    let _ = black_box(section.write_to(buf));
27
22.4k
}
28
29
22.4k
fn fuzz_mutable_section(
30
22.4k
    file: &mut gix_config::File<'_>,
31
22.4k
    section_name: &str,
32
22.4k
    subsection_name: Option<BString>,
33
22.4k
) -> Result<()> {
34
    use bstr::ByteSlice;
35
22.4k
    let subsection_name: Option<&BStr> = subsection_name.as_ref().map(|b| (**b).as_bstr());
36
37
    // Mutate section.
38
22.4k
    let section_id = {
39
22.4k
        let mut section = file.section_mut(section_name, subsection_name)?;
40
22.4k
        let key = section.value_names().next().cloned();
41
42
22.4k
        if let Some(key) = key {
43
12.4k
            section.push_newline();
44
12.4k
            section.set(key, BStr::new("Set value"));
45
12.4k
            section.push_newline();
46
12.4k
        }
47
22.4k
        let kv_pair = section.pop().map(|(key, value)| (key.to_owned(), value));
48
22.4k
        if let Some((key, value)) = kv_pair {
49
12.4k
            section.push_with_comment(key, Some(&value), "Popped");
50
12.4k
        } else {
51
9.98k
            section.push("new-implicit".try_into()?, None);
52
9.98k
            section.push("new".try_into()?, Some("value".into()));
53
        }
54
22.4k
        section.id()
55
    };
56
57
22.4k
    _ = black_box(file.section_mut_by_id(section_id));
58
59
22.4k
    let new_section_name = section_name.to_string() + "_new";
60
22.4k
    _ = black_box(file.section_mut_or_create_new(&new_section_name, subsection_name));
61
62
22.4k
    if let Some(removed_section) = file.remove_section(&new_section_name, subsection_name) {
63
0
        _ = black_box(file.push_section(removed_section));
64
22.4k
    }
65
66
22.4k
    _ = black_box(file.new_section(Cow::Owned(new_section_name.clone()), None));
67
22.4k
    let renamed_section_name = section_name.to_string() + "_renamed";
68
22.4k
    let renamed_subsection_name: Option<Cow<'_, BStr>> =
69
22.4k
        subsection_name.map(|x| Cow::Owned((x.to_string() + "_renamed").into()));
70
22.4k
    _ = black_box(file.rename_section(
71
22.4k
        &new_section_name.clone(),
72
22.4k
        subsection_name,
73
22.4k
        Cow::Owned(renamed_section_name.clone()),
74
22.4k
        renamed_subsection_name.clone(),
75
22.4k
    ));
76
77
22.4k
    _ = black_box(file.rename_section_filter(
78
22.4k
        &new_section_name.clone(),
79
22.4k
        subsection_name,
80
22.4k
        Cow::Owned(renamed_section_name.clone()),
81
22.4k
        renamed_subsection_name.clone(),
82
        |_| false,
83
    ));
84
85
22.4k
    Ok(())
86
22.4k
}
87
88
6.80k
fn fuzz(input: &[u8]) -> Result<()> {
89
6.80k
    let meta = Metadata::default();
90
6.80k
    let options = Options::default();
91
6.80k
    let file = gix_config::File::from_bytes_no_includes(input, meta.clone(), options)?;
92
93
    // Sections and frontmatter.
94
6.16k
    _ = black_box(file.sections_and_ids().count());
95
6.16k
    _ = black_box(file.sections_and_postmatter().count());
96
6.16k
    _ = black_box(file.sections_by_name("section").map(std::iter::Iterator::count));
97
6.16k
    _ = black_box(file.frontmatter());
98
99
6.16k
    let mut buf = Vec::new();
100
6.16k
    let mut sections = Vec::new();
101
    // Don't perform too much work as this can blow up the size of the file.
102
22.4k
    for section in file.sections().take(10) {
103
22.4k
        fuzz_immutable_section(section, &mut buf);
104
22.4k
105
22.4k
        let header = section.header();
106
22.4k
        let section_name = str::from_utf8(header.name()).unwrap();
107
22.4k
        let subsection_name = header.subsection_name().map(std::borrow::ToOwned::to_owned);
108
22.4k
        sections.push((section_name, subsection_name));
109
22.4k
    }
110
111
6.16k
    let mut mutated_file = file.clone();
112
22.4k
    for (section_name, subsection_name) in sections.into_iter() {
113
22.4k
        let _ = black_box(fuzz_mutable_section(&mut mutated_file, section_name, subsection_name));
114
22.4k
    }
115
116
6.16k
    _ = black_box(mutated_file.append(file));
117
6.16k
    _ = black_box(gix_config::File::from_bytes_no_includes(
118
6.16k
        &mutated_file.to_bstring(),
119
6.16k
        meta,
120
6.16k
        options,
121
188
    )?);
122
123
5.97k
    Ok(())
124
6.80k
}
125
126
fuzz_target!(|input: &[u8]| {
127
    _ = black_box(fuzz(input));
128
});