LibGfx: Avoid undefined behavior on a BMP with an INT_MIN height

Problem: Decoding a BMP whose height is INT_MIN triggered a UBSan error.
A top-down BMP legitimately uses a negative height — and unlike width,
it’s not rejected, so it can be INT_MIN.

Cause: decode_bmp_pixel_data() took the magnitude of the i32 width and
height with abs(). Negating INT_MIN is undefined behavior.

Fix: Widen to i64 before taking the absolute value — so the magnitude of
INT_MIN is representable. The resulting out-of-range dimension is still
rejected by Bitmap::create — so only the undefined behavior changes.

Fixes: https://github.com/LadybirdBrowser/ladybird/issues/9994
This commit is contained in:
sideshowbarker 2026-06-15 02:16:42 +09:00 committed by Jelle Raaijmakers
parent 20b1129352
commit 9bde8a5c88
3 changed files with 10 additions and 2 deletions

View file

@ -1297,8 +1297,9 @@ static ErrorOr<void> decode_bmp_pixel_data(BMPLoadingContext& context)
return Error::from_string_literal("BMP has invalid bpp");
}
u32 const width = abs(context.dib.core.width);
u32 const height = !context.is_included_in_ico ? abs(context.dib.core.height) : (abs(context.dib.core.height) / 2);
u32 const width = static_cast<u32>(abs(static_cast<i64>(context.dib.core.width)));
u32 const absolute_height = static_cast<u32>(abs(static_cast<i64>(context.dib.core.height)));
u32 const height = !context.is_included_in_ico ? absolute_height : (absolute_height / 2);
context.bitmap = TRY(Bitmap::create(format, Gfx::AlphaType::Unpremultiplied, { static_cast<int>(width), static_cast<int>(height) }));

View file

@ -92,6 +92,13 @@ TEST_CASE(test_bmp_v4)
EXPECT_EQ(frame.image->get_pixel(0, 0), Gfx::Color::NamedColor::Red);
}
TEST_CASE(test_bmp_negative_int_min_height)
{
auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("bmp/negative-height-int-min.bmp"sv)));
auto plugin_decoder = TRY_OR_FAIL(Gfx::BMPImageDecoderPlugin::create(file->bytes()));
EXPECT(plugin_decoder->frame(0).is_error());
}
TEST_CASE(test_bmp_os2_3bit)
{
auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("bmp/os2_3bpc.bmp"sv)));

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 B