/src/zip/fuzz/fuzz_targets/structured_fuzz_reader.rs
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2023 Google LLC |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | //limitations under the License. |
14 | | // |
15 | | //################### |
16 | | |
17 | | #![no_main] |
18 | | |
19 | | use arbitrary::Arbitrary; |
20 | | use libfuzzer_sys::fuzz_target; |
21 | | use std::hint::black_box; |
22 | | use std::io::Cursor; |
23 | | |
24 | 932k | #[derive(Arbitrary, Debug)] <structured_fuzz_reader::ReadOperations as arbitrary::Arbitrary>::arbitrary::{closure#0} Line | Count | Source | 24 | 14.3k | #[derive(Arbitrary, Debug)] |
<structured_fuzz_reader::ReadOperations as arbitrary::Arbitrary>::arbitrary::{closure#1} Line | Count | Source | 24 | 917k | #[derive(Arbitrary, Debug)] |
<structured_fuzz_reader::ReadOperations as arbitrary::Arbitrary>::arbitrary::{closure#2} Line | Count | Source | 24 | 14.3k | #[derive(Arbitrary, Debug)] |
Unexecuted instantiation: <structured_fuzz_reader::ReadOperations as arbitrary::Arbitrary>::arbitrary_take_rest::{closure#0} Unexecuted instantiation: <structured_fuzz_reader::ReadOperations as arbitrary::Arbitrary>::arbitrary_take_rest::{closure#1} Unexecuted instantiation: <structured_fuzz_reader::ReadOperations as arbitrary::Arbitrary>::arbitrary_take_rest::{closure#2} Unexecuted instantiation: <structured_fuzz_reader::ReadOperations as arbitrary::Arbitrary>::try_size_hint::{closure#0} |
25 | | enum ReadOperations<'a> { |
26 | | ReadByName(String), |
27 | | ReadByNameDecrypt { name: String, password: &'a [u8] }, |
28 | | ReadByIterableNames, |
29 | | ReadByIndex(usize), |
30 | | ReadByIndexDecrypt { index: usize, password: &'a [u8] }, |
31 | | ReadByIterableIndicies, |
32 | | ReadComment, |
33 | | } |
34 | | |
35 | 46.6k | #[derive(Arbitrary, Debug)] Unexecuted instantiation: <structured_fuzz_reader::Driver as arbitrary::Arbitrary>::arbitrary_take_rest::{closure#0} <structured_fuzz_reader::Driver as arbitrary::Arbitrary>::arbitrary_take_rest::{closure#1} Line | Count | Source | 35 | 23.3k | #[derive(Arbitrary, Debug)] |
Unexecuted instantiation: <structured_fuzz_reader::Driver as arbitrary::Arbitrary>::arbitrary_take_rest::{closure#2} <structured_fuzz_reader::Driver as arbitrary::Arbitrary>::try_size_hint::{closure#0} Line | Count | Source | 35 | 23.3k | #[derive(Arbitrary, Debug)] |
Unexecuted instantiation: <structured_fuzz_reader::Driver as arbitrary::Arbitrary>::arbitrary::{closure#0} Unexecuted instantiation: <structured_fuzz_reader::Driver as arbitrary::Arbitrary>::arbitrary::{closure#1} Unexecuted instantiation: <structured_fuzz_reader::Driver as arbitrary::Arbitrary>::arbitrary::{closure#2} |
36 | | #[repr(C)] |
37 | | struct Driver<'a> { |
38 | | // NOTE: we are defining repr(C) so that the struct field ordering is consistent. |
39 | | // This consistent ordering means that we still generate a set of zip files |
40 | | // for the fuzz corpus, which will become the .zip_file, and any leftover data |
41 | | // will be used for structured fuzzing. |
42 | | zip_file: &'a [u8], |
43 | | read_operations: Vec<ReadOperations<'a>>, |
44 | | } |
45 | | |
46 | 376k | fn read_file_attributes(file: &mut zip::read::ZipFile) -> Result<(), zip::result::ZipError> { |
47 | | use std::io::Read; |
48 | 376k | let _unused = black_box(file.name()); |
49 | 376k | let _unused = black_box(file.mangled_name()); |
50 | 376k | let _unused = black_box(file.enclosed_name()); |
51 | 376k | let _unused = black_box(file.compression()); |
52 | 376k | let _unused = black_box(file.compressed_size()); |
53 | 376k | let _unused = black_box(file.size()); |
54 | 376k | let _unused = black_box(file.last_modified()); |
55 | 376k | let _unused = black_box(file.is_dir()); |
56 | 376k | let _unused = black_box(file.is_file()); |
57 | 376k | let _unused = black_box(file.unix_mode()); |
58 | 376k | let _unused = black_box(file.crc32()); |
59 | 376k | let _unused = black_box(file.data_start()); |
60 | 376k | let _unused = black_box(file.header_start()); |
61 | 376k | let _unused = black_box(file.central_header_start()); |
62 | 376k | let mut s: String = String::new(); |
63 | 376k | let _unused = black_box(file.read_to_string(&mut s)); |
64 | 376k | return Ok(()); |
65 | 376k | } |
66 | | |
67 | 23.3k | fn fuzzed_extract(driver: Driver) -> Result<(), zip::result::ZipError> { |
68 | 23.3k | match zip::ZipArchive::new(Cursor::new(driver.zip_file)) { |
69 | 21.2k | Ok(mut archive) => { |
70 | 322k | for operation in driver.read_operations.iter() { |
71 | 322k | match operation { |
72 | 21.7k | ReadOperations::ReadByName(name) => { |
73 | 21.7k | let mut file = archive.by_name(name)?; |
74 | 21.1k | let _unused = black_box(read_file_attributes(&mut file)); |
75 | | } |
76 | | // TODO: This could probably use a custom mutator, or a specialised seed corpus. |
77 | | // The probability that the fuzzer guesses the correct password is exceedingly low. |
78 | 1.38k | ReadOperations::ReadByNameDecrypt { name, password } => { |
79 | 1.38k | match archive.by_name_decrypt(name, password)? { |
80 | 854 | Ok(mut file) => { |
81 | 854 | let _unused = black_box(&read_file_attributes(&mut file)); |
82 | 854 | } |
83 | 419 | Err(e) => { |
84 | 419 | let _unused = black_box(format!("{e:?}")); |
85 | 419 | let _unused = black_box(format!("{e:#?}")); |
86 | 419 | } |
87 | | } |
88 | | } |
89 | | ReadOperations::ReadByIterableNames => { |
90 | 190k | let names: Vec<String> = |
91 | 235k | archive.file_names().map(|x| x.to_string()).collect(); |
92 | 235k | for name in names.iter() { |
93 | 235k | let mut file = archive.by_name(&name)?; |
94 | 235k | let _unused = black_box(read_file_attributes(&mut file)); |
95 | | } |
96 | | } |
97 | 238 | ReadOperations::ReadByIndex(index) => { |
98 | 238 | let mut file = archive.by_index(*index)?; |
99 | 132 | let _unused = black_box(read_file_attributes(&mut file)); |
100 | | } |
101 | 222 | ReadOperations::ReadByIndexDecrypt { index, password } => { |
102 | 222 | match archive.by_index_decrypt(*index, password)? { |
103 | 98 | Ok(mut file) => { |
104 | 98 | let _unused = black_box(read_file_attributes(&mut file)); |
105 | 98 | } |
106 | 4 | Err(e) => { |
107 | 4 | let _unused = black_box(format!("{e:?}")); |
108 | 4 | let _unused = black_box(format!("{e:#?}")); |
109 | 4 | } |
110 | | } |
111 | | } |
112 | | ReadOperations::ReadByIterableIndicies => { |
113 | 118k | for i in 0..archive.len() { |
114 | 118k | let mut file = archive.by_index(i)?; |
115 | 118k | let _unused = black_box(read_file_attributes(&mut file)); |
116 | | } |
117 | | } |
118 | 1.91k | ReadOperations::ReadComment => { |
119 | 1.91k | let _unused = black_box(archive.comment()); |
120 | 1.91k | } |
121 | | } |
122 | | } |
123 | | } |
124 | 2.07k | Err(e) => return Err(e), |
125 | | } |
126 | 20.2k | return Ok(()); |
127 | 23.3k | } |
128 | | |
129 | | fuzz_target!(|driver: Driver| { |
130 | | if let Err(e) = fuzzed_extract(driver) { |
131 | | let _unused = black_box(format!("{e:?}")); |
132 | | let _unused = black_box(format!("{e:#?}")); |
133 | | } |
134 | | }); |