Skip to content

Commit

Permalink
Merge pull request #115 from birktj/update_limits
Browse files Browse the repository at this point in the history
Update limits to be byte based
  • Loading branch information
birktj authored May 4, 2019
2 parents 51f0338 + eddef82 commit d81f117
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 24 deletions.
46 changes: 24 additions & 22 deletions src/decoder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,16 @@ impl OutputInfo {
}

#[derive(Clone, Copy, Debug)]
/// Limits on the resources the `Decoder` is allowed too use
pub struct Limits {
/// max number of pixels: `width * height` (default: 67M = 2<sup>26</sup>)
pub pixels: u64,
/// maximum number of bytes the decoder is allowed to allocate, default is 64Mib
pub bytes: usize,
}

impl Default for Limits {
fn default() -> Limits {
Limits {
pixels: 1 << 26,
bytes: 1024*1024*64,
}
}
}
Expand All @@ -73,7 +74,7 @@ pub struct Decoder<R: Read> {
r: R,
/// Output transformations
transform: Transformations,
/// Images that are considered too big
/// Limits on resources the Decoder is allowed to use
limits: Limits,
}

Expand All @@ -90,19 +91,19 @@ impl<R: Read> Decoder<R> {
}
}

/// Images that are considered too big
/// Limit resource usage
///
/// ```
/// use std::fs::File;
/// use png::{Decoder, Limits};
/// // This image is 32x32 pixels, so it's more than four pixels in size.
/// // This image is 32x32 pixels, so the deocder will allocate more than four bytes
/// let mut limits = Limits::default();
/// limits.pixels = 4;
/// limits.bytes = 4;
/// let mut decoder = Decoder::new_with_limits(File::open("tests/pngsuite/basi0g01.png").unwrap(), limits);
/// assert!(decoder.read_info().is_err());
/// // This image is 32x32 pixels, so it's exactly 1024 pixels in size.
/// // This image is 32x32 pixels, so the decoder will allocate less than 10Kib
/// let mut limits = Limits::default();
/// limits.pixels = 1024;
/// limits.bytes = 10*1024;
/// let mut decoder = Decoder::new_with_limits(File::open("tests/pngsuite/basi0g01.png").unwrap(), limits);
/// assert!(decoder.read_info().is_ok());
/// ```
Expand All @@ -112,7 +113,7 @@ impl<R: Read> Decoder<R> {

/// Reads all meta data until the first IDAT chunk
pub fn read_info(self) -> Result<(OutputInfo, Reader<R>), DecodingError> {
let mut r = Reader::new(self.r, StreamingDecoder::new(), self.transform);
let mut r = Reader::new(self.r, StreamingDecoder::new(), self.transform, self.limits);
r.init()?;
let (ct, bits) = r.output_color_type();
let info = {
Expand All @@ -125,12 +126,6 @@ impl<R: Read> Decoder<R> {
line_size: r.output_line_size(info.width),
}
};
let (width, height, pixels) = (info.width as u64, info.height as u64, self.limits.pixels);
if width.checked_mul(height).map(|p| p > pixels).unwrap_or(true) {
// DecodingError::Other is used for backwards compatibility.
// In the next major version, add a variant for this.
return Err(DecodingError::Other(borrow::Cow::Borrowed("pixels limit exceeded")));
}
Ok((info, r))
}
}
Expand Down Expand Up @@ -187,7 +182,8 @@ pub struct Reader<R: Read> {
/// Output transformations
transform: Transformations,
/// Processed line
processed: Vec<u8>
processed: Vec<u8>,
limits: Limits,
}

macro_rules! get_info(
Expand All @@ -198,7 +194,7 @@ macro_rules! get_info(

impl<R: Read> Reader<R> {
/// Creates a new PNG reader
fn new(r: R, d: StreamingDecoder, t: Transformations) -> Reader<R> {
fn new(r: R, d: StreamingDecoder, t: Transformations, limits: Limits) -> Reader<R> {
Reader {
decoder: ReadDecoder {
reader: BufReader::with_capacity(CHUNCK_BUFFER_SIZE, r),
Expand All @@ -211,7 +207,8 @@ impl<R: Read> Reader<R> {
prev: Vec::new(),
current: Vec::new(),
transform: t,
processed: Vec::new()
processed: Vec::new(),
limits,
}
}

Expand Down Expand Up @@ -243,7 +240,7 @@ impl<R: Read> Reader<R> {
self.adam7 = Some(utils::Adam7Iterator::new(info.width, info.height))
}
}
self.allocate_out_buf();
self.allocate_out_buf()?;
self.prev = vec![0; self.rowlen];
Ok(())
}
Expand Down Expand Up @@ -425,9 +422,14 @@ impl<R: Read> Reader<R> {
len + match extra { 0 => 0, _ => 1 }
}

fn allocate_out_buf(&mut self) {
fn allocate_out_buf(&mut self) -> Result<(), DecodingError> {
let width = get_info!(self).width;
self.processed = vec![0; self.line_size(width)]
let bytes = self.limits.bytes;
if bytes < self.line_size(width) {
return Err(DecodingError::LimitsExceeded);
}
self.processed = vec![0; self.line_size(width)];
Ok(())
}

/// Returns the next raw row of the image
Expand Down
6 changes: 4 additions & 2 deletions src/decoder/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ pub enum DecodingError {
chunk: ChunkType
},
Other(Cow<'static, str>),
CorruptFlateStream
CorruptFlateStream,
LimitsExceeded,
}

impl error::Error for DecodingError {
Expand All @@ -80,7 +81,8 @@ impl error::Error for DecodingError {
Format(ref desc) | Other(ref desc) => &desc,
InvalidSignature => "invalid signature",
CrcMismatch { .. } => "CRC error",
CorruptFlateStream => "compressed data stream corrupted"
CorruptFlateStream => "compressed data stream corrupted",
LimitsExceeded => "limits are exceeded"
}
}
}
Expand Down

0 comments on commit d81f117

Please sign in to comment.