/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 | | }); |