From aa54007943023d5135e5b2cd318fcd9cdcb7f170 Mon Sep 17 00:00:00 2001 From: Tim Ledbetter Date: Tue, 31 Oct 2023 18:16:27 +0000 Subject: [PATCH] LibGfx/TinyVG: Avoid OOM if header contains a bogus color table size This change limits the amount of memory that is initially allocated for the color table. This prevents an OOM condition if the file contains an incorrect color table size. --- Tests/LibGfx/TestImageDecoder.cpp | 14 ++++++++++++++ .../test-inputs/tvg/bogus-color-table-size.tvg | Bin 0 -> 10 bytes .../LibGfx/ImageFormats/TinyVGLoader.cpp | 13 ++++++++----- 3 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 Tests/LibGfx/test-inputs/tvg/bogus-color-table-size.tvg diff --git a/Tests/LibGfx/TestImageDecoder.cpp b/Tests/LibGfx/TestImageDecoder.cpp index fdc76d228c..725c6b06f5 100644 --- a/Tests/LibGfx/TestImageDecoder.cpp +++ b/Tests/LibGfx/TestImageDecoder.cpp @@ -721,6 +721,20 @@ TEST_CASE(test_everything_tvg) } } +TEST_CASE(test_tvg_malformed) +{ + Array test_inputs = { + TEST_INPUT("tvg/bogus-color-table-size.tvg"sv) + }; + + for (auto test_input : test_inputs) { + auto file = MUST(Core::MappedFile::map(test_input)); + auto plugin_decoder = TRY_OR_FAIL(Gfx::TinyVGImageDecoderPlugin::create(file->bytes())); + auto frame_or_error = plugin_decoder->frame(0); + EXPECT(frame_or_error.is_error()); + } +} + TEST_CASE(test_jxl_modular_simple_tree_upsample2_10bits) { auto file = MUST(Core::MappedFile::map(TEST_INPUT("jxl/modular_simple_tree_upsample2_10bits_rct.jxl"sv))); diff --git a/Tests/LibGfx/test-inputs/tvg/bogus-color-table-size.tvg b/Tests/LibGfx/test-inputs/tvg/bogus-color-table-size.tvg new file mode 100644 index 0000000000000000000000000000000000000000..6ddcb9422771ff76a8a5faaaa68ac8c708d61a53 GIT binary patch literal 10 RcmXR4V`MnNaDsu60RRtP0$Tt8 literal 0 HcmV?d00001 diff --git a/Userland/Libraries/LibGfx/ImageFormats/TinyVGLoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/TinyVGLoader.cpp index cff49dac2a..ec295a7a14 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/TinyVGLoader.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/TinyVGLoader.cpp @@ -127,11 +127,14 @@ static ErrorOr decode_tinyvg_header(Stream& stream) return header; } -static ErrorOr> decode_color_table(Stream& stream, ColorEncoding encoding, u32 color_count) +static ErrorOr> decode_color_table(Stream& stream, ColorEncoding encoding, u32 color_count) { if (encoding == ColorEncoding::Custom) return Error::from_string_literal("Invalid TinyVG: Unsupported color encoding"); - auto color_table = TRY(FixedArray::create(color_count)); + + static constexpr size_t MAX_INITIAL_COLOR_TABLE_SIZE = 65536; + Vector color_table; + TRY(color_table.try_ensure_capacity(min(MAX_INITIAL_COLOR_TABLE_SIZE, color_count))); auto parse_color = [&]() -> ErrorOr { switch (encoding) { case ColorEncoding::RGBA8888: { @@ -157,8 +160,8 @@ static ErrorOr> decode_color_table(Stream& stream, ColorEncodi return Error::from_string_literal("Invalid TinyVG: Bad color encoding"); } }; - for (auto& color : color_table) { - color = TRY(parse_color()); + while (color_count-- > 0) { + TRY(color_table.try_append(TRY(parse_color()))); } return color_table; } @@ -357,7 +360,7 @@ ErrorOr> TinyVGDecodedImageData::decode(St if (header.version != 1) return Error::from_string_literal("Invalid TinyVG: Unsupported version"); - auto color_table = TRY(decode_color_table(stream, header.color_encoding, header.color_count)); + auto const& color_table = TRY(decode_color_table(stream, header.color_encoding, header.color_count)); TinyVGReader reader { stream, header, color_table.span() }; auto rectangle_to_path = [](FloatRect const& rect) -> Path {