Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix crash caused by arithmetic underflow in layout2020 #30897

Merged
merged 5 commits into from Dec 21, 2023

Conversation

atbrakhi
Copy link
Member

@atbrakhi atbrakhi commented Dec 20, 2023

Currently we are trying to subtract a non-zero number from 0 in self.justification_opportunities -= spaces_trimmed, which is resulting in an arithmetic underflow in the trim_trailing_whitespace inside inline.rs, hence the crash.

This PR prevents the underflow by using checked_sub for safe subtraction.


@atbrakhi atbrakhi added the T-full Do a full try run label Dec 20, 2023
@github-actions github-actions bot removed the T-full Do a full try run label Dec 20, 2023
Copy link

🔨 Triggering try run (#7273542356) with platforms=linux,macos,windows and layout=all

Copy link

Test results for linux-wpt-layout-2013 from try job (#7273542356):

Flaky unexpected result (17)
  • CRASH [expected PASS] /_webgl/conformance/glsl/bugs/nested-functions-should-not-crash.html (#30680)
  • TIMEOUT [expected OK] /_webgl/conformance/uniforms/out-of-bounds-uniform-array-access.html (#26225)
    • NOTRUN [expected PASS] subtest: Overall test
  • OK /css/cssom-view/MediaQueryList-addListener-removeListener.html (#24569)
    • FAIL [expected PASS] subtest: listeners are called correct number of times assert_equals: expected 10 but got 9
    • FAIL [expected PASS] subtest: removing listener from one MQL doesn't remove it from all MQLs assert_equals: expected 1 but got 0
  • TIMEOUT /fetch/metadata/generated/css-images.sub.tentative.html (#29047)
    • TIMEOUT [expected PASS] subtest: background-image sec-fetch-site - HTTPS downgrade (header not sent) Test timed out
  • CRASH [expected PASS] /html/browsers/origin/origin-keyed-agent-clusters/popups-crash.https.html
  • OK /html/browsers/the-window-object/open-close/creating_browsing_context_test_01.html (#29046)
    • PASS [expected FAIL] subtest: first argument: absolute url
  • FAIL [expected CRASH] /html/canvas/element/manual/text/canvas.2d.disconnected.html (#30063)
  • TIMEOUT [expected OK] /html/interaction/focus/the-autofocus-attribute/document-with-fragment-empty.html (#28259)
    • TIMEOUT [expected FAIL] subtest: Autofocus elements in top-level browsing context's documents with empty fragments should work. Test timed out
  • TIMEOUT [expected OK] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-2.html (#22667)
    • TIMEOUT [expected FAIL] subtest: Check that popups from a sandboxed iframe escape the sandbox if
      allow-popups-to-escape-sandbox is used Test timed out
  • OK /html/semantics/forms/historical.html (#28568)
    • PASS [expected FAIL] subtest: <input name=isindex> should not be supported
  • OK /html/syntax/parsing/DOMContentLoaded-defer.html (#21550)
    • PASS [expected FAIL] subtest: The end: DOMContentLoaded and defer scripts
  • OK /html/webappapis/dynamic-markup-insertion/document-write/module-static-import-delayed.html (#26243)
    • PASS [expected FAIL] subtest: document.write in an imported module
  • TIMEOUT /html/webappapis/scripting/events/compile-event-handler-settings-objects.html (#24246)
    • FAIL [expected PASS] subtest: The entry settings object while executing the compiled callback via Web IDL's invoke must be that of the node document assert_equals: expected "/html/webappapis/scripting/events/resources/open-window.html" but got "blank"
  • CRASH [expected PASS] /streams/readable-streams/crashtests/strategy-worker-terminate.html (#30124)
  • OK [expected TIMEOUT] /webaudio/the-audio-api/the-audiocontext-interface/audiocontext-not-fully-active.html (#27664)
  • TIMEOUT [expected OK] /webmessaging/without-ports/018.html (#24485)
    • TIMEOUT [expected PASS] subtest: origin of the script that invoked the method, javascript: Test timed out
  • OK [expected TIMEOUT] /webstorage/localstorage-about-blank-3P-iframe-opens-3P-window.partitioned.tentative.html (#29053)
    • PASS [expected TIMEOUT] subtest: StorageKey: test 3P about:blank window opened from a 3P iframe
Stable unexpected results that are known to be intermittent (18)
  • TIMEOUT [expected OK] /_webgl/conformance/glsl/misc/shader-uniform-packing-restrictions.html (#28103)
  • TIMEOUT [expected OK] /_webgl/conformance/glsl/misc/shader-with-non-reserved-words.html (#16216)
    • NOTRUN [expected PASS] subtest: Overall test
  • TIMEOUT [expected FAIL] /css/compositing/mix-blend-mode/mix-blend-mode-animation.html (#21930)
  • FAIL [expected PASS] /css/css-text/white-space/trailing-other-space-separators-break-spaces-008.html (#25875)
  • OK /css/cssom-view/MediaQueryList-extends-EventTarget-interop.html (#25285)
    • PASS [expected FAIL] subtest: listener added with addListener and addEventListener is called once
  • OK /css/cssom-view/offsetTopLeft-border-box.html (#24237)
    • PASS [expected FAIL] subtest: container: 0
    • PASS [expected FAIL] subtest: container: 1
  • OK /html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-iframe-contentWindow.html (#28681)
    • PASS [expected FAIL] subtest: load & pageshow events do not fire on contentWindow of <iframe> element created with src=''
  • OK /html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling-dynamic.html (#28066)
    • FAIL [expected PASS] subtest: 0041 set in href="" targeting a frame and clicked assert_equals: expected "A" but got ""
    • FAIL [expected PASS] subtest: 0080 00FF set in href="" targeting a frame and clicked assert_equals: expected "�ÿ" but got ""
    • FAIL [expected PASS] subtest: 0080 00FF 0100 set in href="" targeting a frame and clicked assert_equals: expected "�ÿĀ" but got ""
  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-cross-origin.sub.window.html (#29056)
    • FAIL [expected PASS] subtest: Cross-origin navigation started from unload handler must be ignored promise_test: Unhandled rejection with value: object "SecurityError: The operation is insecure."
  • OK /html/browsers/history/the-history-interface/traverse_the_history_3.html (#21383)
    • FAIL [expected PASS] subtest: Multiple history traversals, last would be aborted assert_array_equals: Pages opened during history navigation expected property 1 to be 3 but got 2 (expected array [6, 3] got [6, 2])
  • OK /html/browsers/history/the-history-interface/traverse_the_history_5.html (#21383)
    • FAIL [expected PASS] subtest: Multiple history traversals, last would be aborted assert_array_equals: Pages opened during history navigation expected property 1 to be 5 but got 3 (expected array [6, 5] got [6, 3])
  • OK [expected TIMEOUT] /html/interaction/focus/the-autofocus-attribute/supported-elements.html (#24145)
    • FAIL [expected TIMEOUT] subtest: Area element should support autofocus promise_test: Unhandled rejection with value: object "TypeError: w.document.querySelector(...) is null"
  • OK [expected CRASH] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-1.html (#22647)
  • OK /html/semantics/forms/form-submission-0/multipart-formdata.window.html (#28725)
    • FAIL [expected PASS] subtest: multipart/form-data: backslash in name (formdata event) assert_equals: expected "\r\nContent-Disposition: form-data; name="a\b"\r\n\r\nc\r\n--\r\n" but got ""
  • OK /html/semantics/forms/form-submission-0/text-plain.window.html (#28687)
    • FAIL [expected PASS] subtest: text/plain: backslash in name (formdata event) assert_equals: expected "a\b=c\r\n" but got ""
  • OK [expected TIMEOUT] /html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry.html (#25805)
  • OK /webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html (#22849)
    • FAIL [expected PASS] subtest: X Stitched sine-wave buffers at sample rate 43800 does not equal [0,0.06264832615852356,0.12505052983760834,0.18696144223213196,0.24813786149024963,0.308339387178421,0.36732959747314453,0.4248766601085663,0.480754554271698,0.5347436666488647,0.5866320133209229,0.6362156271934509,0.6832997798919678,0.7276994585990906,0.7692402601242065,0.8077589869499207...] with an element-wise tolerance of {"absoluteThreshold":0.0038986,"relativeThreshold":0}.
      Index Actual Expected AbsError RelError Test threshold
      [15073] 7.0995013264089648e+33 6.4605611562728882e-1 7.0995013264089648e+33 1.0988985561286261e+34 3.8985999999999999e-3
      [15074] 2.5936898589134216e-1 5.9696805477142334e-1 3.3759906888008118e-1 5.6552283858697683e-1 3.8985999999999999e-3
      Max AbsError of 7.0995013264089648e+33 at index of 15073.
      Max RelError of 1.0988985561286261e+34 at index of 15073.
      assert_true: expected true got false
    • FAIL [expected PASS] subtest: X SNR (-633.5904709492256 dB) is not greater than or equal to 65.737. Got -633.5904709492256. assert_true: expected true got false
  • OK [expected TIMEOUT] /webmessaging/with-ports/018.html (#24485)
    • PASS [expected TIMEOUT] subtest: origin of the script that invoked the method, javascript:

Copy link

Test results for linux-wpt-layout-2020 from try job (#7273542356):

Flaky unexpected result (15)
  • TIMEOUT [expected OK] /FileAPI/url/url-charset.window.html (#26997)
    • TIMEOUT [expected PASS] subtest: Blob charset should override any auto-detected charset. Test timed out
  • TIMEOUT [expected OK] /_webgl/conformance/uniforms/out-of-bounds-uniform-array-access.html (#26225)
    • NOTRUN [expected PASS] subtest: Overall test
  • OK /css/cssom-view/matchMedia.html
    • PASS [expected FAIL] subtest: iframe.matchMedia("(min-width: 150px)") matches
  • TIMEOUT /fetch/metadata/generated/element-img-environment-change.sub.html (#30111)
    • PASS [expected FAIL] subtest: sec-fetch-site - Not sent to non-trustworthy same-origin destination, no attributes
  • OK /html/browsers/browsing-the-web/navigating-across-documents/empty-iframe-load-event.html (#29066)
    • PASS [expected FAIL] subtest: Check execution order on load handler
    • PASS [expected FAIL] subtest: Check execution order from nested timeout
  • OK /html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-navigate-immediately.html (#29048)
    • PASS [expected FAIL] subtest: Navigating to a different document with form submission
  • OK /html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-iframe-contentWindow.html (#28681)
    • FAIL [expected PASS] subtest: load & pageshow events do not fire on contentWindow of <iframe> element created with src='about:blank' assert_unreached: load should not be fired Reached unreachable code
  • TIMEOUT /html/browsers/browsing-the-web/navigating-across-documents/javascript-url-no-beforeunload.window.html (#29055)
    • PASS [expected FAIL] subtest: Navigating an opened window with an iframe via location.href to a javascript: URL must not fire beforeunload on the iframe: undefined completion
  • TIMEOUT [expected OK] /html/browsers/browsing-the-web/navigating-across-documents/navigate-to-unparseable-url.html (#29050)
    • TIMEOUT [expected FAIL] subtest: location.href setter throws a SyntaxError DOMException for unparseable URLs Test timed out
    • NOTRUN [expected FAIL] subtest: <a> tag navigate fails for unparseable URLs
  • OK /html/browsers/history/the-history-interface/traverse_the_history_4.html (#21383)
    • FAIL [expected PASS] subtest: Multiple history traversals, last would be aborted assert_array_equals: Pages opened during history navigation expected property 1 to be 5 but got 3 (expected array [6, 5] got [6, 3])
  • OK /html/semantics/forms/form-submission-0/urlencoded2.window.html (#28687)
    • PASS [expected FAIL] subtest: application/x-www-form-urlencoded: 0x00 in value (normal form)
  • OK /html/webappapis/dynamic-markup-insertion/document-write/module-static-import-delayed.html (#26243)
    • FAIL [expected PASS] subtest: document.write in an imported module assert_true: onload must be called expected true got false
  • OK /html/webappapis/dynamic-markup-insertion/document-write/module-tla-delayed.html (#29137)
    • FAIL [expected PASS] subtest: document.write in an imported module assert_true: onload must be called expected true got false
  • TIMEOUT [expected OK] /webmessaging/with-ports/017.html (#24486)
    • TIMEOUT [expected PASS] subtest: origin of the script that invoked the method, about:blank Test timed out
  • TIMEOUT [expected OK] /webmessaging/without-ports/018.html (#24485)
    • TIMEOUT [expected PASS] subtest: origin of the script that invoked the method, javascript: Test timed out
Stable unexpected results that are known to be intermittent (14)
  • TIMEOUT [expected OK] /_webgl/conformance/glsl/misc/shader-uniform-packing-restrictions.html (#28103)
    • NOTRUN [expected PASS] subtest: Overall test
  • TIMEOUT [expected OK] /_webgl/conformance/glsl/misc/shader-with-non-reserved-words.html (#16216)
    • NOTRUN [expected PASS] subtest: Overall test
  • OK /css/css-fonts/variations/at-font-face-font-matching.html (#20684)
    • PASS [expected FAIL] subtest: Matching font-weight: '430' should prefer '500' over '400 425'
    • FAIL [expected PASS] subtest: Matching font-weight: '430' should prefer '400 425' over '350 399' assert_equals: Unexpected font on test element expected 487 but got 532
    • PASS [expected FAIL] subtest: Matching font-weight: '430' should prefer '340 398' over '501 550'
    • PASS [expected FAIL] subtest: Matching font-weight: '500' should prefer '450 460' over '400'
    • PASS [expected FAIL] subtest: Matching font-weight: '500' should prefer '400' over '350 399'
    • FAIL [expected PASS] subtest: Matching font-weight: '501' should prefer '501' over '502 510' assert_equals: Unexpected font on test element expected 487 but got 532
    • FAIL [expected PASS] subtest: Matching font-weight: '501' should prefer '503 520' over '500' assert_equals: Unexpected font on test element expected 487 but got 532
    • FAIL [expected PASS] subtest: Matching font-weight: '399' should prefer '400' over '450 460' assert_equals: Unexpected font on test element expected 487 but got 532
    • PASS [expected FAIL] subtest: Matching font-weight: '399' should prefer '500 501' over '502 510'
    • FAIL [expected PASS] subtest: Matching font-stretch: '100%' should prefer '100%' over '110% 120%' assert_equals: Unexpected font on test element expected 487 but got 532
    • And 12 more unexpected results...
  • OK /css/cssom-view/MediaQueryList-addListener-removeListener.html (#24569)
    • PASS [expected FAIL] subtest: listeners are called correct number of times
  • TIMEOUT /fetch/metadata/generated/css-images.sub.tentative.html (#29047)
    • PASS [expected TIMEOUT] subtest: background-image sec-fetch-site - HTTPS downgrade (header not sent)
  • TIMEOUT /fetch/metadata/generated/element-img-environment-change.https.sub.html (#30111)
    • TIMEOUT [expected FAIL] subtest: sec-fetch-site - Cross-site, no attributes Test timed out
    • NOTRUN [expected FAIL] subtest: sec-fetch-site - Same site, no attributes
    • NOTRUN [expected TIMEOUT] subtest: sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect, no attributes
  • OK [expected CRASH] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-2.html (#22667)
  • TIMEOUT [expected OK] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html (#24066)
    • NOTRUN [expected FAIL] subtest: Check that popups from a sandboxed iframe do not escape the sandbox
  • OK /html/semantics/forms/form-submission-0/multipart-formdata.window.html (#28725)
    • FAIL [expected PASS] subtest: multipart/form-data: 0x00 in name (normal form) assert_equals: expected "\r\nContent-Disposition: form-data; name="a\0b"\r\n\r\nc\r\n--\r\n" but got ""
  • OK /html/semantics/forms/form-submission-0/text-plain.window.html (#28687)
    • FAIL [expected PASS] subtest: text/plain: double quote in value (formdata event) assert_equals: expected "a=b"c\r\n" but got ""
  • ERROR [expected OK] /html/semantics/scripting-1/the-script-element/defer-script/async-script.html?reload (#29054)
  • TIMEOUT /html/webappapis/scripting/events/compile-event-handler-settings-objects.html (#24246)
    • PASS [expected FAIL] subtest: The entry settings object while executing the compiled callback via Web IDL's invoke must be that of the node document
  • OK /webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html (#22849)
    • FAIL [expected PASS] subtest: X Stitched sine-wave buffers at sample rate 43800 does not equal [0,0.06264832615852356,0.12505052983760834,0.18696144223213196,0.24813786149024963,0.308339387178421,0.36732959747314453,0.4248766601085663,0.480754554271698,0.5347436666488647,0.5866320133209229,0.6362156271934509,0.6832997798919678,0.7276994585990906,0.7692402601242065,0.8077589869499207...] with an element-wise tolerance of {"absoluteThreshold":0.0038986,"relativeThreshold":0}.
      Index Actual Expected AbsError RelError Test threshold
      [15073] -7.4345410180874913e-20 6.4605611562728882e-1 6.4605611562728882e-1 1.0000000000000000e+0 3.8985999999999999e-3
      [15074] 2.5936898589134216e-1 5.9696805477142334e-1 3.3759906888008118e-1 5.6552283858697683e-1 3.8985999999999999e-3
      Max AbsError of 6.4605611562728882e-1 at index of 15073.
      Max RelError of 1.0000000000000000e+0 at index of 15073.
      assert_true: expected true got false
  • TIMEOUT [expected OK] /webmessaging/without-ports/017.html (#24486)
    • TIMEOUT [expected PASS] subtest: origin of the script that invoked the method, about:blank Test timed out

Copy link

✨ Try run (#7273542356) succeeded.

@servo-wpt-sync
Copy link
Collaborator

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

@servo-wpt-sync
Copy link
Collaborator

✍ Updated existing upstream WPT pull request (web-platform-tests/wpt#43745) title and body.

@atbrakhi atbrakhi marked this pull request as ready for review December 20, 2023 11:47
@servo-wpt-sync
Copy link
Collaborator

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

Comment on lines 182 to 185
self.justification_opportunities = self
.justification_opportunities
.checked_sub(spaces_trimmed)
.unwrap_or(0);
Copy link
Member

Choose a reason for hiding this comment

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

I think this might be papering over the issue. We shouldn't be subtracting more justification opportunities than we counted up to this point.

@mrobinson
Copy link
Member

Looks like the issue is that when processing the last line, I zeroed out the number of justification opportunities. That's not going to work when subtracting later in finish_line_and_reset(). :) Here's another fix which I think gets closer to fixing the root of the issue:

diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs
index 2b99cd63cd..ca5360469a 100644
--- a/components/layout_2020/flow/inline.rs
+++ b/components/layout_2020/flow/inline.rs
@@ -535,23 +535,23 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
         // there is no line wrapping, so this forces the segment into the current line.
         self.commit_current_segment_to_line();
 
-        // This has the effect of preventing the application of `text-align: justify` to
-        // this line because no justification opportunities means no justification.
-        self.current_line.justification_opportunities = 0;
-
         // Finally we finish the line itself and convert all of the LineItems into
         // fragments.
-        self.finish_current_line_and_reset();
+        self.finish_current_line_and_reset(true /* last_line */);
     }
 
     /// Finish layout of all inline boxes for the current line. This will gather all
     /// [`LineItem`]s and turn them into [`Fragment`]s, then reset the
     /// [`InlineFormattingContextState`] preparing it for laying out a new line.
-    fn finish_current_line_and_reset(&mut self) {
+    fn finish_current_line_and_reset(&mut self, last_line: bool) {
         let whitespace_trimmed = self.current_line.trim_trailing_whitespace();
-        let (inline_start_position, justification_adjustment) = self
+        let (inline_start_position, mut justification_adjustment) = self
             .calculate_current_line_inline_start_and_justification_adjustment(whitespace_trimmed);
 
+        if last_line {
+            justification_adjustment = Length::zero();
+        }
+
         let block_start_position = self
             .current_line
             .line_block_start_considering_placement_among_floats();
@@ -1029,7 +1029,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
                 &self.current_line,
                 self.inline_box_state_stack.len(),
             );
-        self.finish_current_line_and_reset();
+        self.finish_current_line_and_reset(false /* last_line */);
     }
 
     /// Process a soft wrap opportunity. This will either commit the current unbreakble

@servo-wpt-sync
Copy link
Collaborator

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

Copy link
Member

@mrobinson mrobinson left a comment

Choose a reason for hiding this comment

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

Looks good to me, though I would remove the WPT test. I think this one is covered by existing tests. We just don't run them in debug mode so we didn't catch it.

@servo-wpt-sync
Copy link
Collaborator

🤖 This change no longer contains upstreamable changes to WPT; closed existing upstream pull request (web-platform-tests/wpt#43745).

@atbrakhi atbrakhi added this pull request to the merge queue Dec 21, 2023
Merged via the queue into servo:main with commit a9bf29c Dec 21, 2023
10 checks passed
@atbrakhi atbrakhi deleted the fix_crash_in_layout2020 branch December 21, 2023 10:17
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.

Servo crash when loading servo.org
3 participants