← WebKit — 2026-W37

f52d0e486a0e85e1f7f6bd41c5bb2a939a9da1c3  [JSC] Attempt to remove many bound-check assertions inside loop via RELEASE_ASSERT etc.

severity medium class IntegerOverflow confidence 0.42 signal score 11.77 Source/JavaScriptCore/runtime
Primitive: JSBigInt size/bounds invariants and int32_t bit-count casts hardened against overflow/OOB (truncateToNBits/truncateAndSubFromPowerOfTwo, cachedMod, parseInt)
Triage note: Converts many bounds ASSERTs to RELEASE_ASSERT across JSBigInt multiplication/mod/shift/parse paths and changes int32_t bit-count variables (n, neededDigits) to unsigned/size_t in truncateToNBits/truncateAndSubFromPowerOfTwo, plus a span-based parseInt trailing-space trim. Defensive hardening that would turn some out-of-bounds/negative-count situations into controlled crashes, but no single concrete reachable overflow is demonstrated in the diff.
Structural signals: security_assert (+6.47), clamp_saturate (+1.8), msg_soft (+1.0), area:Source/JavaScriptCore/runtime (+2.5)

Commit message

[JSC] Attempt to remove many bound-check assertions inside loop via RELEASE_ASSERT etc.
https://bugs.webkit.org/show_bug.cgi?id=323699
rdar://186956345

Reviewed by Yijia Huang.

std::span uses release-assert for bound-check. While clang is having
analysis to remove unnecessary bound-check, it is not sufficient in many
cases and it ends up emitting it in the middle of loop. We can remove
this if we emit RELEASE_ASSERT explicitly out of the loop with a
specific condition as a hint to clang to prove that this bound-check is
unnecessary. This patch systematically analyze JSBigInt compiled binary
to detect brk generated from std::span, and puts the right precondition
etc. to remove this from the loop. Sometimes, we replace int32_t to
size_t for index as int32_t is hard to prove for clang about
bound-check. Since std::span::size is size_t and it is unsigned, the
behavior is fully specified in C++ spec, and clang can relatively easily
prove the value's bound.

* Source/JavaScriptCore/runtime/JSBigInt.cpp:
(JSC::JSBigInt::karatsubaMain):
(JSC::JSBigInt::greaterThanOrEqual):
(JSC::JSBigInt::leftShift):
(JSC::JSBigInt::divideSchoolbook):
(JSC::JSBigInt::cachedModFixed):
(JSC::JSBigInt::cachedMod):
(JSC::JSBigInt::addSchoolbook):
(JSC::JSBigInt::absoluteBitwiseOp):
(JSC::JSBigInt::absoluteAddOne):
(JSC::JSBigInt::absoluteSubOne):
(JSC::JSBigInt::leftShiftByAbsolute):
(JSC::JSBigInt::rightShiftByAbsolute):
(JSC::JSBigInt::toStringGeneric):
(JSC::JSBigInt::parseInt):
(JSC::JSBigInt::asIntNImpl):
(JSC::JSBigInt::asUintNImpl):
(JSC::JSBigInt::truncateToNBits):
(JSC::JSBigInt::truncateAndSubFromPowerOfTwo):
* Source/JavaScriptCore/runtime/JSBigInt.h:

Canonical link: https://commits.webkit.org/320745@main

Before / after

Loading diff…