From 9d64cd8c9b2b99b6676453f96a1d3866ff0709f6 Mon Sep 17 00:00:00 2001 From: sideshowbarker Date: Fri, 19 Jun 2026 15:23:57 +0900 Subject: [PATCH] LibGfx: Reject the Invalid bitmap format from IPC decoders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: A borked process sending a Gfx::Bitmap (inside BitmapSequence) or a Gfx::ShareableBitmap over IPC could set BitmapFormat::Invalid as the format field. The receiving process then aborted while decoding the message β€” an IPC-reachable crash. Cause: The helper that both decoders use for validating the format read off the wire accepts BitmapFormat::Invalid. The decoders go on to build a bitmap with that format. But that triggers an assert β€” because minimum_pitch only knows the four real pixel formats. Fix: Drop BitmapFormat::Invalid from is_valid_bitmap_format. It’s an indicator of an absent/unknown format, never one a real bitmap can have. And any real bitmap is never encoded with it. So, both BitmapSequence and ShareableBitmap decode now return a clean decode error for it. --- Libraries/LibGfx/Bitmap.h | 1 - Tests/LibGfx/CMakeLists.txt | 1 + Tests/LibGfx/TestShareableBitmap.cpp | 56 ++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 Tests/LibGfx/TestShareableBitmap.cpp diff --git a/Libraries/LibGfx/Bitmap.h b/Libraries/LibGfx/Bitmap.h index e34f512978..942ea78e59 100644 --- a/Libraries/LibGfx/Bitmap.h +++ b/Libraries/LibGfx/Bitmap.h @@ -38,7 +38,6 @@ enum class BitmapFormat { inline bool is_valid_bitmap_format(u32 const format) { switch (format) { - case static_cast(BitmapFormat::Invalid): case static_cast(BitmapFormat::BGRx8888): case static_cast(BitmapFormat::RGBx8888): case static_cast(BitmapFormat::BGRA8888): diff --git a/Tests/LibGfx/CMakeLists.txt b/Tests/LibGfx/CMakeLists.txt index 7a2bf71bcd..c6528d21fd 100644 --- a/Tests/LibGfx/CMakeLists.txt +++ b/Tests/LibGfx/CMakeLists.txt @@ -18,4 +18,5 @@ target_link_libraries(BenchmarkJPEGLoader PRIVATE LibImageDecoders) target_link_libraries(TestImageDecoder PRIVATE LibImageDecoders) target_link_libraries(TestImageWriter PRIVATE LibImageDecoders) +ladybird_test(TestShareableBitmap.cpp LibGfx LIBS LibGfx LibIPC) ladybird_test(TestYUVData.cpp LibGfx LIBS LibGfx skia) diff --git a/Tests/LibGfx/TestShareableBitmap.cpp b/Tests/LibGfx/TestShareableBitmap.cpp new file mode 100644 index 0000000000..a2b9997f81 --- /dev/null +++ b/Tests/LibGfx/TestShareableBitmap.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2026, Ladybird contributors + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static ErrorOr decode_shareable_bitmap(Gfx::BitmapFormat format, Gfx::IntSize size, size_t buffer_size) +{ + auto buffer = MUST(Core::AnonymousBuffer::create_with_size(buffer_size)); + + IPC::MessageBuffer message_buffer; + IPC::Encoder encoder { message_buffer }; + MUST(encoder.encode(true)); + MUST(encoder.encode(MUST(IPC::File::clone_fd(buffer.fd())))); + MUST(encoder.encode(size)); + MUST(encoder.encode(static_cast(format))); + MUST(encoder.encode(static_cast(Gfx::AlphaType::Premultiplied))); + + auto data = message_buffer.take_data(); + FixedMemoryStream stream { data.span() }; + + Queue attachments; + for (auto& attachment : message_buffer.take_attachments()) + attachments.enqueue(move(attachment)); + + IPC::Decoder decoder { stream, attachments }; + return IPC::decode(decoder); +} + +TEST_CASE(decode_rejects_invalid_bitmap_format) +{ + // A ShareableBitmap whose format field is BitmapFormat::Invalid must be rejected at the IPC boundary. Accepting it + // previously reached minimum_pitch(), which has no case for Invalid and trips an assert β€” an IPC-reachable crash. + auto result = decode_shareable_bitmap(Gfx::BitmapFormat::Invalid, Gfx::IntSize { 16, 16 }, 1024); + EXPECT(result.is_error()); +} + +TEST_CASE(decode_accepts_valid_bitmap_format) +{ + auto required = Gfx::Bitmap::size_in_bytes(Gfx::Bitmap::minimum_pitch(16, Gfx::BitmapFormat::BGRA8888), 16); + auto result = decode_shareable_bitmap(Gfx::BitmapFormat::BGRA8888, Gfx::IntSize { 16, 16 }, required); + EXPECT(!result.is_error()); +}