From 5f6d8d72ea1ea0d974a82f6f97457f670296eb0b Mon Sep 17 00:00:00 2001 From: relrelb Date: Sat, 22 Jul 2023 00:47:58 +0300 Subject: [PATCH] render: Skip unreasonably large bitmaps Some SWFs report unreasonable bitmap dimensions, and trying to reserve enough capacity for them always fails. To avoid panics, skip those bitmaps. Fixes #1191 Fixes #2759 Fixes #10701 --- render/src/utils.rs | 70 ++++++++++++++++++++---------------------- swf/src/types/color.rs | 1 + 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/render/src/utils.rs b/render/src/utils.rs index b04a37179..224c4c2ff 100644 --- a/render/src/utils.rs +++ b/render/src/utils.rs @@ -141,6 +141,18 @@ pub fn remove_invalid_jpeg_data(data: &[u8]) -> Cow<[u8]> { } } +/// Some SWFs report unreasonable bitmap dimensions (#1191). +/// Fail before decoding such bitmaps to avoid panics. +fn validate_size(width: u16, height: u16) -> Result<(), Error> { + const INVALID_SIZE: usize = 0x8000000; // 128MB + + let size = (width as usize).saturating_mul(height as usize); + if size >= INVALID_SIZE { + return Err(Error::TooLarge); + } + Ok(()) +} + /// Decodes a JPEG with optional alpha data. /// The decoded bitmap will have pre-multiplied alpha. fn decode_jpeg(jpeg_data: &[u8], alpha_data: Option<&[u8]>) -> Result { @@ -151,6 +163,7 @@ fn decode_jpeg(jpeg_data: &[u8], alpha_data: Option<&[u8]>) -> Result Result< // Decompress the image data (DEFLATE compression). let mut decoded_data = decompress_zlib(swf_tag.data)?; + let has_alpha = swf_tag.version == 2; + // Swizzle/de-palettize the bitmap. let out_data = match (swf_tag.version, swf_tag.format) { (1, swf::BitmapFormat::Rgb15) => { let padded_width = (swf_tag.width + 0b1) & !0b1; - let mut out_data: Vec = + validate_size(swf_tag.width, swf_tag.height)?; + let mut out_data = Vec::with_capacity(swf_tag.width as usize * swf_tag.height as usize * 4); let mut i = 0; for _ in 0..swf_tag.height { @@ -242,7 +258,7 @@ pub fn decode_define_bits_lossless(swf_tag: &swf::DefineBitsLossless) -> Result< rgb5_component(10), rgb5_component(5), rgb5_component(0), - 0xff, + u8::MAX, ]); i += 2; } @@ -251,63 +267,45 @@ pub fn decode_define_bits_lossless(swf_tag: &swf::DefineBitsLossless) -> Result< out_data } (1 | 2, swf::BitmapFormat::Rgb32) => { - let has_alpha = swf_tag.version == 2; for rgba in decoded_data.chunks_exact_mut(4) { rgba.rotate_left(1); if !has_alpha { - rgba[3] = 0xff; + rgba[3] = u8::MAX; } } decoded_data } - (1, swf::BitmapFormat::ColorMap8 { num_colors }) => { + (1 | 2, swf::BitmapFormat::ColorMap8 { num_colors }) => { let mut i = 0; let padded_width = (swf_tag.width + 0b11) & !0b11; let mut palette = Vec::with_capacity(num_colors as usize + 1); for _ in 0..=num_colors { + let a = if has_alpha { + decoded_data[i + 3] + } else { + u8::MAX + }; palette.push(Color { r: decoded_data[i], g: decoded_data[i + 1], b: decoded_data[i + 2], - a: 255, + a, }); - i += 3; + i += if has_alpha { 4 } else { 3 }; } - let mut out_data: Vec = - Vec::with_capacity(swf_tag.width as usize * swf_tag.height as usize * 4); - for _ in 0..swf_tag.height { - for _ in 0..swf_tag.width { - let entry = decoded_data[i] as usize; - let color = palette.get(entry).unwrap_or(&Color::BLACK); - out_data.extend([color.r, color.g, color.b, color.a]); - i += 1; - } - i += (padded_width - swf_tag.width) as usize; - } - out_data - } - (2, swf::BitmapFormat::ColorMap8 { num_colors }) => { - let mut i = 0; - let padded_width = (swf_tag.width + 0b11) & !0b11; - let mut palette = Vec::with_capacity(num_colors as usize + 1); - for _ in 0..=num_colors { - palette.push(Color { - r: decoded_data[i], - g: decoded_data[i + 1], - b: decoded_data[i + 2], - a: decoded_data[i + 3], - }); - i += 4; - } - let mut out_data: Vec = + validate_size(swf_tag.width, swf_tag.height)?; + let mut out_data = Vec::with_capacity(swf_tag.width as usize * swf_tag.height as usize * 4); for _ in 0..swf_tag.height { for _ in 0..swf_tag.width { let entry = decoded_data[i] as usize; - const TRANSPARENT: Color = Color::from_rgb(0, 0); - let color = palette.get(entry).unwrap_or(&TRANSPARENT); + let color = palette.get(entry).unwrap_or(if has_alpha { + &Color::TRANSPARENT + } else { + &Color::BLACK + }); out_data.extend([color.r, color.g, color.b, color.a]); i += 1; } diff --git a/swf/src/types/color.rs b/swf/src/types/color.rs index ff2fb91fa..accdec3bb 100644 --- a/swf/src/types/color.rs +++ b/swf/src/types/color.rs @@ -17,6 +17,7 @@ pub struct Color { } impl Color { + pub const TRANSPARENT: Self = Self::from_rgb(0, 0); pub const BLACK: Self = Self::from_rgb(0, 255); pub const WHITE: Self = Self::from_rgb(0xFFFFFF, 255); pub const RED: Self = Self::from_rgb(0xFF0000, 255);