Skip to content

script: Fix assertion failure when stringifying cross-origin location object#43844

Merged
jdm merged 4 commits into
servo:mainfrom
thebabalola:script/fix-cross-origin-console-stringify
Apr 3, 2026
Merged

script: Fix assertion failure when stringifying cross-origin location object#43844
jdm merged 4 commits into
servo:mainfrom
thebabalola:script/fix-cross-origin-console-stringify

Conversation

@thebabalola
Copy link
Copy Markdown
Contributor

@thebabalola thebabalola commented Apr 1, 2026

This fixes the assertion failure (!JS_IsExceptionPending(*cx)) that happens when console.log() is called on a cross-origin location object (e.g. console.log(frame.contentWindow.location)).

The problem was that maybe_stringify_dom_object calls ToString on cross-origin objects, which throws a JS exception via the DissimilarOriginLocation stringifier. That exception was never cleared, so subsequent JS API calls would hit the assertion.

The fix refactors console_argument_from_handle_value using an inner/outer function pattern based on @jdm's suggestion:

  • The inner function returns Result<ConsoleArgument, ()> and returns Err(()) when console_object_from_handle_value returns None for an object, instead of falling through to stringify_handle_value which could trigger the same crash
  • The outer function catches the Err, reports any pending JS exception via report_pending_exception, and returns a fallback ConsoleArgument

Fixes #43530

@thebabalola thebabalola requested a review from gterzian as a code owner April 1, 2026 19:52
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Apr 1, 2026
Copy link
Copy Markdown
Member

@jdm jdm left a comment

Choose a reason for hiding this comment

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

This looks good! Can you write a test that uses https://github.com/web-platform-tests/wpt/blob/0f8fc11ddab5f0462806006b46e3b230f6e8a8d0/cors/preflight-failure.htm#L19 to create the cross-origin iframe? We can use a crash test for that, and it can go in tests/wpt/tests/html/browsers/history/the-location-interface

Copy link
Copy Markdown
Member

@jdm jdm left a comment

Choose a reason for hiding this comment

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

Excellent!

@servo-wpt-sync
Copy link
Copy Markdown
Collaborator

🤖 Opened new upstream WPT pull request (web-platform-tests/wpt#58948) with upstreamable changes.

@thebabalola
Copy link
Copy Markdown
Contributor Author

Excellent!

appreciate the review.... would be waiting for CI to finish : )

@jdm
Copy link
Copy Markdown
Member

jdm commented Apr 2, 2026

Hmm, we will need to add the test path to

# Intentional use of console.*
to avoid one lint failure, then run ./mach update-manifest to fix the rest.

@servo-highfive servo-highfive added S-awaiting-review There is new code that needs to be reviewed. S-needs-rebase There are merge conflict errors. labels Apr 2, 2026
@servo-wpt-sync
Copy link
Copy Markdown
Collaborator

📝 Transplanted new upstreamable changes to existing upstream WPT pull request (web-platform-tests/wpt#58948).

@jdm
Copy link
Copy Markdown
Member

jdm commented Apr 2, 2026

You will need to rebase to the latest changes in main and fix the conflict.

… object

Refactor console_argument_from_handle_value to use an inner function
that returns Result<ConsoleArgument, ()>. When console_object_from_handle_value
returns None for an object (e.g. a cross-origin location), the inner function
returns Err instead of falling through to stringify_handle_value which would
trigger the same assertion. The outer function reports any pending JS exception
and returns a fallback value.

Fixes servo#43530

Signed-off-by: thebabalola <[email protected]>
Add a WPT crash test that loads a cross-origin iframe and calls
console.log on its location object, verifying that the browser
does not crash.

Signed-off-by: thebabalola <[email protected]>
@thebabalola thebabalola force-pushed the script/fix-cross-origin-console-stringify branch from a3700b2 to 425c8ce Compare April 2, 2026 19:46
@servo-highfive servo-highfive removed the S-needs-rebase There are merge conflict errors. label Apr 2, 2026
@servo-wpt-sync
Copy link
Copy Markdown
Collaborator

📝 Transplanted new upstreamable changes to existing upstream WPT pull request (web-platform-tests/wpt#58948).

@thebabalola
Copy link
Copy Markdown
Contributor Author

with CI checks passing.... this should be ready to be merged...

@jdm
Copy link
Copy Markdown
Member

jdm commented Apr 2, 2026

Yep, just need to wait for the CI in the upstream PR to finish as well!

@jdm jdm added this pull request to the merge queue Apr 2, 2026
@servo-highfive servo-highfive added the S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. label Apr 2, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Apr 2, 2026
@servo-highfive servo-highfive added S-tests-failed The changes caused existing tests to fail. and removed S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. labels Apr 2, 2026
@thebabalola
Copy link
Copy Markdown
Contributor Author

Hi @jdm , looks like the merge queue failed on unrelated checks (Linux WPT and OpenHarmony). Could you re-add it when you get a chance? Thanks

@jdm
Copy link
Copy Markdown
Member

jdm commented Apr 3, 2026

We'll need to figure out why the new test timed out:

TIMEOUT [expected PASS] /html/browsers/history/the-location-interface/cross-origin-location-stringify-crash.sub.html

@servo-highfive servo-highfive removed the S-tests-failed The changes caused existing tests to fail. label Apr 3, 2026
@thebabalola
Copy link
Copy Markdown
Contributor Author

thebabalola commented Apr 3, 2026

Fixed... The script was running before the <body> existed, so document.body.appendChild wasn't actually adding the iframe and the onload never fired. Added a tag before the script.
should pass now....

@servo-wpt-sync
Copy link
Copy Markdown
Collaborator

📝 Transplanted new upstreamable changes to existing upstream WPT pull request (web-platform-tests/wpt#58948).

The test script was running in the head, before the body existed,
so document.body.appendChild never added the iframe and the test
timed out.

Signed-off-by: thebabalola <[email protected]>
@thebabalola thebabalola force-pushed the script/fix-cross-origin-console-stringify branch from cb267f2 to faaa968 Compare April 3, 2026 08:43
@servo-wpt-sync
Copy link
Copy Markdown
Collaborator

📝 Transplanted new upstreamable changes to existing upstream WPT pull request (web-platform-tests/wpt#58948).

@jdm jdm added the T-linux-wpt Do a try run of the WPT label Apr 3, 2026
@github-actions github-actions Bot removed the T-linux-wpt Do a try run of the WPT label Apr 3, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 3, 2026

🔨 Triggering try run (#23940601390) for Linux (WPT)

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 3, 2026

Test results for linux-wpt from try job (#23940601390):

Flaky unexpected result (25)
  • OK /FileAPI/url/url-with-fetch.any.worker.html (#21517)
    • FAIL [expected PASS] subtest: Revoke blob URL after calling fetch, fetch should succeed

      promise_test: Unhandled rejection with value: object "TypeError: Network error: Blob URL store error: InvalidFileID"
      

  • CRASH [expected ERROR] /_webgl/conformance/extensions/oes-texture-float-with-canvas.html
  • OK /_webgl/conformance/textures/misc/texture-upload-size.html (#21770)
    • PASS [expected FAIL] subtest: WebGL test #53
    • PASS [expected FAIL] subtest: WebGL test #55
    • PASS [expected FAIL] subtest: WebGL test #57
    • PASS [expected FAIL] subtest: WebGL test #59
  • CRASH [expected OK] /_webgl/conformance2/wasm/readpixels-2gb-in-4gb-wasm-memory.html
  • FAIL [expected PASS] /css/css-backgrounds/background-size-041.html
  • FAIL [expected PASS] /css/css-backgrounds/border-image-repeat-space-9.html
  • OK /css/css-cascade/layer-cssom-order-reverse.html (#36094)
    • FAIL [expected PASS] subtest: Delete layer invalidates @font-face

      assert_equals: expected "220px" but got "133px"
      

  • OK /css/css-fonts/generic-family-keywords-002.html (#40929)
    • FAIL [expected PASS] subtest: font-family: -webkit-serif treated as &lt;font-family&gt;, not &lt;generic-name&gt;

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-sans-serif treated as &lt;font-family&gt;, not &lt;generic-name&gt;

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-cursive treated as &lt;font-family&gt;, not &lt;generic-name&gt;

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-fantasy treated as &lt;font-family&gt;, not &lt;generic-name&gt;

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-monospace treated as &lt;font-family&gt;, not &lt;generic-name&gt;

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-system-ui treated as &lt;font-family&gt;, not &lt;generic-name&gt;

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-math treated as &lt;font-family&gt;, not &lt;generic-name&gt;

      assert_equals: expected 30 but got 50
      

    • PASS [expected FAIL] subtest: font-family: -webkit-generic(fangsong) treated as &lt;font-family&gt;, not &lt;generic-name&gt;
    • PASS [expected FAIL] subtest: font-family: -webkit-generic(kai) treated as &lt;font-family&gt;, not &lt;generic-name&gt;
    • PASS [expected FAIL] subtest: font-family: -webkit-generic(khmer-mul) treated as &lt;font-family&gt;, not &lt;generic-name&gt;
    • And 12 more unexpected results...
  • FAIL [expected PASS] /css/css-ui/webkit-appearance-searchfield-001.html
  • OK /html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click.html (#28697)
    • FAIL [expected PASS] subtest: aElement.click() before the load event must NOT replace

      assert_equals: expected "http://web-platform.test:8000/common/blank.html?thereplacement" but got "http://web-platform.test:8000/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/code-injector.html?pipe=sub(none)&amp;code=%0A%20%20%20%20const%20a%20%3D%20document.createElement(%22a%22)%3B%0A%20%20%20%20a.href%20%3D%20%22%2Fcommon%2Fblank.html%3Fthereplacement%22%3B%0A%20%20%20%20document.currentScript.before(a)%3B%0A%20%20%20%20a.click()%3B%0A%20%20"
      

  • OK /html/browsers/history/the-history-interface/traverse_the_history_2.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • OK [expected TIMEOUT] /html/interaction/focus/the-autofocus-attribute/update-the-rendering.html (#24145)
    • FAIL [expected TIMEOUT] subtest: "Flush autofocus candidates" should be happen before a scroll event and animation frame callbacks

      assert_array_equals: animationFrame lengths differ, expected array ["autofocus", "scroll", "animationFrame"] length 3, got ["animationFrame"] length 1
      

  • OK /html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-1.html (#39694)
    • PASS [expected FAIL] subtest: Meta refresh is blocked by the allow-scripts sandbox flag at its creation time, not when refresh comes due
  • OK /html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-2.html (#39703)
    • FAIL [expected PASS] subtest: Meta refresh of the original iframe is not blocked if moved into a sandboxed iframe

      uncaught exception: Error: assert_unreached: The iframe into which the meta was moved must not refresh Reached unreachable code
      

  • TIMEOUT /html/semantics/embedded-content/media-elements/preserves-pitch.html (#40352)
    • PASS [expected TIMEOUT] subtest: Speed-ups should not change the pitch when preservesPitch=true
    • PASS [expected NOTRUN] subtest: Slow-downs should not change the pitch when preservesPitch=true
    • TIMEOUT [expected NOTRUN] subtest: Speed-ups should change the pitch when preservesPitch=false

      Test timed out
      

  • CRASH [expected ERROR] /html/semantics/forms/the-select-element/customizable-select/select-appearance-button-after-span.html
  • OK /html/semantics/scripting-1/the-script-element/module/dynamic-import/blob-url.any.worker.html (#33909)
    • FAIL [expected PASS] subtest: Revoking a blob URL immediately after calling import will not fail

      promise_test: Unhandled rejection with value: object "TypeError: Module fetching failed"
      

  • TIMEOUT [expected OK] /html/user-activation/navigation-state-reset-sameorigin.html
    • TIMEOUT [expected PASS] subtest: Post-navigation state reset.

      Test timed out
      

  • OK /resource-timing/buffer-full-add-then-clear.html (#40819)
    • FAIL [expected PASS] subtest: Test that if the buffer is cleared after entries were added to the secondary buffer, those entries make it into the primary one

      assert_equals: Number of entries does not match the expected value. expected 3 but got 0
      

  • ERROR [expected OK] /resource-timing/cors-preflight.any.html (#28694)
  • TIMEOUT /trusted-types/trusted-types-navigation.html?31-35 (#38034)
    • TIMEOUT [expected PASS] subtest: Navigate a frame via form-submission with javascript:-urls in report-only mode.

      Test timed out
      

    • NOTRUN [expected TIMEOUT] subtest: Navigate a frame via form-submission with javascript:-urls w/ default policy in report-only mode.
  • OK /visual-viewport/resize-event-order.html (#41981)
    • PASS [expected FAIL] subtest: Popup: DOMWindow resize fired before VisualViewport.
  • OK [expected ERROR] /webxr/render_state_update.https.html (#27535)
  • CRASH [expected OK] /workers/WorkerGlobalScope_importScripts.htm
  • CRASH [expected OK] /xhr/access-control-preflight-sync-header-denied.htm
Stable unexpected results that are known to be intermittent (18)
  • TIMEOUT /FileAPI/url/url-in-tags-revoke.window.html (#19978)
    • PASS [expected TIMEOUT] subtest: Fetching a blob URL immediately before revoking it works in &lt;script&gt; tags.
  • FAIL [expected PASS] /_mozilla/mozilla/sslfail.html (#10760)
  • TIMEOUT [expected OK] /_mozilla/mozilla/window_resize_event.html (#36741)
    • TIMEOUT [expected PASS] subtest: Popup onresize event fires after resizeTo

      Test timed out
      

  • OK /css/css-fonts/generic-family-keywords-001.html (#37467)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(fangsong)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(kai)
  • OK /css/css-fonts/generic-family-keywords-003.html (#38994)
    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted cursive (drawing text in a canvas)

      assert_equals: quoted cursive matches  @font-face rule expected 125 but got 40
      

    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted fantasy (drawing text in a canvas)

      assert_equals: quoted fantasy matches  @font-face rule expected 125 but got 40
      

    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted monospace (drawing text in a canvas)

      assert_equals: quoted monospace matches  @font-face rule expected 125 but got 40
      

    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted system-ui (drawing text in a canvas)

      assert_equals: quoted system-ui matches  @font-face rule expected 125 but got 40
      

  • OK [expected ERROR] /fetch/fetch-later/quota/same-origin-iframe/multiple-iframes.https.window.html (#35176)
  • OK /fetch/metadata/generated/css-font-face.https.sub.tentative.html (#32732)
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Cross-site
  • OK /fetch/metadata/generated/css-font-face.sub.tentative.html (#34624)
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Not sent to non-trustworthy same-site destination
  • TIMEOUT /fetch/metadata/generated/css-images.https.sub.tentative.html (#42229)
    • FAIL [expected PASS] subtest: content sec-fetch-site - Cross-Site -&gt; Same-Site

      assert_unreached: Reached unreachable code
      

  • ERROR [expected OK] /focus/focus-event-after-switching-iframes.sub.html (#40368)
  • TIMEOUT /html/interaction/focus/the-autofocus-attribute/supported-elements.html (#24145)
    • PASS [expected TIMEOUT] subtest: Non-HTMLElement should not support autofocus
    • TIMEOUT [expected NOTRUN] subtest: Host element with delegatesFocus should support autofocus

      Test timed out
      

  • OK /html/webappapis/user-prompts/print-during-unload.html (#35944)
    • FAIL [expected PASS] subtest: print() during unload

      assert_array_equals: expected property 1 to be "destination" but got "error: window.print is not a function" (expected array ["start", "destination"] got ["start", "error: window.print is not a function"])
      

  • OK /mixed-content/tentative/autoupgrades/mixed-content-cors.https.sub.html (#41123)
    • PASS [expected FAIL] subtest: Cross-Origin video should get upgraded even if CORS is set
  • OK /navigation-timing/test-navigation-type-reload.html (#33334)
    • FAIL [expected PASS] subtest: Reload domContentLoadedEventEnd &gt; Original domContentLoadedEventEnd

      assert_true: Reload domContentLoadedEventEnd &gt; Original domContentLoadedEventEnd expected true got false
      

    • FAIL [expected PASS] subtest: Reload domContentLoadedEventStart &gt; Original domContentLoadedEventStart

      assert_true: Reload domContentLoadedEventStart &gt; Original domContentLoadedEventStart expected true got false
      

  • TIMEOUT [expected OK] /preload/modulepreload-sri.html (#43354)
    • TIMEOUT [expected PASS] subtest: Script should not be loaded if modulepreload's integrity is invalid

      Test timed out
      

  • OK [expected CRASH] /resource-timing/render-blocking-status-link.html (#41664)
  • OK /touch-events/single-tap-when-touchend-listener-use-sync-xhr.html (#41175)
    • FAIL [expected PASS] subtest: Click event should be fired when touchend opens synchronous XHR

      assert_equals: expected "touchend@div, mousedown@div, mouseup@div, click@div" but got "touchend@div"
      

  • OK /trusted-types/trusted-types-reporting.html (#43737)
    • PASS [expected FAIL] subtest: Trusted Type violation report: creating a forbidden-but-not-reported policy.
    • PASS [expected FAIL] subtest: Trusted Type violation report: sample for SVGScriptElement href assignment by setAttribute

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 3, 2026

✨ Try run (#23940601390) succeeded.

@jdm jdm added this pull request to the merge queue Apr 3, 2026
@servo-highfive servo-highfive added the S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. label Apr 3, 2026
Merged via the queue into servo:main with commit ac71215 Apr 3, 2026
68 checks passed
@servo-highfive servo-highfive removed the S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. label Apr 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-awaiting-review There is new code that needs to be reviewed.

Projects

None yet

4 participants