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 0000000000..c8548e6f05 Binary files /dev/null and b/Tests/LibGfx/test-inputs/woff/invalid_sfnt_size.woff differ 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;