/src/image-png/fuzz/fuzz_targets/roundtrip.rs
Line | Count | Source (jump to first uncovered line) |
1 | | #![no_main] |
2 | | |
3 | | use png::{FilterType, ColorType, BitDepth}; |
4 | | #[macro_use] extern crate libfuzzer_sys; |
5 | | extern crate png; |
6 | | |
7 | | fuzz_target!(|data: (u8, u8, u8, u8, u8, Vec<u8>, Vec<u8>)| { |
8 | | if let Some((raw, encoded)) = encode_png(data.0, data.1, data.2, data.3, data.4, &data.5, &data.6) { |
9 | | let (_info, raw_decoded) = decode_png(&encoded); |
10 | | // raw_decoded can be padded with zeroes at the end, not sure if that's correct |
11 | | let raw_decoded = &raw_decoded[..raw.len()]; |
12 | | assert_eq!(raw, raw_decoded); |
13 | | } |
14 | | }); |
15 | | |
16 | 0 | fn encode_png<'a>(width: u8, filter: u8, compression: u8, color_type: u8, raw_bit_depth: u8, raw_palette: &'a [u8], data: &'a [u8]) -> Option<(&'a [u8], Vec<u8>)> { |
17 | 0 | // Convert untyped bytes to the correct types and validate them: |
18 | 0 | let width = width as u32; |
19 | 0 | if width == 0 { return None }; |
20 | 0 | let filter = FilterType::from_u8(filter)?; |
21 | 0 | let bit_depth = BitDepth::from_u8(raw_bit_depth)?; |
22 | 0 | let max_palette_length = 3 * u32::pow(2, raw_bit_depth as u32) as usize; |
23 | 0 | let mut palette = raw_palette; |
24 | 0 | let color_type = ColorType::from_u8(color_type)?; |
25 | 0 | if let ColorType::Indexed = color_type { |
26 | | // when palette is needed, ensure that palette.len() <= 2 ^ bit_depth |
27 | 0 | if raw_palette.len() > max_palette_length { |
28 | 0 | palette = &raw_palette[..max_palette_length]; |
29 | 0 | } |
30 | 0 | } |
31 | | // compression |
32 | 0 | let compression = match compression { |
33 | 0 | 0 => png::Compression::Default, |
34 | 0 | 1 => png::Compression::Fast, |
35 | 0 | 2 => png::Compression::Best, |
36 | 0 | 3 => png::Compression::Huffman, |
37 | 0 | 4 => png::Compression::Rle, |
38 | 0 | _ => return None, |
39 | | }; |
40 | | |
41 | | // infer the rest of the parameters |
42 | | // raw_row_length_from_width() will add +1 to the row length in bytes |
43 | | // to account for the first bit in the row indicating the filter, so subtract 1 |
44 | 0 | let bytes_per_row = raw_row_length_from_width(bit_depth, color_type, width) - 1; |
45 | 0 | let height = data.len() / bytes_per_row; |
46 | 0 | let total_bytes = bytes_per_row * height; |
47 | 0 | let data_to_encode = &data[..total_bytes]; |
48 | 0 |
|
49 | 0 | // perform the PNG encoding |
50 | 0 | let mut output: Vec<u8> = Vec::new(); |
51 | 0 | { // scoped so that we could return the Vec |
52 | 0 | let mut encoder = png::Encoder::new(&mut output, width, height as u32); |
53 | 0 | encoder.set_depth(bit_depth); |
54 | 0 | encoder.set_color(color_type); |
55 | 0 | encoder.set_filter(filter); |
56 | 0 | encoder.set_compression(compression); |
57 | 0 | if let ColorType::Indexed = color_type { |
58 | 0 | encoder.set_palette(palette) |
59 | 0 | } |
60 | | // write_header will return an error given invalid parameters, |
61 | | // such as height 0, or invalid color mode and bit depth combination |
62 | 0 | let mut writer = encoder.write_header().ok()?; |
63 | 0 | writer.write_image_data(data_to_encode).expect("Encoding failed"); |
64 | 0 | } |
65 | 0 | Some((data_to_encode, output)) |
66 | 0 | } |
67 | | |
68 | 0 | fn decode_png(data: &[u8]) -> (png::OutputInfo, Vec<u8>) { |
69 | 0 | let decoder = png::Decoder::new(data); |
70 | 0 | let mut reader = decoder.read_info().unwrap(); |
71 | 0 |
|
72 | 0 | let mut img_data = vec![0u8; reader.info().raw_bytes()]; |
73 | 0 |
|
74 | 0 | let info = reader.next_frame(&mut img_data).unwrap(); |
75 | 0 |
|
76 | 0 | (info, img_data) |
77 | 0 | } |
78 | | |
79 | | // copied from the `png` codebase because it's pub(crate) |
80 | 0 | fn raw_row_length_from_width(depth: BitDepth, color: ColorType, width: u32) -> usize { |
81 | 0 | let samples = width as usize * color.samples(); |
82 | 0 | 1 + match depth { |
83 | 0 | BitDepth::Sixteen => samples * 2, |
84 | 0 | BitDepth::Eight => samples, |
85 | 0 | subbyte => { |
86 | 0 | let samples_per_byte = 8 / subbyte as usize; |
87 | 0 | let whole = samples / samples_per_byte; |
88 | 0 | let fract = usize::from(samples % samples_per_byte > 0); |
89 | 0 | whole + fract |
90 | | } |
91 | | } |
92 | 0 | } |