diff options
-rw-r--r-- | CHANGELOG.md | 6 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | src/lib.rs | 14 |
3 files changed, 21 insertions, 1 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..265e560 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +## 0.1.1 + +- add overflow check for chunk length. + 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. @@ -6,7 +6,7 @@ categories = ["encoding", "graphics", "multimedia::images", "no-std::no-alloc"] keywords = ["png", "decoder", "image", "no_std", "no_alloc"] exclude = ["/test/large", "/benches/large.png"] license = "0BSD" -version = "0.1.0" +version = "0.1.1" edition = "2021" [dev-dependencies] @@ -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], |