diff options
author | pommicket <pommicket@gmail.com> | 2023-09-20 23:38:34 -0400 |
---|---|---|
committer | pommicket <pommicket@gmail.com> | 2023-09-20 23:38:34 -0400 |
commit | 521a88b66f0bd76c52fef0d84776478c0b0664d9 (patch) | |
tree | 0df450d897f6e3ab422f83b8a0ef54d769d023cf /src | |
parent | bac500bc1289d9bf2c61173bd170bc721406087a (diff) |
this could have resulted in debug-only panics for maliciously crafted images.
- add “impossible compressed size” check which slightly mitigates the
problem of a malicious image causing you to allocate a shitton of memory.
Diffstat (limited to 'src')
-rw-r--r-- | src/lib.rs | 14 |
1 files changed, 14 insertions, 0 deletions
@@ -53,6 +53,10 @@ pub enum Error { NoIdat, /// Adler-32 checksum doesn't check out (invalid PNG file) BadAdlerChecksum, + /// e.g. chunk is larger than 2GB, chunk goes past end of file (invalid PNG) + BadChunkSize, + /// compressed data cannot possibly be expanded to the full image because it's too small (invalid PNG) + CompressedSizeTooSmall, } #[cold] @@ -86,6 +90,8 @@ impl Display for Error { Self::NoIdat => write!(f, "missing IDAT chunk"), Self::BadNlen => write!(f, "LEN doesn't match NLEN"), Self::BadAdlerChecksum => write!(f, "bad adler-32 checksum"), + Self::BadChunkSize => write!(f, "bad chunk size"), + Self::CompressedSizeTooSmall => write!(f, "compressed data too small"), } } } @@ -857,6 +863,11 @@ pub fn decode_png_header(bytes: &[u8]) -> Result<ImageHeader> { color_type, length: 8 + ihdr_len, }; + // in the best-case scenario, each bit can decompress to 258 bytes + // (see DEFLATE RFC especially §3.2.5). + if hdr.decompressed_size() / (8 * 258) > bytes.len() { + return Err(Error::CompressedSizeTooSmall); + } Ok(hdr) } @@ -1215,6 +1226,9 @@ fn read_non_idat_chunks( ]) .try_into() .map_err(|_| Error::TooLargeForUsize)?; + if chunk_len > 0x7FFF_FFFF || chunk_len > reader.0.len() { + return Err(Error::BadChunkSize); + } let chunk_type = [ chunk_header[4], chunk_header[5], |