From 602e7fe2bd7f63876f00b47ef1be18ffc9dc97bb Mon Sep 17 00:00:00 2001 From: sideshowbarker Date: Sun, 14 Jun 2026 20:07:35 +0900 Subject: [PATCH] LibGfx: Reject a BMP V5 ICC profile offset that points out of bounds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: A BMP V5 image whose embedded ICC profile offset points past the end of the file triggers an OOB read. Cause: The bounds check summed the profile offset, the file-header size, and the profile size in 32-bit arithmetic. So, a large offset (e.g. 0xfffffff0) wraps the sum back into range and passes the check. The decoder then returns a span pointing far past the end of the file. Fix: Compute the sum in 64 bits — so an out-of-bounds offset can no longer wrap, and the profile’s rejected. Fixes: https://github.com/LadybirdBrowser/ladybird/issues/9967 --- Libraries/LibGfx/ImageFormats/BMPLoader.cpp | 2 +- Tests/LibGfx/TestImageDecoder.cpp | 8 ++++++++ .../bmp/v5-icc-profile-out-of-bounds.bmp | Bin 0 -> 142 bytes 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 Tests/LibGfx/test-inputs/bmp/v5-icc-profile-out-of-bounds.bmp diff --git a/Libraries/LibGfx/ImageFormats/BMPLoader.cpp b/Libraries/LibGfx/ImageFormats/BMPLoader.cpp index 9596dd7864..e19891ac04 100644 --- a/Libraries/LibGfx/ImageFormats/BMPLoader.cpp +++ b/Libraries/LibGfx/ImageFormats/BMPLoader.cpp @@ -1571,7 +1571,7 @@ ErrorOr> BMPImageDecoderPlugin::icc_data() // FIXME: Do something with v5.intent (which has a GamutMappingIntent value). u8 header_size = m_context->is_included_in_ico ? 0 : bmp_header_size; - if (v5.profile_data + header_size + v5.profile_size > m_context->file_size) + if (static_cast(v5.profile_data) + header_size + v5.profile_size > m_context->file_size) return Error::from_string_literal("BMPImageDecoderPlugin: ICC profile data out of bounds"); return ReadonlyBytes { m_context->file_bytes + header_size + v5.profile_data, v5.profile_size }; diff --git a/Tests/LibGfx/TestImageDecoder.cpp b/Tests/LibGfx/TestImageDecoder.cpp index 6a7b14a010..a8b2390823 100644 --- a/Tests/LibGfx/TestImageDecoder.cpp +++ b/Tests/LibGfx/TestImageDecoder.cpp @@ -99,6 +99,14 @@ TEST_CASE(test_bmp_negative_int_min_height) EXPECT(plugin_decoder->frame(0).is_error()); } +TEST_CASE(test_bmp_v5_icc_profile_out_of_bounds) +{ + auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("bmp/v5-icc-profile-out-of-bounds.bmp"sv))); + EXPECT(Gfx::BMPImageDecoderPlugin::sniff(file->bytes())); + auto plugin_decoder = TRY_OR_FAIL(Gfx::BMPImageDecoderPlugin::create(file->bytes())); + EXPECT(plugin_decoder->icc_data().is_error()); +} + TEST_CASE(test_bmp_os2_3bit) { auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("bmp/os2_3bpc.bmp"sv))); diff --git a/Tests/LibGfx/test-inputs/bmp/v5-icc-profile-out-of-bounds.bmp b/Tests/LibGfx/test-inputs/bmp/v5-icc-profile-out-of-bounds.bmp new file mode 100644 index 0000000000000000000000000000000000000000..7728d689f560daea6e60a42abd2829aad7731117 GIT binary patch literal 142 rcmZ?r?PGv|E+AC{#Eft(0hV9^llTc2S0`WmDzM9b`2YXE0g@B|xL^l( literal 0 HcmV?d00001