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
|
use crate::{decode_png, decode_png_header, ColorType, Error};
use std::{fs, sync::Mutex};
#[derive(Debug)]
enum Flaw {
ErrorFromValidPNG,
NoErrorFromInvalidPNG,
DecodedMismatch,
ConvertedMismatch,
}
impl core::fmt::Display for Flaw {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
Self::ErrorFromValidPNG => write!(f, "decoding valid PNG gives error"),
Self::NoErrorFromInvalidPNG => write!(f, "decoding invalid PNG gives no error"),
Self::DecodedMismatch => write!(f, "image incorrectly decoded"),
Self::ConvertedMismatch => write!(f, "image incorrectly converted"),
}
}
}
const LARGE_BUF: Mutex<Vec<u8>> = Mutex::new(vec![]);
fn test_bytes(bytes: &[u8]) -> Result<(), Flaw> {
let decoder = png::Decoder::new(bytes);
let mut is_valid = true;
if let Ok(mut reader) = decoder.read_info() {
let mut png_buf = vec![0; reader.output_buffer_size()];
if let Ok(png_header) = reader.next_frame(&mut png_buf) {
let png_bytes = &png_buf[..png_header.buffer_size()];
let mini_header = match decode_png_header(bytes) {
Ok(h) => h,
Err(Error::UnsupportedInterlace) => return Ok(()),
Err(_) => return Err(Flaw::ErrorFromValidPNG),
};
let mut mini_buf = vec![0; mini_header.required_bytes_rgba8bpc()];
let mut image =
decode_png(bytes, &mut mini_buf).map_err(|_| Flaw::ErrorFromValidPNG)?;
let mini_bytes = image.pixels();
if png_bytes != mini_bytes {
return Err(Flaw::DecodedMismatch);
}
let (_, mut data) = png_decoder::decode(bytes).unwrap();
if matches!(image.color_type(), ColorType::Gray | ColorType::Rgb) {
// pretend there's no tRNS chunk.
// there shouldnt be one who the fucks stupid idea was that.
for i in 0..data.len() / 4 {
data[4 * i + 3] = 255;
}
}
image.convert_to_rgba8bpc().unwrap();
if data != image.pixels() {
return Err(Flaw::ConvertedMismatch);
}
} else {
is_valid = false;
}
} else {
is_valid = false;
}
if !is_valid && decode_png(bytes, &mut LARGE_BUF.lock().unwrap()).is_ok() {
return Err(Flaw::NoErrorFromInvalidPNG);
}
Ok(())
}
fn test_images_in_dir(dir: &str) {
for entry in fs::read_dir(dir).unwrap() {
let entry = entry.unwrap();
let r#type = entry.file_type().unwrap();
let path = entry.path().to_str().unwrap().to_string();
if r#type.is_file() {
if let Err(flaw) = test_bytes(&std::fs::read(&path).unwrap()) {
panic!("flaw for file {path}: {flaw}");
}
} else if r#type.is_dir() {
test_images_in_dir(&path);
}
println!("{path} ... \x1b[32mok\x1b[0m");
}
}
#[test]
fn test_images() {
*LARGE_BUF.lock().unwrap() = vec![0; 10 << 20];
test_images_in_dir("test");
}
#[test]
fn test_bad_png() {
let mut data = &b"hello"[..];
let err = decode_png_header(&mut data).unwrap_err();
assert!(matches!(err, Error::NotPng));
}
#[test]
fn test_buffer_too_small() {
let png = &include_bytes!("../test/ouroboros.png")[..];
let mut buffer = [0; 128];
let err = decode_png(png, &mut buffer[..]).unwrap_err();
assert!(matches!(err, Error::BufferTooSmall));
}
|