From c2112cde76599316c12311c52117b7a8d3838a6a Mon Sep 17 00:00:00 2001 From: Tim Ledbetter Date: Mon, 23 Oct 2023 21:48:15 +0100 Subject: [PATCH] LibGfx/WOFF: Ensure header `totalSfntSize` matches expected value --- Tests/LibGfx/CMakeLists.txt | 1 + Tests/LibGfx/TestWOFF.cpp | 27 ++++++++++++++++++ .../test-inputs/woff/invalid_sfnt_size.woff | Bin 0 -> 44 bytes Userland/Libraries/LibGfx/Font/WOFF/Font.cpp | 9 ++++++ 4 files changed, 37 insertions(+) create mode 100644 Tests/LibGfx/TestWOFF.cpp create mode 100644 Tests/LibGfx/test-inputs/woff/invalid_sfnt_size.woff diff --git a/Tests/LibGfx/CMakeLists.txt b/Tests/LibGfx/CMakeLists.txt index 12c3bf68ed..e11157988f 100644 --- a/Tests/LibGfx/CMakeLists.txt +++ b/Tests/LibGfx/CMakeLists.txt @@ -9,6 +9,7 @@ set(TEST_SOURCES TestParseISOBMFF.cpp TestRect.cpp TestScalingFunctions.cpp + TestWOFF.cpp ) foreach(source IN LISTS TEST_SOURCES) diff --git a/Tests/LibGfx/TestWOFF.cpp b/Tests/LibGfx/TestWOFF.cpp new file mode 100644 index 0000000000..452ca0603b --- /dev/null +++ b/Tests/LibGfx/TestWOFF.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023, Tim Ledbetter + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +#ifdef AK_OS_SERENITY +# define TEST_INPUT(x) ("/usr/Tests/LibGfx/test-inputs/" x) +#else +# define TEST_INPUT(x) ("test-inputs/" x) +#endif + +TEST_CASE(malformed_woff) +{ + Array test_inputs = { + TEST_INPUT("woff/invalid_sfnt_size.woff"sv) + }; + + for (auto test_input : test_inputs) { + auto file = MUST(Core::MappedFile::map(test_input)); + auto font_or_error = WOFF::Font::try_load_from_externally_owned_memory(file->bytes()); + EXPECT(font_or_error.is_error()); + } +} diff --git a/Tests/LibGfx/test-inputs/woff/invalid_sfnt_size.woff b/Tests/LibGfx/test-inputs/woff/invalid_sfnt_size.woff new file mode 100644 index 0000000000000000000000000000000000000000..c8548e6f05fce7c29b8b19516178d01ec58a8efc GIT binary patch literal 44 fcmXT-cXLxv_^-ggz@Wnb0bB|S3jd)%0T}=Q6B-QQ literal 0 HcmV?d00001 diff --git a/Userland/Libraries/LibGfx/Font/WOFF/Font.cpp b/Userland/Libraries/LibGfx/Font/WOFF/Font.cpp index bba95149c0..f8d2e9489d 100644 --- a/Userland/Libraries/LibGfx/Font/WOFF/Font.cpp +++ b/Userland/Libraries/LibGfx/Font/WOFF/Font.cpp @@ -91,6 +91,7 @@ ErrorOr> Font::try_load_from_externally_owned_memory(Readonl // (The value 0x74727565 'true' has been used for some TrueType-flavored fonts on Mac OS, for example.) // Whether client software will actually support other types of sfnt font data is outside the scope of the WOFF specification, which simply describes how the sfnt is repackaged for Web use. + auto expected_total_sfnt_size = sizeof(OpenType::TableDirectory) + header.num_tables * 16; if (header.length > buffer.size()) return Error::from_string_literal("Invalid WOFF length"); if (header.num_tables > NumericLimits::max() / 16) @@ -103,6 +104,8 @@ ErrorOr> Font::try_load_from_externally_owned_memory(Readonl return Error::from_string_literal("Invalid WOFF private block offset"); if (sizeof(Header) + header.num_tables * sizeof(TableDirectoryEntry) > header.length) return Error::from_string_literal("Truncated WOFF table directory"); + if (header.total_sfnt_size < expected_total_sfnt_size) + return Error::from_string_literal("Invalid WOFF total sfnt size"); if (header.total_sfnt_size > 10 * MiB) return Error::from_string_literal("Uncompressed font is more than 10 MiB"); auto font_buffer = TRY(ByteBuffer::create_zeroed(header.total_sfnt_size)); @@ -121,6 +124,9 @@ ErrorOr> Font::try_load_from_externally_owned_memory(Readonl for (size_t i = 0; i < header.num_tables; ++i) { auto entry = TRY(stream.read_value()); + expected_total_sfnt_size += (entry.orig_length + 3) & 0xFFFFFFFC; + if (expected_total_sfnt_size > header.total_sfnt_size) + return Error::from_string_literal("Invalid WOFF total sfnt size"); if ((size_t)entry.offset + entry.comp_length > header.length) return Error::from_string_literal("Truncated WOFF table"); if (font_buffer_offset + entry.orig_length > font_buffer.size()) @@ -150,6 +156,9 @@ ErrorOr> Font::try_load_from_externally_owned_memory(Readonl font_buffer_offset += entry.orig_length; } + if (header.total_sfnt_size != expected_total_sfnt_size) + return Error::from_string_literal("Invalid WOFF total sfnt size"); + auto input_font = TRY(OpenType::Font::try_load_from_externally_owned_memory(font_buffer.bytes(), index)); auto font = adopt_ref(*new Font(input_font, move(font_buffer))); return font;