From d82854885ca833f016b763180c5de942fcc6db2d Mon Sep 17 00:00:00 2001 From: MrGunflame Date: Sat, 3 Feb 2024 00:19:46 +0100 Subject: [PATCH] Improve panic messages when providing invalid buffer sizes to `ImageEncoder`s (#2116) --- src/codecs/avif/encoder.rs | 9 +++++++-- src/codecs/bmp/encoder.rs | 11 +++++++++-- src/codecs/farbfeld.rs | 9 +++++++-- src/codecs/ico/encoder.rs | 9 +++++++-- src/codecs/jpeg/encoder.rs | 10 ++++++++-- src/codecs/openexr.rs | 9 +++++++-- src/codecs/png.rs | 9 +++++++-- src/codecs/pnm/encoder.rs | 9 +++++++-- src/codecs/qoi.rs | 9 +++++++-- src/codecs/tga/encoder.rs | 10 ++++++++-- src/codecs/tiff.rs | 10 ++++++++-- src/codecs/webp/encoder.rs | 10 ++++++++-- 12 files changed, 90 insertions(+), 24 deletions(-) diff --git a/src/codecs/avif/encoder.rs b/src/codecs/avif/encoder.rs index 48210cce89..900a10e648 100644 --- a/src/codecs/avif/encoder.rs +++ b/src/codecs/avif/encoder.rs @@ -98,6 +98,7 @@ impl ImageEncoder for AvifEncoder { /// The encoder currently requires all data to be RGBA8, it will be converted internally if /// necessary. When data is suitably aligned, i.e. u16 channels to two bytes, then the /// conversion may be more efficient. + #[track_caller] fn write_image( mut self, data: &[u8], @@ -105,9 +106,13 @@ impl ImageEncoder for AvifEncoder { height: u32, color: ColorType, ) -> ImageResult<()> { + let expected_buffer_len = + (width as u64 * height as u64).saturating_mul(color.bytes_per_pixel() as u64); assert_eq!( - (width as u64 * height as u64).saturating_mul(color.bytes_per_pixel() as u64), - data.len() as u64 + expected_buffer_len, + data.len() as u64, + "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", + data.len(), ); self.set_color(color); diff --git a/src/codecs/bmp/encoder.rs b/src/codecs/bmp/encoder.rs index 0f7e491779..c3e1ae08f4 100644 --- a/src/codecs/bmp/encoder.rs +++ b/src/codecs/bmp/encoder.rs @@ -27,6 +27,7 @@ impl<'a, W: Write + 'a> BmpEncoder<'a, W> { /// # Panics /// /// Panics if `width * height * c.bytes_per_pixel() != image.len()`. + #[track_caller] pub fn encode( &mut self, image: &[u8], @@ -43,6 +44,7 @@ impl<'a, W: Write + 'a> BmpEncoder<'a, W> { /// # Panics /// /// Panics if `width * height * c.bytes_per_pixel() != image.len()`. + #[track_caller] pub fn encode_with_palette( &mut self, image: &[u8], @@ -61,9 +63,13 @@ impl<'a, W: Write + 'a> BmpEncoder<'a, W> { ))); } + let expected_buffer_len = + (width as u64 * height as u64).saturating_mul(c.bytes_per_pixel() as u64); assert_eq!( - (width as u64 * height as u64).saturating_mul(c.bytes_per_pixel() as u64), - image.len() as u64 + expected_buffer_len, + image.len() as u64, + "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", + image.len(), ); let bmp_header_size = BITMAPFILEHEADER_SIZE; @@ -262,6 +268,7 @@ impl<'a, W: Write + 'a> BmpEncoder<'a, W> { } impl<'a, W: Write> ImageEncoder for BmpEncoder<'a, W> { + #[track_caller] fn write_image( mut self, buf: &[u8], diff --git a/src/codecs/farbfeld.rs b/src/codecs/farbfeld.rs index 3f2b923a86..b6dcd0565f 100644 --- a/src/codecs/farbfeld.rs +++ b/src/codecs/farbfeld.rs @@ -261,10 +261,14 @@ impl FarbfeldEncoder { /// # Panics /// /// Panics if `width * height * 8 != data.len()`. + #[track_caller] pub fn encode(self, data: &[u8], width: u32, height: u32) -> ImageResult<()> { + let expected_buffer_len = (width as u64 * height as u64).saturating_mul(8); assert_eq!( - (width as u64 * height as u64).saturating_mul(8), - data.len() as u64 + expected_buffer_len, + data.len() as u64, + "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", + data.len(), ); self.encode_impl(data, width, height)?; Ok(()) @@ -286,6 +290,7 @@ impl FarbfeldEncoder { } impl ImageEncoder for FarbfeldEncoder { + #[track_caller] fn write_image( self, buf: &[u8], diff --git a/src/codecs/ico/encoder.rs b/src/codecs/ico/encoder.rs index 155212d254..75d3116590 100644 --- a/src/codecs/ico/encoder.rs +++ b/src/codecs/ico/encoder.rs @@ -145,6 +145,7 @@ impl ImageEncoder for IcoEncoder { /// native endian. /// /// WARNING: In image 0.23.14 and earlier this method erroneously expected buf to be in big endian. + #[track_caller] fn write_image( self, buf: &[u8], @@ -152,9 +153,13 @@ impl ImageEncoder for IcoEncoder { height: u32, color_type: ColorType, ) -> ImageResult<()> { + let expected_buffer_len = + (width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64); assert_eq!( - (width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64), - buf.len() as u64 + expected_buffer_len, + buf.len() as u64, + "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", + buf.len(), ); let image = IcoFrame::as_png(buf, width, height, color_type)?; diff --git a/src/codecs/jpeg/encoder.rs b/src/codecs/jpeg/encoder.rs index 8544da0ad8..21f12a13b4 100644 --- a/src/codecs/jpeg/encoder.rs +++ b/src/codecs/jpeg/encoder.rs @@ -440,6 +440,7 @@ impl JpegEncoder { /// # Panics /// /// Panics if `width * height * color_type.bytes_per_pixel() != image.len()`. + #[track_caller] pub fn encode( &mut self, image: &[u8], @@ -447,9 +448,13 @@ impl JpegEncoder { height: u32, color_type: ColorType, ) -> ImageResult<()> { + let expected_buffer_len = + (width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64); assert_eq!( - (width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64), - image.len() as u64 + expected_buffer_len, + image.len() as u64, + "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", + image.len(), ); match color_type { @@ -663,6 +668,7 @@ impl JpegEncoder { } impl ImageEncoder for JpegEncoder { + #[track_caller] fn write_image( mut self, buf: &[u8], diff --git a/src/codecs/openexr.rs b/src/codecs/openexr.rs index bd7e3cbc77..b28955e63c 100644 --- a/src/codecs/openexr.rs +++ b/src/codecs/openexr.rs @@ -352,6 +352,7 @@ where /// /// Assumes the writer is buffered. In most cases, you should wrap your writer in a `BufWriter` /// for best performance. + #[track_caller] fn write_image( self, buf: &[u8], @@ -359,9 +360,13 @@ where height: u32, color_type: ColorType, ) -> ImageResult<()> { + let expected_buffer_len = + (width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64); assert_eq!( - (width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64), - buf.len() as u64 + expected_buffer_len, + buf.len() as u64, + "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", + buf.len(), ); write_buffer(self.0, buf, width, height, color_type) diff --git a/src/codecs/png.rs b/src/codecs/png.rs index d92ee44970..39e1e5cb97 100644 --- a/src/codecs/png.rs +++ b/src/codecs/png.rs @@ -699,6 +699,7 @@ impl ImageEncoder for PngEncoder { /// For color types with 16-bit per channel or larger, the contents of `buf` should be in /// native endian. PngEncoder will automatically convert to big endian as required by the /// underlying PNG format. + #[track_caller] fn write_image( self, buf: &[u8], @@ -709,9 +710,13 @@ impl ImageEncoder for PngEncoder { use byteorder::{BigEndian, ByteOrder, NativeEndian}; use ColorType::*; + let expected_bufffer_len = + (width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64); assert_eq!( - (width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64), - buf.len() as u64 + expected_bufffer_len, + buf.len() as u64, + "Invalid buffer length: expected {expected_bufffer_len} got {} for {width}x{height} image", + buf.len(), ); // PNG images are big endian. For 16 bit per channel and larger types, diff --git a/src/codecs/pnm/encoder.rs b/src/codecs/pnm/encoder.rs index 70ea010a95..87c62f730a 100644 --- a/src/codecs/pnm/encoder.rs +++ b/src/codecs/pnm/encoder.rs @@ -289,6 +289,7 @@ impl PnmEncoder { } impl ImageEncoder for PnmEncoder { + #[track_caller] fn write_image( mut self, buf: &[u8], @@ -296,9 +297,13 @@ impl ImageEncoder for PnmEncoder { height: u32, color_type: ColorType, ) -> ImageResult<()> { + let expected_buffer_len = + (width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64); assert_eq!( - (width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64), - buf.len() as u64 + expected_buffer_len, + buf.len() as u64, + "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", + buf.len(), ); self.encode(buf, width, height, color_type) diff --git a/src/codecs/qoi.rs b/src/codecs/qoi.rs index 615319a791..2a1627e486 100644 --- a/src/codecs/qoi.rs +++ b/src/codecs/qoi.rs @@ -63,6 +63,7 @@ impl QoiEncoder { } impl ImageEncoder for QoiEncoder { + #[track_caller] fn write_image( mut self, buf: &[u8], @@ -77,9 +78,13 @@ impl ImageEncoder for QoiEncoder { ))); } + let expected_buffer_len = + (width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64); assert_eq!( - (width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64), - buf.len() as u64 + expected_buffer_len, + buf.len() as u64, + "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", + buf.len(), ); // Encode data in QOI diff --git a/src/codecs/tga/encoder.rs b/src/codecs/tga/encoder.rs index f1f3e1325e..171dde7e30 100644 --- a/src/codecs/tga/encoder.rs +++ b/src/codecs/tga/encoder.rs @@ -156,6 +156,7 @@ impl TgaEncoder { /// # Panics /// /// Panics if `width * height * color_type.bytes_per_pixel() != data.len()`. + #[track_caller] pub fn encode( mut self, buf: &[u8], @@ -163,9 +164,13 @@ impl TgaEncoder { height: u32, color_type: ColorType, ) -> ImageResult<()> { + let expected_buffer_len = + (width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64); assert_eq!( - (width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64), - buf.len() as u64 + expected_buffer_len, + buf.len() as u64, + "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", + buf.len(), ); // Validate dimensions. @@ -226,6 +231,7 @@ impl TgaEncoder { } impl ImageEncoder for TgaEncoder { + #[track_caller] fn write_image( self, buf: &[u8], diff --git a/src/codecs/tiff.rs b/src/codecs/tiff.rs index 6ad356230a..fd7dca74dc 100644 --- a/src/codecs/tiff.rs +++ b/src/codecs/tiff.rs @@ -345,10 +345,15 @@ impl TiffEncoder { /// # Panics /// /// Panics if `width * height * color_type.bytes_per_pixel() != data.len()`. + #[track_caller] pub fn encode(self, data: &[u8], width: u32, height: u32, color: ColorType) -> ImageResult<()> { + let expected_buffer_len = + (width as u64 * height as u64).saturating_mul(color.bytes_per_pixel() as u64); assert_eq!( - (width as u64 * height as u64).saturating_mul(color.bytes_per_pixel() as u64), - data.len() as u64 + expected_buffer_len, + data.len() as u64, + "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", + data.len(), ); let mut encoder = @@ -394,6 +399,7 @@ impl TiffEncoder { } impl ImageEncoder for TiffEncoder { + #[track_caller] fn write_image( self, buf: &[u8], diff --git a/src/codecs/webp/encoder.rs b/src/codecs/webp/encoder.rs index cf3c4a0617..8fb125b65b 100644 --- a/src/codecs/webp/encoder.rs +++ b/src/codecs/webp/encoder.rs @@ -704,10 +704,15 @@ impl WebPEncoder { /// # Panics /// /// Panics if `width * height * color.bytes_per_pixel() != data.len()`. + #[track_caller] pub fn encode(self, data: &[u8], width: u32, height: u32, color: ColorType) -> ImageResult<()> { + let expected_buffer_len = + (width as u64 * height as u64).saturating_mul(color.bytes_per_pixel() as u64); assert_eq!( - (width as u64 * height as u64).saturating_mul(color.bytes_per_pixel() as u64), - data.len() as u64 + expected_buffer_len, + data.len() as u64, + "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", + data.len(), ); if let WebPQuality(Quality::Lossless) = self.quality { @@ -722,6 +727,7 @@ impl WebPEncoder { } impl ImageEncoder for WebPEncoder { + #[track_caller] fn write_image( self, buf: &[u8],