Skip to content

Commit

Permalink
Check for integer overflows in RawBmp::parse (#32)
Browse files Browse the repository at this point in the history
* Check for integer overflows in RawBmp::parse

* Don't allow images with zero width or height

* Check for negative width and add tests

* Reformat code
  • Loading branch information
rfuest committed Sep 30, 2022
1 parent 58c8e3f commit c64e35a
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 5 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -23,6 +23,10 @@
- [#28](https://github.com/embedded-graphics/tinybmp/pull/28) `Bpp::bits`, `RawBmp::image_data`, `RawBmp::header`, and `RawPixel::new` are now `const`.
- [#28](https://github.com/embedded-graphics/tinybmp/pull/28) BMP files with incomplete image data are now detected by `Bmp::from_slice`.

### Fixed

- [#32](https://github.com/embedded-graphics/tinybmp/pull/32) Report error for images with `width <= 0` or `height == 0` instead of causing a panic.

## [0.3.3] - 2022-04-18

### Fixed
Expand Down
8 changes: 6 additions & 2 deletions src/header/dib_header.rs
Expand Up @@ -48,7 +48,7 @@ impl DibHeader {
};

// Fields common to all DIB variants
let (dib_header_data, image_width) = le_u32(dib_header_data)?;
let (dib_header_data, image_width) = le_i32(dib_header_data)?;
let (dib_header_data, image_height) = le_i32(dib_header_data)?;
let (dib_header_data, _color_planes) = le_u16(dib_header_data)?;
let (dib_header_data, bpp) = Bpp::parse(dib_header_data)?;
Expand Down Expand Up @@ -91,6 +91,10 @@ impl DibHeader {
colors_used
};

if image_width <= 0 || image_height == 0 {
return Err(ParseError::InvalidImageDimensions);
}

let row_order = if image_height < 0 {
RowOrder::TopDown
} else {
Expand All @@ -101,7 +105,7 @@ impl DibHeader {
input,
Self {
header_type,
image_size: Size::new(image_width, image_height.unsigned_abs()),
image_size: Size::new(image_width.unsigned_abs(), image_height.unsigned_abs()),
image_data_len,
bpp,
channel_masks,
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Expand Up @@ -365,6 +365,9 @@ pub enum ParseError {

/// Unsupported channel masks.
UnsupportedChannelMasks,

/// Invalid image dimensions.
InvalidImageDimensions,
}

#[cfg(test)]
Expand Down
17 changes: 14 additions & 3 deletions src/raw_bmp.rs
Expand Up @@ -44,10 +44,21 @@ impl<'a> RawBmp<'a> {

let color_type = ColorType::from_header(&header)?;

let data_length = header.bytes_per_row() * header.image_size.height as usize;

let height = header
.image_size
.height
.try_into()
.map_err(|_| ParseError::InvalidImageDimensions)?;

let data_length = header
.bytes_per_row()
.checked_mul(height)
.ok_or(ParseError::InvalidImageDimensions)?;

// The `get` is split into two calls to prevent an possible integer overflow.
let image_data = &bytes
.get(header.image_data_start..header.image_data_start + data_length)
.get(header.image_data_start..)
.and_then(|bytes| bytes.get(..data_length))
.ok_or(ParseError::UnexpectedEndOfFile)?;

Ok(Self {
Expand Down
Binary file added tests/error-height-0.bmp
Binary file not shown.
Binary file added tests/error-width-0.bmp
Binary file not shown.
Binary file added tests/error-width-negative.bmp
Binary file not shown.
26 changes: 26 additions & 0 deletions tests/errors.rs
@@ -0,0 +1,26 @@
use embedded_graphics::pixelcolor::Rgb888;
use tinybmp::{Bmp, ParseError};

#[test]
fn zero_width() {
assert_eq!(
Bmp::<Rgb888>::from_slice(include_bytes!("error-width-0.bmp")),
Err(ParseError::InvalidImageDimensions)
);
}

#[test]
fn negative_width() {
assert_eq!(
Bmp::<Rgb888>::from_slice(include_bytes!("error-width-negative.bmp")),
Err(ParseError::InvalidImageDimensions)
);
}

#[test]
fn zero_height() {
assert_eq!(
Bmp::<Rgb888>::from_slice(include_bytes!("error-height-0.bmp")),
Err(ParseError::InvalidImageDimensions)
);
}

0 comments on commit c64e35a

Please sign in to comment.