From e904ec51c7637bfaa7abe9d255854ae62cfdd75c Mon Sep 17 00:00:00 2001 From: fedotoff Date: Fri, 2 Sep 2022 01:31:30 +0300 Subject: [PATCH 1/3] Fix unbound allocation in fuzzer_script_exr --- fuzz/fuzzers/fuzzer_script_exr.rs | 53 ++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/fuzz/fuzzers/fuzzer_script_exr.rs b/fuzz/fuzzers/fuzzer_script_exr.rs index af058dc763..6edbd84894 100644 --- a/fuzz/fuzzers/fuzzer_script_exr.rs +++ b/fuzz/fuzzers/fuzzer_script_exr.rs @@ -1,28 +1,41 @@ #![no_main] -#[macro_use] extern crate libfuzzer_sys; +#[macro_use] +extern crate libfuzzer_sys; extern crate image; -use std::io::Cursor; -use image::ImageResult; use image::codecs::openexr::*; -use std::io::Seek; -use std::convert::TryFrom; +use image::ColorType; use image::ImageDecoder; use image::ImageEncoder; -use image::ColorType; -use std::io::Write; +use image::ImageResult; +use std::convert::TryFrom; +use std::io::Cursor; use std::io::Read; +use std::io::Seek; +use std::io::SeekFrom; +use std::io::Write; // "just dont panic" fn roundtrip(bytes: &[u8]) -> ImageResult<()> { - /// Read the file from the specified path into an `Rgba32FImage`. // TODO this method should probably already exist in the main image crate - fn read_as_rgba_byte_image(read: impl Read + Seek) -> ImageResult<(u32,u32,Vec)> { + fn read_as_rgba_byte_image(read: &mut (impl Read + Seek)) -> ImageResult<(u32, u32, Vec)> { + let current_pos = read.stream_position()?; + let total_size = read.seek(SeekFrom::End(0))?; + let _ = read.seek(SeekFrom::Start(current_pos))?; + let decoder = OpenExrDecoder::with_alpha_preference(read, Some(true))?; let (width, height) = decoder.dimensions(); - let mut buffer = vec![0; usize::try_from(decoder.total_bytes()).unwrap()]; + let decoded_size = usize::try_from(decoder.total_bytes()).unwrap(); + if total_size - current_pos < decoded_size as u64 { + return Err(image::ImageError::Limits( + image::error::LimitError::from_kind( + image::error::LimitErrorKind::InsufficientMemory, + ), + )); + } + let mut buffer = vec![0; decoded_size]; decoder.read_image(buffer.as_mut_slice())?; Ok((width, height, buffer)) @@ -32,23 +45,25 @@ fn roundtrip(bytes: &[u8]) -> ImageResult<()> { /// Assumes the writer is buffered. In most cases, /// you should wrap your writer in a `BufWriter` for best performance. // TODO this method should probably already exist in the main image crate - fn write_rgba_image(write: impl Write + Seek, (width, height, data): &(u32,u32,Vec)) -> ImageResult<()> { - OpenExrEncoder::new(write).write_image( - data.as_slice(), *width, *height, - ColorType::Rgba32F - ) + fn write_rgba_image( + write: impl Write + Seek, + (width, height, data): &(u32, u32, Vec), + ) -> ImageResult<()> { + OpenExrEncoder::new(write).write_image(data.as_slice(), *width, *height, ColorType::Rgba32F) } - - let decoded_image = read_as_rgba_byte_image(Cursor::new(bytes))?; + let decoded_image = read_as_rgba_byte_image(&mut Cursor::new(bytes))?; let mut bytes = Vec::with_capacity(bytes.len() + 20); write_rgba_image(Cursor::new(&mut bytes), &decoded_image)?; - let redecoded_image = read_as_rgba_byte_image(Cursor::new(bytes))?; + let redecoded_image = read_as_rgba_byte_image(&mut Cursor::new(bytes))?; // if both images are valid, assert read/write consistency - assert_eq!(decoded_image, redecoded_image, "image was valid but was not reproducible"); + assert_eq!( + decoded_image, redecoded_image, + "image was valid but was not reproducible" + ); Ok(()) } From d60a15a8da27f60c535d8b22fc8553d7ffa0c4c9 Mon Sep 17 00:00:00 2001 From: fedotoff Date: Fri, 2 Sep 2022 10:40:39 +0300 Subject: [PATCH 2/3] try_unwrap check + decode_size limit up to 256MB --- fuzz/fuzzers/fuzzer_script_exr.rs | 34 ++++++++++++++++--------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/fuzz/fuzzers/fuzzer_script_exr.rs b/fuzz/fuzzers/fuzzer_script_exr.rs index 6edbd84894..1db0b0ba67 100644 --- a/fuzz/fuzzers/fuzzer_script_exr.rs +++ b/fuzz/fuzzers/fuzzer_script_exr.rs @@ -12,33 +12,35 @@ use std::convert::TryFrom; use std::io::Cursor; use std::io::Read; use std::io::Seek; -use std::io::SeekFrom; use std::io::Write; // "just dont panic" fn roundtrip(bytes: &[u8]) -> ImageResult<()> { /// Read the file from the specified path into an `Rgba32FImage`. // TODO this method should probably already exist in the main image crate - fn read_as_rgba_byte_image(read: &mut (impl Read + Seek)) -> ImageResult<(u32, u32, Vec)> { - let current_pos = read.stream_position()?; - let total_size = read.seek(SeekFrom::End(0))?; - let _ = read.seek(SeekFrom::Start(current_pos))?; - + fn read_as_rgba_byte_image(read: impl Read + Seek) -> ImageResult<(u32, u32, Vec)> { let decoder = OpenExrDecoder::with_alpha_preference(read, Some(true))?; let (width, height) = decoder.dimensions(); - let decoded_size = usize::try_from(decoder.total_bytes()).unwrap(); - if total_size - current_pos < decoded_size as u64 { - return Err(image::ImageError::Limits( + if let Ok(decoded_size) = usize::try_from(decoder.total_bytes()) { + if decoded_size > 256 * 1024 * 1024 { + return Err(image::ImageError::Limits( + image::error::LimitError::from_kind( + image::error::LimitErrorKind::InsufficientMemory, + ), + )); + } + let mut buffer = vec![0; decoded_size]; + decoder.read_image(buffer.as_mut_slice())?; + + Ok((width, height, buffer)) + } else { + Err(image::ImageError::Limits( image::error::LimitError::from_kind( image::error::LimitErrorKind::InsufficientMemory, ), - )); + )) } - let mut buffer = vec![0; decoded_size]; - decoder.read_image(buffer.as_mut_slice())?; - - Ok((width, height, buffer)) } /// Write an `Rgba32FImage`. @@ -52,12 +54,12 @@ fn roundtrip(bytes: &[u8]) -> ImageResult<()> { OpenExrEncoder::new(write).write_image(data.as_slice(), *width, *height, ColorType::Rgba32F) } - let decoded_image = read_as_rgba_byte_image(&mut Cursor::new(bytes))?; + let decoded_image = read_as_rgba_byte_image(Cursor::new(bytes))?; let mut bytes = Vec::with_capacity(bytes.len() + 20); write_rgba_image(Cursor::new(&mut bytes), &decoded_image)?; - let redecoded_image = read_as_rgba_byte_image(&mut Cursor::new(bytes))?; + let redecoded_image = read_as_rgba_byte_image(Cursor::new(bytes))?; // if both images are valid, assert read/write consistency assert_eq!( From 1abb2ac8bd10145c66b72ed8662e540be2a7e393 Mon Sep 17 00:00:00 2001 From: fedotoff Date: Sun, 4 Sep 2022 11:11:11 +0300 Subject: [PATCH 3/3] Bound buffer size in fuzzer_script_exr up to 256MB + set_limits default check. --- fuzz/fuzzers/fuzzer_script_exr.rs | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/fuzz/fuzzers/fuzzer_script_exr.rs b/fuzz/fuzzers/fuzzer_script_exr.rs index 1db0b0ba67..645150ab37 100644 --- a/fuzz/fuzzers/fuzzer_script_exr.rs +++ b/fuzz/fuzzers/fuzzer_script_exr.rs @@ -4,6 +4,7 @@ extern crate libfuzzer_sys; extern crate image; use image::codecs::openexr::*; +use image::io::Limits; use image::ColorType; use image::ImageDecoder; use image::ImageEncoder; @@ -19,27 +20,20 @@ fn roundtrip(bytes: &[u8]) -> ImageResult<()> { /// Read the file from the specified path into an `Rgba32FImage`. // TODO this method should probably already exist in the main image crate fn read_as_rgba_byte_image(read: impl Read + Seek) -> ImageResult<(u32, u32, Vec)> { - let decoder = OpenExrDecoder::with_alpha_preference(read, Some(true))?; - let (width, height) = decoder.dimensions(); - - if let Ok(decoded_size) = usize::try_from(decoder.total_bytes()) { - if decoded_size > 256 * 1024 * 1024 { - return Err(image::ImageError::Limits( - image::error::LimitError::from_kind( - image::error::LimitErrorKind::InsufficientMemory, - ), - )); + let mut decoder = OpenExrDecoder::with_alpha_preference(read, Some(true))?; + match usize::try_from(decoder.total_bytes()) { + Ok(decoded_size) if decoded_size <= 256 * 1024 * 1024 => { + decoder.set_limits(Limits::default())?; + let (width, height) = decoder.dimensions(); + let mut buffer = vec![0; decoded_size]; + decoder.read_image(buffer.as_mut_slice())?; + Ok((width, height, buffer)) } - let mut buffer = vec![0; decoded_size]; - decoder.read_image(buffer.as_mut_slice())?; - - Ok((width, height, buffer)) - } else { - Err(image::ImageError::Limits( + _ => Err(image::ImageError::Limits( image::error::LimitError::from_kind( image::error::LimitErrorKind::InsufficientMemory, ), - )) + )), } }