1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
|
//! Fuzzy testing.
//! Tries to discover panics with random bytes.
//! This test is expensive and therefore marked with `#[ignore]`. To run this test, use `cargo test -- --ignored`.
use std::panic::{catch_unwind};
use rand::rngs::{StdRng};
use rand::{Rng};
extern crate exr;
use exr::prelude::*;
use std::path::PathBuf;
use std::ffi::OsStr;
use std::fs::File;
use std::io::{Write, Cursor};
use exr::image::read::read_first_rgba_layer_from_file;
use exr::image::pixel_vec::PixelVec;
fn exr_files(path: &'static str, filter: bool) -> impl Iterator<Item=PathBuf> {
walkdir::WalkDir::new(path).into_iter().map(std::result::Result::unwrap)
.filter(|entry| entry.path().is_file())
.filter(move |entry| !filter || entry.path().extension() == Some(OsStr::new("exr")))
.map(walkdir::DirEntry::into_path)
}
/// Just don't panic.
#[test]
pub fn fuzzed(){
for ref file in exr_files("tests/images/fuzzed", false) {
let _ = read().no_deep_data().largest_resolution_level().all_channels()
.first_valid_layer().all_attributes().pedantic().from_file(file);
let _ = read().no_deep_data().all_resolution_levels().all_channels()
.all_layers().all_attributes().pedantic().from_file(file);
}
}
/// Require an error but no panic.
#[test]
pub fn damaged(){
let mut passed = true;
for ref file in exr_files("tests/images/invalid", false) {
let result = catch_unwind(move || {
let _meta_data = MetaData::read_from_file(file, false)?;
{
let _minimal = read().no_deep_data()
.largest_resolution_level()
.rgba_channels(
|_size, _channels| (),
|_: &mut (), _position: Vec2<usize>, _pixel: (Sample, Sample, Sample, Sample)| {}
)
.first_valid_layer().all_attributes()
.from_file(&file)?;
}
{
let _minimal = read().no_deep_data()
.largest_resolution_level() // TODO all levels
.rgba_channels(
|_size, _channels| (),
|_: &mut (), _position: Vec2<usize>, _pixel: (Sample, Sample, Sample, Sample)| {}
)
.all_layers().all_attributes()
.pedantic()
.from_file(&file)?;
}
{
let _rgba = read_first_rgba_layer_from_file(
file,
PixelVec::<(Sample, Sample, Sample, Sample)>::constructor,
PixelVec::set_pixel
)?;
}
{
let _full = read_all_data_from_file(file)?;
}
Ok(())
});
// this should not panic, only err:
passed = passed && match result {
Ok(Err(Error::Invalid(message))) => {
println!("✓ Recognized as invalid ({}): {:?}", message, file);
true
},
Ok(Err(Error::NotSupported(message))) => {
println!("- Unsupported ({}): {:?}", message, file);
true
},
Ok(Err(Error::Io(error))) => {
println!("✗ Unexpected IO Error: {:?}, {:?}", file, error);
false
},
Err(_) => {
println!("✗ Not recognized as invalid: {:?}", file);
false
},
Ok(Ok(_)) => {
let meta_data = MetaData::read_from_file(file, true);
if let Err(error) = meta_data {
println!("✓ Recognized as invalid when pedantic ({}): {:?}", error, file);
true
}
else {
println!("✗ Oh no, there is nothing wrong with: {:#?}", file);
false
}
},
_ => unreachable!(),
};
}
assert!(passed, "A damaged file was not handled correctly");
}
#[test]
#[ignore]
pub fn fuzz(){
println!("started fuzzing");
let files: Vec<PathBuf> = exr_files("tests/images", true).collect();
let seed = [92,1,0,130,211,8,21,70,74,4,9,5,0,23,0,3,20,25,6,5,229,30,0,34,218,0,40,7,5,2,7,0,];
let mut random: StdRng = rand::SeedableRng::from_seed(seed);
let mut records = File::create("tests/images/fuzzed/list.txt").unwrap();
records.write_all(format!("seed = {:?}", seed).as_bytes()).unwrap();
let start_index = 0; // default is 0. increase this integer for debugging a specific fuzz case
for fuzz_index in 0 .. 1024_u64 * 2048 * 4 {
let file_1_name = &files[random.gen_range(0 .. files.len())];
let mutation_point = random.gen::<f32>().powi(3);
let mutation = random.gen::<u8>();
if fuzz_index >= start_index {
let mut file = std::fs::read(file_1_name).unwrap();
let index = ((mutation_point * file.len() as f32) as usize + 4) % file.len();
file[index] = mutation;
let file = file.as_slice();
let result = catch_unwind(move || {
let read_all_data = read().no_deep_data()
.all_resolution_levels().all_channels().all_layers().all_attributes();
match read_all_data.from_buffered(Cursor::new(file)) {
Err(Error::Invalid(error)) => println!("✓ No Panic. [{}]: Invalid: {}.", fuzz_index, error),
Err(Error::NotSupported(error)) => println!("- No Panic. [{}]: Unsupported: {}.", fuzz_index, error),
_ => {},
}
});
if let Err(_) = result {
records.write_all(fuzz_index.to_string().as_bytes()).unwrap();
records.flush().unwrap();
let seed = seed.iter().map(|num| num.to_string()).collect::<Vec<String>>().join("-");
let mut saved = File::create(format!("tests/images/fuzzed/fuzz_{}_{}.exr", fuzz_index, seed)).unwrap();
saved.write_all(file).unwrap();
println!("✗ PANIC! [{}]", fuzz_index);
}
}
}
}
|