Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make ImageEncoder::write_image assert that dimensions are valid #2008

Merged
merged 3 commits into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/codecs/avif/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ impl<W: Write> ImageEncoder for AvifEncoder<W> {
height: u32,
color: ColorType,
) -> ImageResult<()> {
assert_eq!(
(width as u64 * height as u64).saturating_mul(color.bytes_per_pixel() as u64),
data.len() as u64
);

self.set_color(color);
// `ravif` needs strongly typed data so let's convert. We can either use a temporarily
// owned version in our own buffer or zero-copy if possible by using the input buffer.
Expand Down
21 changes: 16 additions & 5 deletions src/codecs/bmp/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ impl<'a, W: Write + 'a> BmpEncoder<'a, W> {
BmpEncoder { writer: w }
}

/// Encodes the image ```image```
/// that has dimensions ```width``` and ```height```
/// and ```ColorType``` ```c```.
/// Encodes the image `image` that has dimensions `width` and `height` and `ColorType` `c`.
///
/// # Panics
///
/// Panics if `width * height * c.bytes_per_pixel() != image.len()`.
pub fn encode(
&mut self,
image: &[u8],
Expand All @@ -35,8 +37,12 @@ impl<'a, W: Write + 'a> BmpEncoder<'a, W> {
self.encode_with_palette(image, width, height, c, None)
}

/// Same as ```encode```, but allow a palette to be passed in.
/// The ```palette``` is ignored for color types other than Luma/Luma-with-alpha.
/// Same as `encode`, but allow a palette to be passed in. The `palette` is ignored for color
/// types other than Luma/Luma-with-alpha.
///
/// # Panics
///
/// Panics if `width * height * c.bytes_per_pixel() != image.len()`.
pub fn encode_with_palette(
&mut self,
image: &[u8],
Expand All @@ -55,6 +61,11 @@ impl<'a, W: Write + 'a> BmpEncoder<'a, W> {
)));
}

assert_eq!(
(width as u64 * height as u64).saturating_mul(c.bytes_per_pixel() as u64),
image.len() as u64
);

let bmp_header_size = BITMAPFILEHEADER_SIZE;

let (dib_header_size, written_pixel_size, palette_color_count) =
Expand Down
11 changes: 9 additions & 2 deletions src/codecs/farbfeld.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,16 @@ impl<W: Write> FarbfeldEncoder<W> {
FarbfeldEncoder { w: buffered_writer }
}

/// Encodes the image ```data``` (native endian)
/// that has dimensions ```width``` and ```height```
/// Encodes the image `data` (native endian) that has dimensions `width` and `height`.
///
/// # Panics
///
/// Panics if `width * height * 8 != data.len()`.
pub fn encode(self, data: &[u8], width: u32, height: u32) -> ImageResult<()> {
assert_eq!(
(width as u64 * height as u64).saturating_mul(8),
data.len() as u64
);
self.encode_impl(data, width, height)?;
Ok(())
}
Expand Down
5 changes: 5 additions & 0 deletions src/codecs/ico/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ impl<W: Write> ImageEncoder for IcoEncoder<W> {
height: u32,
color_type: ColorType,
) -> ImageResult<()> {
assert_eq!(
(width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64),
buf.len() as u64
);

let image = IcoFrame::as_png(buf, width, height, color_type)?;
self.encode_images(&[image])
}
Expand Down
9 changes: 9 additions & 0 deletions src/codecs/jpeg/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,13 +436,22 @@ impl<W: Write> JpegEncoder<W> {
/// and ```ColorType``` ```c```
///
/// The Image in encoded with subsampling ratio 4:2:2
///
/// # Panics
///
/// Panics if `width * height * color_type.bytes_per_pixel() != image.len()`.
pub fn encode(
&mut self,
image: &[u8],
width: u32,
height: u32,
color_type: ColorType,
) -> ImageResult<()> {
assert_eq!(
(width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64),
image.len() as u64
);

match color_type {
ColorType::L8 => {
let image: ImageBuffer<Luma<_>, _> =
Expand Down
10 changes: 7 additions & 3 deletions src/codecs/openexr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,16 +355,20 @@ where
{
/// Writes the complete image.
///
/// Returns an Error if it has an invalid length.
/// Assumes the writer is buffered. In most cases,
/// you should wrap your writer in a `BufWriter` for best performance.
/// Assumes the writer is buffered. In most cases, you should wrap your writer in a `BufWriter`
/// for best performance.
fn write_image(
self,
buf: &[u8],
width: u32,
height: u32,
color_type: ColorType,
) -> ImageResult<()> {
assert_eq!(
(width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64),
buf.len() as u64
);

write_buffer(self.0, buf, width, height, color_type)
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/codecs/png.rs
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,11 @@ impl<W: Write> ImageEncoder for PngEncoder<W> {
use byteorder::{BigEndian, ByteOrder, NativeEndian};
use ColorType::*;

assert_eq!(
(width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64),
buf.len() as u64
);

// PNG images are big endian. For 16 bit per channel and larger types,
// the buffer may need to be reordered to big endian per the
// contract of `write_image`.
Expand Down
5 changes: 5 additions & 0 deletions src/codecs/pnm/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,11 @@ impl<W: Write> ImageEncoder for PnmEncoder<W> {
height: u32,
color_type: ColorType,
) -> ImageResult<()> {
assert_eq!(
(width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64),
buf.len() as u64
);

self.encode(buf, width, height, color_type)
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/codecs/qoi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ impl<W: Write> ImageEncoder for QoiEncoder<W> {
)));
}

assert_eq!(
(width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64),
buf.len() as u64
);

// Encode data in QOI
let data = qoi::encode_to_vec(buf, width, height).map_err(encoding_error)?;

Expand Down
9 changes: 9 additions & 0 deletions src/codecs/tga/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,22 @@ impl<W: Write> TgaEncoder<W> {
///
/// The dimensions of the image must be between 0 and 65535 (inclusive) or
/// an error will be returned.
///
/// # Panics
///
/// Panics if `width * height * color_type.bytes_per_pixel() != data.len()`.
pub fn encode(
mut self,
buf: &[u8],
width: u32,
height: u32,
color_type: ColorType,
) -> ImageResult<()> {
assert_eq!(
(width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64),
buf.len() as u64
);

// Validate dimensions.
let width = u16::try_from(width)
.map_err(|_| ImageError::from(EncoderError::WidthInvalid(width)))?;
Expand Down
9 changes: 9 additions & 0 deletions src/codecs/tiff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,16 @@ impl<W: Write + Seek> TiffEncoder<W> {
/// Encodes the image `image` that has dimensions `width` and `height` and `ColorType` `c`.
///
/// 16-bit types assume the buffer is native endian.
///
/// # Panics
///
/// Panics if `width * height * color_type.bytes_per_pixel() != data.len()`.
pub fn encode(self, data: &[u8], width: u32, height: u32, color: ColorType) -> ImageResult<()> {
assert_eq!(
(width as u64 * height as u64).saturating_mul(color.bytes_per_pixel() as u64),
data.len() as u64
);

let mut encoder =
tiff::encoder::TiffEncoder::new(self.w).map_err(ImageError::from_tiff_encode)?;
match color {
Expand Down
22 changes: 20 additions & 2 deletions src/codecs/webp/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,16 @@ impl<W: Write> WebPEncoder<W> {
/// Encode image data with the indicated color type.
///
/// The encoder requires image data be Rgb8 or Rgba8.
///
/// # Panics
///
/// Panics if `width * height * color.bytes_per_pixel() != data.len()`.
pub fn encode(self, data: &[u8], width: u32, height: u32, color: ColorType) -> ImageResult<()> {
assert_eq!(
(width as u64 * height as u64).saturating_mul(color.bytes_per_pixel() as u64),
data.len() as u64
);

if let WebPQuality(Quality::Lossless) = self.quality {
self.encode_lossless(data, width, height, color)
} else {
Expand Down Expand Up @@ -804,15 +813,24 @@ mod native_tests {

fn fuzz_webp_no_panic(data: Vec<u8>, width: u8, height: u8, quality: u8) -> bool {
// Check random (usually invalid) parameters do not panic.

if data.len() < width as usize * height as usize * 4 {
return true;
}

let mut buffer = Vec::<u8>::new();
for color in [ColorType::Rgb8, ColorType::Rgba8] {
for webp_quality in [WebPQuality::lossless(), #[allow(deprecated)] WebPQuality::lossy(quality)] {
buffer.clear();
#[allow(deprecated)]
let encoder = WebPEncoder::new_with_quality(&mut buffer, webp_quality);
// Ignore errors.
let _ = encoder
.write_image(&data, width as u32, height as u32, color);
let _ = encoder.write_image(
&data[..width as usize * height as usize * color.bytes_per_pixel() as usize],
width as u32,
height as u32,
color,
);
}
}
true
Expand Down
4 changes: 4 additions & 0 deletions src/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,10 @@ pub trait ImageEncoder {
///
/// See also `ImageDecoder::read_image` which reads byte buffers into
/// native endian.
///
/// # Panics
///
/// Panics if `width * height * color_type.bytes_per_pixel() != buf.len()`.
fn write_image(
self,
buf: &[u8],
Expand Down
Loading