/src/data-encoding/lib/fuzz/src/cmd.rs
Line | Count | Source |
1 | | #[cfg(not(fuzzing))] |
2 | | use std::collections::HashMap; |
3 | | #[cfg(not(fuzzing))] |
4 | | use std::path::{Path, PathBuf}; |
5 | | #[cfg(not(fuzzing))] |
6 | | use std::sync::OnceLock; |
7 | | |
8 | | use data_encoding::{BitOrder, Encoding, Specification}; |
9 | | |
10 | | use crate::{gen, spec}; |
11 | | |
12 | | macro_rules! debug { |
13 | | ($($arg:tt)*) => { |
14 | | #[cfg(not(fuzzing))] |
15 | | if *DEBUG.get().unwrap() { |
16 | | println!($($arg)*); |
17 | | } |
18 | | }; |
19 | | } |
20 | | |
21 | | #[cfg(not(fuzzing))] |
22 | | pub fn path(debug: bool) -> PathBuf { |
23 | | DEBUG.set(debug).unwrap(); |
24 | | PathBuf::from(std::env::args_os().nth(1).unwrap()) |
25 | | } |
26 | | |
27 | | #[cfg(not(fuzzing))] |
28 | | pub fn target(path: &Path) -> String { |
29 | | path.components().nth(2).unwrap().as_os_str().to_str().unwrap().to_owned() |
30 | | } |
31 | | |
32 | 42.7k | pub fn execute(target: &str, mut input: &[u8]) -> Output { |
33 | 42.7k | let mut output = BothOutput::default(); |
34 | 42.7k | match target { |
35 | 42.7k | "fuzz_any_spec" => { |
36 | 727 | let Some(spec) = gen::any_spec(&mut input) else { return output.reject() }; |
37 | 659 | let Ok(base) = spec.encoding() else { return output.reject() }; |
38 | 405 | let spec = base.specification(); |
39 | 405 | stat_spec(&mut output, &spec, &base); |
40 | 405 | let input = gen::rev_spec(&spec); |
41 | 405 | assert_eq!(gen::spec(&mut input.as_slice()).encoding().unwrap(), base); |
42 | | } |
43 | 42.0k | "impl_encode_len" => { |
44 | 481 | let (_, base) = gen_spec_base(&mut input, &mut output); |
45 | 481 | let _ = base.encode_len(usize::MAX / 512); |
46 | 481 | } |
47 | 41.5k | "impl_decode_len" => { |
48 | 464 | let (_, base) = gen_spec_base(&mut input, &mut output); |
49 | 464 | let _ = base.decode_len(usize::MAX / 8); |
50 | 464 | } |
51 | 41.1k | "impl_encode" => { |
52 | 4.11k | let (spec, base) = gen_spec_base(&mut input, &mut output); |
53 | 4.11k | assert_eq!(base.encode(input), spec::encode(&spec, input)); |
54 | | } |
55 | 36.9k | "impl_decode" => { |
56 | 8.06k | let (spec, base) = gen_spec_base(&mut input, &mut output); |
57 | 8.06k | let actual = base.decode(input); |
58 | 8.06k | output.insert("decode_ok", actual.is_ok() as usize); |
59 | 8.06k | assert_eq!(actual.ok(), spec::decode(&spec, input)); |
60 | | } |
61 | 28.9k | "impl_encode_mut_str" => { |
62 | 4.17k | let (spec, base) = gen_spec_base(&mut input, &mut output); |
63 | 4.17k | let mut output = vec![0; base.encode_len(input.len())]; |
64 | 4.17k | assert_eq!(base.encode_mut_str(input, &mut output), spec::encode(&spec, input)); |
65 | | } |
66 | 24.7k | "impl_encode_append" => { |
67 | 4.23k | let (spec, base) = gen_spec_base(&mut input, &mut output); |
68 | 4.23k | let mut output = String::new(); |
69 | 4.23k | base.encode_append(input, &mut output); |
70 | 4.23k | assert_eq!(output, spec::encode(&spec, input)); |
71 | | } |
72 | 20.5k | "impl_encode_write_buffer" => { |
73 | 5.44k | let (_, base) = gen_spec_base(&mut input, &mut output); |
74 | 5.44k | let mut buffer = vec![0; gen::nat(&mut input, 510, 2050)]; |
75 | 5.44k | output.insert("buffer_len", buffer.len()); |
76 | 5.44k | let mut actual = String::new(); |
77 | 5.44k | base.encode_write_buffer(input, &mut actual, &mut buffer).unwrap(); |
78 | 5.44k | assert_eq!(actual, base.encode(input)); |
79 | | } |
80 | 15.0k | "impl_new_encoder" => { |
81 | 5.47k | let (_, base) = gen_spec_base(&mut input, &mut output); |
82 | 5.47k | let mut actual = String::new(); |
83 | 5.47k | let mut full = Vec::new(); |
84 | 5.47k | let mut encoder = base.new_encoder(&mut actual); |
85 | 5.47k | let mut num_chunks = 0; |
86 | 6.61M | while !input.is_empty() { |
87 | 6.60M | let len = gen::nat(&mut input, 0, 3 * 256 - 1); |
88 | 6.60M | let chunk = gen::bytes(&mut input, len); |
89 | 6.60M | full.extend_from_slice(chunk); |
90 | 6.60M | encoder.append(chunk); |
91 | 6.60M | num_chunks += 1; |
92 | 6.60M | } |
93 | 5.47k | encoder.finalize(); |
94 | 5.47k | output.insert("full_len", full.len()); |
95 | 5.47k | output.insert("num_chunks", num_chunks); |
96 | 5.47k | assert_eq!(actual, base.encode(&full)); |
97 | | } |
98 | 9.59k | "spec_decode_encode" => { |
99 | 2.12k | let (_, base) = gen_spec_base(&mut input, &mut output); |
100 | 2.12k | let true = base.is_canonical() else { return output.reject() }; |
101 | 1.89k | let Ok(tmp) = base.decode(input) else { return output.reject() }; |
102 | 1.55k | assert_eq!(base.encode(&tmp).as_bytes(), input); |
103 | | } |
104 | 7.47k | "spec_encode_decode" => { |
105 | 7.02k | let (_, base) = gen_spec_base(&mut input, &mut output); |
106 | 7.02k | assert_eq!(base.decode(base.encode(input).as_bytes()).unwrap(), input); |
107 | | } |
108 | 452 | "spec_spec_base" => { |
109 | 452 | let (_, base) = gen_spec_base(&mut input, &mut output); |
110 | 452 | assert_eq!(base.specification().encoding().unwrap(), base); |
111 | | } |
112 | 0 | x => unimplemented!("{x:?}"), |
113 | | } |
114 | 41.8k | output.0 |
115 | 42.7k | } |
116 | | |
117 | 42.0k | fn gen_spec_base(input: &mut &[u8], output: &mut BothOutput) -> (Specification, Encoding) { |
118 | 42.0k | let base = gen::base(input); |
119 | 42.0k | let spec = base.specification(); |
120 | | debug!("{spec:#?}"); |
121 | | debug!("{input:?}"); |
122 | 42.0k | stat_spec(output, &spec, &base); |
123 | 42.0k | output.insert("input_len", input.len()); |
124 | 42.0k | (spec, base) |
125 | 42.0k | } |
126 | | |
127 | 42.4k | fn stat_spec(output: &mut BothOutput, spec: &Specification, base: &Encoding) { |
128 | 42.4k | output.insert("bit", spec.symbols.len().trailing_zeros() as usize); |
129 | 42.4k | output.insert("msb", (spec.bit_order == BitOrder::MostSignificantFirst) as usize); |
130 | 42.4k | output.insert("ctb", spec.check_trailing_bits as usize); |
131 | 42.4k | output.insert("pad", spec.padding.is_some() as usize); |
132 | 42.4k | output.insert("ignore_len", spec.ignore.len()); |
133 | 42.4k | output.insert("wrap_col", spec.wrap.width); |
134 | 42.4k | output.insert("wrap_len", spec.wrap.separator.len()); |
135 | 42.4k | output.insert("translate_len", spec.translate.from.len()); |
136 | 42.4k | output.insert("is_canonical", base.is_canonical() as usize); |
137 | 42.4k | } |
138 | | |
139 | | #[cfg(fuzzing)] |
140 | | type Output = libfuzzer_sys::Corpus; |
141 | | #[cfg(not(fuzzing))] |
142 | | type Output = HashMap<&'static str, usize>; |
143 | | |
144 | | struct BothOutput(Output); |
145 | | |
146 | | impl Default for BothOutput { |
147 | 42.7k | fn default() -> Self { |
148 | | #[cfg(fuzzing)] |
149 | 42.7k | let output = libfuzzer_sys::Corpus::Keep; |
150 | | #[cfg(not(fuzzing))] |
151 | | let output = HashMap::default(); |
152 | 42.7k | BothOutput(output) |
153 | 42.7k | } |
154 | | } |
155 | | |
156 | | impl BothOutput { |
157 | | #[cfg(fuzzing)] |
158 | 448k | fn insert(&mut self, _: &'static str, _: usize) {} |
159 | | #[cfg(not(fuzzing))] |
160 | | fn insert(&mut self, key: &'static str, value: usize) { |
161 | | assert!(self.0.insert(key, value).is_none()); |
162 | | } |
163 | | |
164 | | #[cfg(fuzzing)] |
165 | 896 | fn reject(self) -> Output { |
166 | 896 | libfuzzer_sys::Corpus::Reject |
167 | 896 | } |
168 | | #[cfg(not(fuzzing))] |
169 | | fn reject(self) -> Output { |
170 | | self.0 |
171 | | } |
172 | | } |
173 | | |
174 | | #[cfg(not(fuzzing))] |
175 | | static DEBUG: OnceLock<bool> = OnceLock::new(); |