summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md6
-rw-r--r--Cargo.toml2
-rw-r--r--src/lib.rs14
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.
diff --git a/Cargo.toml b/Cargo.toml
index 40ad49e..0a0916c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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]
diff --git a/src/lib.rs b/src/lib.rs
index b17c413..9d61021 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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],