Skip to content

[JSC] WebAssembly.Exception.prototype.getArg: an index that is not an unsigned long is a TypeError, not a RangeError - #613

Open
robobun wants to merge 1 commit into
mainfrom
robobun/5682cef4/wasm-jsapi-getarg-shared-grow
Open

[JSC] WebAssembly.Exception.prototype.getArg: an index that is not an unsigned long is a TypeError, not a RangeError#613
robobun wants to merge 1 commit into
mainfrom
robobun/5682cef4/wasm-jsapi-getarg-shared-grow

Conversation

@robobun

@robobun robobun commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Problem

  • WebAssembly.Exception.prototype.getArg(tag, index) throws RangeError: Expect an integer argument in the range: [0, 2^32 - 1] for index values like -1, 2 ** 32, NaN and undefined. The IDL is [EnforceRange] unsigned long index ([js-api] Add missing EnforceRange to getArg argument. WebAssembly/exception-handling#257), so a failed conversion is a TypeError. V8 (EnforceUint32) and SpiderMonkey (JSMSG_WASM_BAD_ENFORCE_RANGE) throw TypeError.
  • The cause is WebAssemblyExceptionPrototype.cpp:118, which passes ErrorType::RangeError to toNonWrappingUint32(). Upstream 282520@main (bug 278413) added that so the imported WPT wasm/jsapi/exception/getArg.tentative.any.js would pass. That WPT copy predates exception-handling#257: the same test in WebAssembly/spec asserts TypeError, V8 lists the WPT copy as an expected failure, and Firefox marks its "Getting out-of-range argument" subtest as an expected FAIL.

Fix

  • Revert the call-site half of 282520@main: getArg calls toNonWrappingUint32() with the default TypeError. The helper keeps its parameter, so JSWebAssemblyHelpers.h stays identical to upstream. An index that converts but is past the payload is still a RangeError.
  • The two getArg.tentative.any*-expected.txt files go back to the FAIL line they had before 282520@main. JSTests/wasm/v8/exceptions-api.js gets back the getArg(tag, undefined) assertion that was commented out on import.
  • Verified: JSTests/wasm/js-api/exception-getarg-index-enforce-range.js (new) and the v8 exceptions-api.js fail on an unpatched Release JSCOnly build and pass patched in all 13 wasm modes. The whole JSTests/wasm.yaml run (17940 runs) passes. A Debug+ASan build passes the new test with --validateExceptionChecks=true.

Background

  • WebIDL [EnforceRange] unsigned long: NaN and the infinities throw TypeError, then the integer part must be in [0, 2^32 - 1] or it throws TypeError. So -0.5 and 0.9 are a valid 0. toNonWrappingUint32() implements this, and every other wasm JS API caller (Memory and Table sizes, deltas, indices) already gets TypeError from it.
  • The spec's getArg steps run after that conversion: a tag mismatch is a TypeError, then index >= payload size is a RangeError.
Notes
  • The upstream route for this is a follow-up to bug 278413 together with a one-line WPT change (getArg.tentative.any.js:46, RangeError to TypeError, matching WebAssembly/spec test/js-api/exception/getArg.tentative.any.js). This PR is the minimal fork-side carry of the same change. V8's expectation is in test/wasm-js/wasm-js.status, Firefox's in testing/web-platform/meta/wasm/jsapi/exception/getArg.tentative.any.js.ini.
  • An earlier revision of this branch also made a shared WebAssembly.Memory hand out a new SharedArrayBuffer after grow(0), because Node 26.3 does. That was wrong: V8 main since crrev.com/c/7660970 only refreshes the buffer of a shared memory whose length actually grew, SpiderMonkey's WasmMemoryObject::refreshBuffer does the same, and [threads][test][js-api] Add tests for growing shared memory WebAssembly/threads#248 adds the test "Growing shared memory by 0 does not change the buffer". JSC already behaves that way, so that part was dropped.

… unsigned long is a TypeError, not a RangeError

getArg(tag, index) declares index as [EnforceRange] unsigned long
(WebAssembly/exception-handling#257, now in the WebAssembly/spec js-api).
A WebIDL [EnforceRange] conversion that fails, because the value is NaN,
an infinity, or has an integer part outside [0, 2^32 - 1], throws
TypeError. Only an index that converts and is then past the payload is
a RangeError. V8 (EnforceUint32 in wasm-js.cc) and SpiderMonkey
(EnforceRangeU32, JSMSG_WASM_BAD_ENFORCE_RANGE) both throw TypeError.

JSC threw TypeError too until 282520@main (bug 278413), which passed
ErrorType::RangeError from getArg to toNonWrappingUint32() so that the
imported WPT test wasm/jsapi/exception/getArg.tentative.any.js would
pass. That WPT copy predates exception-handling#257: the copy of the
same test in WebAssembly/spec asserts TypeError, V8 lists the WPT copy
as an expected failure in test/wasm-js/wasm-js.status, and Firefox marks
its "Getting out-of-range argument" subtest as an expected FAIL. This
reverts the call-site half of 282520@main and restores the previous
FAIL expectation for that subtest. toNonWrappingUint32() keeps its
ErrorType parameter, unused for now, so the helper stays as upstream
has it.

* JSTests/wasm/js-api/exception-getarg-index-enforce-range.js: Added.
* JSTests/wasm/v8/exceptions-api.js:
(TestGetArg): Restore the getArg(tag, undefined) TypeError assertion with JSC's message.
* LayoutTests/imported/w3c/web-platform-tests/wasm/jsapi/exception/getArg.tentative.any-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/wasm/jsapi/exception/getArg.tentative.any.worker-expected.txt:
* Source/JavaScriptCore/wasm/js/WebAssemblyExceptionPrototype.cpp:
(JSC::JSC_DEFINE_HOST_FUNCTION):

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — one-line spec-conformance fix with thorough test coverage.

What was reviewed:

  • Confirmed toNonWrappingUint32 defaults to ErrorType::TypeError (JSWebAssemblyHelpers.h:49), so dropping the explicit RangeError argument yields the WebIDL-mandated behavior for [EnforceRange]; RETURN_IF_EXCEPTION and the subsequent tag/index-past-payload checks are unchanged.
  • New test in JSTests/wasm/js-api/ follows existing directory convention, uses assert.js (throws on failure, no printing), and covers conversion ordering, empty payloads, and ToNumber propagation.
  • WPT expected-output flip to FAIL is intentional: the imported test still asserts RangeError, which the PR shows is stale relative to the spec; V8/Firefox already expect this subtest to fail.
Extended reasoning...

Overview

The functional change is a single line in Source/JavaScriptCore/wasm/js/WebAssemblyExceptionPrototype.cpp: the index argument to WebAssembly.Exception.prototype.getArg is now converted via toNonWrappingUint32(globalObject, callFrame->argument(1)) without the explicit ErrorType::RangeError, so the helper falls back to its default ErrorType::TypeError. This matches WebIDL [EnforceRange] unsigned long semantics and how every other wasm JS API caller of the same helper already behaves. The rest of the host function — RETURN_IF_EXCEPTION, the tag-mismatch TypeError, and the index-past-payload RangeError — is untouched. Accompanying changes are a new regression test in JSTests/wasm/js-api/, an uncommented assertion in the imported V8 exceptions-api.js (with JSC's error message substituted), and two WPT -expected.txt files flipped from PASS to FAIL because the vendored WPT test still asserts the pre-spec-change RangeError.

Security risks

None. This changes only the constructor of the error object thrown when a JS caller passes an out-of-range or non-numeric index. There is no memory access, allocation sizing, JIT codegen, or privilege boundary involved; it is a pure host-function error-type adjustment.

Level of scrutiny

Low. The source change is one deleted argument reverting to a well-established default, and I verified the default in JSWebAssemblyHelpers.h. This is a host function (JSC_DEFINE_HOST_FUNCTION), so there are no per-tier LLInt/Baseline/DFG/FTL implementations to keep in sync. The new test lives alongside dozens of similar wasm/js-api/*.js tests, uses the shared assert.js (throws on failure, no logging), has no hot loops needing wasmTestLoopCount, and is clearly sub-200ms. The WPT expectation update is the standard WebKit way of tracking a stale imported test.

Other factors

.github/CODEOWNERS lists @ WebKit/jsc-reviewers for Source/JavaScriptCore and JSTests, but the file's own header states contributors do not "own" components — it is upstream WebKit's auto-reviewer-add list carried into this fork, not a gating ownership rule here. Given the change is a trivial, well-justified partial revert of an upstream tweak with comprehensive tests (the PR reports full wasm.yaml and Debug+ASan --validateExceptionChecks passes), approving is appropriate.

@coderabbitai

coderabbitai Bot commented Sep 9, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Essentials

Run ID: 3695e0f6-f684-4438-864f-48d5e1530809

📥 Commits

Reviewing files that changed from the base of the PR and between 10350dd and 07a1e62.

📒 Files selected for processing (5)
  • JSTests/wasm/js-api/exception-getarg-index-enforce-range.js
  • JSTests/wasm/v8/exceptions-api.js
  • LayoutTests/imported/w3c/web-platform-tests/wasm/jsapi/exception/getArg.tentative.any-expected.txt
  • LayoutTests/imported/w3c/web-platform-tests/wasm/jsapi/exception/getArg.tentative.any.worker-expected.txt
  • Source/JavaScriptCore/wasm/js/WebAssemblyExceptionPrototype.cpp

Included review availability: Your plan provides up to 5 included reviews per hour; 1 remains after this review.


Walkthrough

Changes

The PR updates WebAssembly.Exception.prototype.getArg to enforce unsigned 32-bit index conversion. Tests cover conversion, error types, side effects, evaluation order, and exception propagation.

Changes

Exception.getArg index validation

Layer / File(s) Summary
Index conversion and validation
Source/JavaScriptCore/wasm/js/WebAssemblyExceptionPrototype.cpp, JSTests/wasm/js-api/exception-getarg-index-enforce-range.js
getArg uses unsigned 32-bit range conversion. Tests cover invalid and coercible indices, bounds checks, empty payloads, tag ordering, and conversion exceptions.
Conformance expectations
JSTests/wasm/v8/exceptions-api.js, LayoutTests/imported/w3c/web-platform-tests/wasm/jsapi/exception/getArg.tentative.any-expected.txt, LayoutTests/imported/w3c/web-platform-tests/wasm/jsapi/exception/getArg.tentative.any.worker-expected.txt
Existing expectations validate TypeError for invalid indices and record the current out-of-range behavior.】【。

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to 07a1e

Exception.getArg now reports invalid unsigned index conversions as TypeError while retaining RangeError for valid indices outside the payload; the change is ready to merge.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, specific, and accurately identifies the primary behavior change: invalid unsigned long indices now throw TypeError instead of RangeError.
Description check ✅ Passed The description is detailed and directly explains the problem, fix, expected behavior, changed tests, and validation results. It does not include the template's formal Bugzilla link or Reviewed by lin…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Warning

Git: CodeRabbit could not clone the repository, so clone-backed analysis was skipped and this review may be incomplete. Verify repository clone access, such as SSH credentials, before requesting another full review. If clone access is intentionally unavailable, use path_filters to narrow the review scope.


Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

Preview Builds

Commit Release Date
07a1e624 autobuild-preview-pr-613-07a1e624 2026-09-09 10:00:12 UTC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants