Coverage Report

Created: 2026-06-30 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quick-xml/fuzz/fuzz_targets/structured_roundtrip.rs
Line
Count
Source
1
#![no_main]
2
3
use arbitrary::{Arbitrary, Unstructured};
4
use libfuzzer_sys::fuzz_target;
5
use quick_xml::events::{BytesCData, BytesPI, BytesText, Event};
6
use quick_xml::reader::{Config, NsReader, Reader};
7
use quick_xml::writer::Writer;
8
use std::{hint::black_box, io::Cursor};
9
10
#[derive(Debug, arbitrary::Arbitrary)]
11
enum ElementWriterFunc<'a> {
12
    WriteTextContent(&'a str),
13
    WriteCDataContent(&'a str),
14
    WritePiContent(&'a str),
15
    WriteEmpty,
16
    // TODO: We can't automatically generate an arbitrary function
17
    // WriteInnerContent,
18
}
19
20
18.6k
fn arbitrary_name(u: &mut Unstructured) -> arbitrary::Result<String> {
21
18.6k
    let s = String::arbitrary(u)?;
22
18.6k
    if s.is_empty() || !s.chars().all(char::is_alphanumeric) {
23
78
        return Err(arbitrary::Error::IncorrectFormat);
24
18.6k
    }
25
18.6k
    return Ok(s);
26
18.6k
}
27
28
#[derive(Debug, arbitrary::Arbitrary)]
29
enum WriterFunc<'a> {
30
    WriteEvent(Event<'a>),
31
    WriteBom,
32
    WriteIndent,
33
    CreateElement {
34
        #[arbitrary(with = arbitrary_name)]
35
        name: String,
36
        func: ElementWriterFunc<'a>,
37
        attributes: Vec<(&'a str, &'a str)>,
38
    },
39
}
40
41
#[derive(Debug, arbitrary::Arbitrary)]
42
struct Driver<'a> {
43
    writer_funcs: Vec<WriterFunc<'a>>,
44
    reader_config: Config,
45
}
46
47
8.25k
fn fuzz_round_trip(driver: Driver) -> quick_xml::Result<()> {
48
8.25k
    let mut writer = Writer::new(Cursor::new(Vec::new()));
49
8.25k
    let writer_funcs = driver.writer_funcs;
50
650k
    for writer_func in writer_funcs.iter() {
51
        // TODO: Handle error cases.
52
        use WriterFunc::*;
53
650k
        match writer_func {
54
38.2k
            WriteEvent(event) => writer.write_event(event.borrow())?,
55
591k
            WriteBom => writer.write_bom()?,
56
2.94k
            WriteIndent => writer.write_indent()?,
57
            CreateElement {
58
18.5k
                name,
59
18.5k
                func,
60
18.5k
                attributes,
61
            } => {
62
18.5k
                let element_writer = writer
63
18.5k
                    .create_element(name)
64
18.5k
                    .with_attributes(attributes.into_iter().copied());
65
                use ElementWriterFunc::*;
66
18.5k
                match func {
67
4.00k
                    WriteTextContent(text) => {
68
4.00k
                        element_writer.write_text_content(BytesText::from_escaped(*text))?;
69
                    }
70
8.59k
                    WriteCDataContent(text) => {
71
8.59k
                        _ = element_writer.write_cdata_content(BytesCData::new(*text))?;
72
                    }
73
2.74k
                    WritePiContent(text) => {
74
2.74k
                        _ = element_writer.write_pi_content(BytesPI::new(*text))?;
75
                    }
76
                    WriteEmpty => {
77
3.18k
                        _ = element_writer.write_empty()?;
78
                    }
79
                }
80
            }
81
        }
82
    }
83
8.25k
    let xml = writer.into_inner().into_inner();
84
    // The str should be valid as we just generated it, unwrapping **should** be safe.
85
8.25k
    let mut reader = Reader::from_str(std::str::from_utf8(&xml).unwrap());
86
8.25k
    *reader.config_mut() = driver.reader_config.clone();
87
88
    loop {
89
12.6M
        let event = black_box(reader.read_event()?);
90
12.6M
        if event == Event::Eof {
91
5.45k
            break;
92
12.6M
        }
93
    }
94
95
5.45k
    let mut reader = NsReader::from_reader(&xml[..]);
96
5.45k
    *reader.config_mut() = driver.reader_config;
97
98
    loop {
99
3.19M
        let event = black_box(reader.read_event()?);
100
3.19M
        if event == Event::Eof {
101
5.31k
            break;
102
3.18M
        }
103
    }
104
5.31k
    Ok(())
105
8.25k
}
106
107
fuzz_target!(|driver: Driver| {
108
    if let Err(e) = fuzz_round_trip(driver) {
109
        black_box(format!("{e:?}"));
110
    }
111
});