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

Prevent crashing when a link tag has two or more in-flight requests (fix for issue #15101) #15145

Merged
merged 2 commits into from Jan 24, 2017

Conversation

@SwagColoredKitteh
Copy link
Contributor

SwagColoredKitteh commented Jan 22, 2017

The HTMLLinkElement::set_stylesheet function now checks whether there already is a stylesheet, and if there is, calls Document::invalidate_stylesheets after modifying self.stylesheet.

This PR also includes a minimal WPT that causes the panic.

This is fundamentally a timing issue, so while this fix prevents the crash, it does not fix the underlying issue. Making a <link> element send a second request before the first can finish and then getting the two stylesheet responses out-of-order will apply the wrong stylesheet, as demonstrated with https://gist.github.com/SwagColoredKitteh/2c24c7fac635445042eda4a30e10420e.

r? @emilio


  • ./mach build -d does not report any errors
  • ./mach test-tidy does not report any errors
  • These changes fix #15101 (github issue number if applicable).
  • There are tests for these changes OR
  • These changes do not require tests because _____

This change is Reviewable

@highfive
Copy link

highfive commented Jan 22, 2017

Thanks for the pull request, and welcome! The Servo team is excited to review your changes, and you should hear from @emilio (or someone else) soon.

@highfive
Copy link

highfive commented Jan 22, 2017

Heads up! This PR modifies the following files:

  • @fitzgen: components/script/dom/htmllinkelement.rs
  • @KiChjang: components/script/dom/htmllinkelement.rs
@jdm
Copy link
Member

jdm commented Jan 22, 2017

The way we handle the timing issue in other code like XMLHttpRequest is to store a monotonically-increasing id in the element and the network load context, and compare them in the implementation of PreInvoke.

@SwagColoredKitteh
Copy link
Contributor Author

SwagColoredKitteh commented Jan 22, 2017

Alright, should I add that to this PR? Is there also a way to write a WPT that checks for these out-of-order responses?

@jdm
Copy link
Member

jdm commented Jan 22, 2017

You could write a test that first loads a resource that has a 3 second response delay, then immediately loads a second one with no delay, then waits for 4 seconds and queries the computed style of an element that would be affected.

assert!(self.stylesheet.borrow().is_none());
*self.stylesheet.borrow_mut() = Some(s);
let mut sheet = self.stylesheet.borrow_mut();
let must_invalidate = sheet.is_some();

This comment has been minimized.

@emilio

emilio Jan 23, 2017

Member

I think we don't need to call invalidate_stylesheets here, since the stylesheet loader will do it right after calling set_stylesheet.

Remove this code and maybe add a note about it?

This comment has been minimized.

@SwagColoredKitteh

SwagColoredKitteh Jan 23, 2017

Author Contributor

It might be better to just leave the assert there as it is and do what @jdm suggested, which would fix the underlying issue instead of clear the symptoms. That assert will continue to be useful to find timing issues such as what caused this issue in the first place.

This comment has been minimized.

@emilio

emilio Jan 23, 2017

Member

Note that we still need to support setting a stylesheet when it is loaded and the href changes, or at least (probably preferably?) add support for unsetting it and invalidate_stylesheets after the href changes.

So yeah, let's keep the assertion there, let's add the load counter, and also let's move this must_invalidate logic to handle_stylesheet_url, and unset the stylesheet there.

@SwagColoredKitteh SwagColoredKitteh force-pushed the SwagColoredKitteh:issue-15101 branch from 367c10d to 23a5fdc Jan 23, 2017
@SwagColoredKitteh
Copy link
Contributor Author

SwagColoredKitteh commented Jan 23, 2017

Did what @jdm suggested, out-of-order stylesheet responses for the same element should be handled correctly now. There are also 2 tests which verify that this works correctly. These tests do time out, even though they should not, though. Added exceptions for those timeouts in the inis files.

@jdm
Copy link
Member

jdm commented Jan 23, 2017

#15159 was filed for followup investigation into the timeouts.

@@ -37,6 +37,15 @@ use stylesheet_loader::{StylesheetLoader, StylesheetContextSource, StylesheetOwn

unsafe_no_jsmanaged_fields!(Stylesheet);

#[derive(JSTraceable, PartialEq, Clone, Copy, HeapSizeOf)]
pub struct GenerationId(u32);

This comment has been minimized.

@emilio

emilio Jan 23, 2017

Member

nit: Maybe RequestGenerationId?

fn should_invoke(&self) -> bool {
// We must first check whether the generations of the context and the element match up,
// else we risk applying the wrong stylesheet when responses come out-of-order.
let gen = self.elem.root()

This comment has been minimized.

@emilio

emilio Jan 23, 2017

Member

We should invoke the callback always, just not set the stylesheet if the generation doesn't match. Otherwise we will lose track of the pending load count, which means we'll never fire the load event.

This comment has been minimized.

@emilio

emilio Jan 23, 2017

Member

Also, elem may be a <style> element where there's an @import, or the load could be for an @import rule in general, in which case the generation id doesn't matter. So we should do the check in process_response_eof, an we should check that StylesheetContextSource is a LinkElement.

Note that the imports can theoretically have the same race if mutated from CSSOM before the load finishes, but we don't expose that right now to the web, so it can't happen, so no need to worry about this in this PR.

This comment has been minimized.

@emilio

emilio Jan 23, 2017

Member

Actually, the above comment may be the reason why the tests timeout :)

@@ -215,13 +227,17 @@ impl<'a> StylesheetLoader<'a> {
integrity_metadata: String) {
let url = source.url();
let document = document_from_node(self.elem);
let gen = self.elem.downcast::<HTMLLinkElement>()

This comment has been minimized.

@emilio

emilio Jan 23, 2017

Member

Similarly, you can't rely on this being a HTMLLinkElement.

@SwagColoredKitteh
Copy link
Contributor Author

SwagColoredKitteh commented Jan 24, 2017

Fixed issue #15159 and made sure that style elements with imports do not crash servo by not assuming that some downcasts always succeed, also did a few cleanups.

@emilio
emilio approved these changes Jan 24, 2017
Copy link
Member

emilio left a comment

Looks great! I have one minor request, after that this is good to go :)

Thanks for fixing this @SwagColoredKitteh!

let link = elem.downcast::<HTMLLinkElement>().unwrap();
// We must first check whether the generations of the context and the element match up,
// else we risk applying the wrong stylesheet when responses come out-of-order.
let should_continue = match self.request_generation_id {

This comment has been minimized.

@emilio

emilio Jan 24, 2017

Member

let's give this variable a meaningful name, something like:

let is_applicable_stylesheet_load =
    self.request_generation_id.map_or(true, |id| id == link.get_request_generation_id());

(or other name if you want, I'm pretty bad at it :))

// else we risk applying the wrong stylesheet when responses come out-of-order.
let should_continue = match self.request_generation_id {
Some(gen) => gen == link.get_request_generation_id(),
None => true,

This comment has been minimized.

@emilio

emilio Jan 24, 2017

Member

nit: I don't think we use to indent fat arrows visually. In any case, you may be able to write this in a more compact way with map_or, as I did above.

@emilio
Copy link
Member

emilio commented Jan 24, 2017

Also, travis seems to be complaining about the manifest order, you need to fix that I think: You can run etc/ci/manifest_changed.sh locally.

@SwagColoredKitteh SwagColoredKitteh force-pushed the SwagColoredKitteh:issue-15101 branch from 86f3209 to abebf34 Jan 24, 2017
@emilio
Copy link
Member

emilio commented Jan 24, 2017

@bors-servo r+

Thanks for doing this! :)

@bors-servo
Copy link
Contributor

bors-servo commented Jan 24, 2017

📌 Commit abebf34 has been approved by emilio

@SwagColoredKitteh
Copy link
Contributor Author

SwagColoredKitteh commented Jan 24, 2017

@emilio My pleasure! I had a lot of fun searching around the codebase and figuring out what went wrong. :D

@SwagColoredKitteh SwagColoredKitteh force-pushed the SwagColoredKitteh:issue-15101 branch from abebf34 to 068f960 Jan 24, 2017
@jdm
Copy link
Member

jdm commented Jan 24, 2017

@bors-servo: r=emilio

@bors-servo
Copy link
Contributor

bors-servo commented Jan 24, 2017

📌 Commit 068f960 has been approved by emilio

@bors-servo
Copy link
Contributor

bors-servo commented Jan 24, 2017

Testing commit 068f960 with merge c3f0c90...

bors-servo added a commit that referenced this pull request Jan 24, 2017
Prevent crashing when a link tag has two or more in-flight requests (fix for issue #15101)

<!-- Please describe your changes on the following line: -->
The `HTMLLinkElement::set_stylesheet` function now checks whether there already is a stylesheet, and if there is, calls `Document::invalidate_stylesheets` after modifying `self.stylesheet`.

This PR also includes a minimal WPT that causes the panic.

This is fundamentally a timing issue, so while this fix prevents the crash, it does not fix the underlying issue. Making a &lt;link&gt; element send a second request before the first can finish and then getting the two stylesheet responses out-of-order will apply the wrong stylesheet, as demonstrated with https://gist.github.com/SwagColoredKitteh/2c24c7fac635445042eda4a30e10420e.

r? @emilio

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [X] These changes fix #15101 (github issue number if applicable).

<!-- Either: -->
- [X] There are tests for these changes OR
- [ ] These changes do not require tests because _____

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/15145)
<!-- Reviewable:end -->
@bors-servo bors-servo merged commit 068f960 into servo:master Jan 24, 2017
3 checks passed
3 checks passed
continuous-integration/appveyor/pr AppVeyor build succeeded
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
homu Test successful
Details
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

5 participants
You can’t perform that action at this time.