LibWeb: Saturate list-item ordinal numbering at the i32 bounds

Problem: Crash when loading an ordered list whose numbering reaches the
i32 limit; e.g. <ol start="2147483647"> with two or more items.

Cause: Element::ordinal_value() kept its numbering in a Checked<i32>
and stepped it once per list item. When the numbering already sits at
the i32 maximum (or minimum, for a reversed list whose value attribute
pins it there), the increment overflowed the Checked value.

Fix: Keep the numbering in a plain i32 instead, and step it with
AK::saturating_add and AK::saturating_sub — so it clamps at the i32
bounds, rather than overflowing.

Fixes https://github.com/LadybirdBrowser/ladybird/issues/10003
This commit is contained in:
sideshowbarker 2026-06-21 15:01:35 +09:00 committed by Jelle Raaijmakers
parent 9ffd3e48c3
commit 86f75e1e35
3 changed files with 19 additions and 4 deletions

View file

@ -13,6 +13,7 @@
#include <AK/IterationDecision.h>
#include <AK/JsonObjectSerializer.h>
#include <AK/NumericLimits.h>
#include <AK/SaturatingMath.h>
#include <AK/StringBuilder.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/DecodedImageFrame.h>
@ -4208,7 +4209,7 @@ i32 Element::ordinal_value()
// 1. Let i be 1. [Not necessary]
// 2. If owner is an ol element, let numbering be owner's starting value. Otherwise, let numbering be 1.
AK::Checked<i32> numbering = 1;
i32 numbering = 1;
auto reversed = false;
if (auto* ol_element = as_if<HTML::HTMLOListElement>(owner.ptr())) {
@ -4233,13 +4234,13 @@ i32 Element::ordinal_value()
}
// 6. The ordinal value of item is numbering.
item->m_ordinal_value = numbering.value();
item->m_ordinal_value = numbering;
// 7. If owner is an ol element, and owner has a reversed attribute, decrement numbering by 1; otherwise, increment numbering by 1.
if (reversed) {
numbering--;
numbering = AK::saturating_sub(numbering, 1);
} else {
numbering++;
numbering = AK::saturating_add(numbering, 1);
}
// 8. Increment i by 1. [Not necessary]

View file

@ -0,0 +1 @@
PASS (didn't crash)

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<script src="include.js"></script>
<!-- Starting value at the i32 maximum: incrementing the ordinal for the second item overflows. -->
<ol start="2147483647"><li>a</li><li>b</li></ol>
<!-- Reversed list whose value attribute pins the ordinal to the i32 minimum: decrementing underflows. -->
<ol reversed><li value="-2147483648">a</li><li>b</li></ol>
<script>
test(() => {
// Forces layout, which computes each list item's ordinal value via the marker box.
document.documentElement.offsetHeight;
println("PASS (didn't crash)");
});
</script>