Handle files with invalid filters (#410)

The bad filter case is reachable, and can happen when the PNG file is corrupted.
This commit is contained in:
Kornel 2021-07-12 04:07:18 +01:00 committed by GitHub
parent 286aaffa31
commit ea75f6ccc1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 15 additions and 6 deletions

View file

@ -1,3 +1,5 @@
use crate::error::PngError;
pub fn filter_line(filter: u8, bpp: usize, data: &[u8], last_line: &[u8], buf: &mut Vec<u8>) { pub fn filter_line(filter: u8, bpp: usize, data: &[u8], last_line: &[u8], buf: &mut Vec<u8>) {
assert!(data.len() >= bpp); assert!(data.len() >= bpp);
assert!(last_line.is_empty() || data.len() == last_line.len()); assert!(last_line.is_empty() || data.len() == last_line.len());
@ -65,7 +67,13 @@ pub fn filter_line(filter: u8, bpp: usize, data: &[u8], last_line: &[u8], buf: &
} }
} }
pub fn unfilter_line(filter: u8, bpp: usize, data: &[u8], last_line: &[u8], buf: &mut Vec<u8>) { pub fn unfilter_line(
filter: u8,
bpp: usize,
data: &[u8],
last_line: &[u8],
buf: &mut Vec<u8>,
) -> Result<(), PngError> {
buf.clear(); buf.clear();
buf.reserve(data.len()); buf.reserve(data.len());
assert!(data.len() >= bpp); assert!(data.len() >= bpp);
@ -114,8 +122,9 @@ pub fn unfilter_line(filter: u8, bpp: usize, data: &[u8], last_line: &[u8], buf:
); );
} }
} }
_ => unreachable!(), _ => return Err(PngError::InvalidData),
} }
Ok(())
} }
fn paeth_predictor(a: u8, b: u8, c: u8) -> u8 { fn paeth_predictor(a: u8, b: u8, c: u8) -> u8 {

View file

@ -136,7 +136,7 @@ impl PngData {
transparency_pixel, transparency_pixel,
aux_headers, aux_headers,
}; };
raw.data = raw.unfilter_image(); raw.data = raw.unfilter_image()?;
// Return the PngData // Return the PngData
Ok(Self { Ok(Self {
idat_data: idat_headers, idat_data: idat_headers,
@ -281,7 +281,7 @@ impl PngImage {
} }
/// Reverse all filters applied on the image, returning an unfiltered IDAT bytestream /// Reverse all filters applied on the image, returning an unfiltered IDAT bytestream
fn unfilter_image(&self) -> Vec<u8> { fn unfilter_image(&self) -> Result<Vec<u8>, PngError> {
let mut unfiltered = Vec::with_capacity(self.data.len()); let mut unfiltered = Vec::with_capacity(self.data.len());
let bpp = ((self.ihdr.bit_depth.as_u8() * self.channels_per_pixel() + 7) / 8) as usize; let bpp = ((self.ihdr.bit_depth.as_u8() * self.channels_per_pixel() + 7) / 8) as usize;
let mut last_line: Vec<u8> = Vec::new(); let mut last_line: Vec<u8> = Vec::new();
@ -293,13 +293,13 @@ impl PngImage {
last_pass = line.pass; last_pass = line.pass;
} }
last_line.resize(line.data.len(), 0); last_line.resize(line.data.len(), 0);
unfilter_line(line.filter, bpp, line.data, &last_line, &mut unfiltered_buf); unfilter_line(line.filter, bpp, line.data, &last_line, &mut unfiltered_buf)?;
unfiltered.push(0); unfiltered.push(0);
unfiltered.extend_from_slice(&unfiltered_buf); unfiltered.extend_from_slice(&unfiltered_buf);
std::mem::swap(&mut last_line, &mut unfiltered_buf); std::mem::swap(&mut last_line, &mut unfiltered_buf);
unfiltered_buf.clear(); unfiltered_buf.clear();
} }
unfiltered Ok(unfiltered)
} }
/// Apply the specified filter type to all rows in the image /// Apply the specified filter type to all rows in the image