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

read_blob returns a reflector to an unrooted DOM object #21164

Closed
jdm opened this issue Jul 11, 2018 · 5 comments
Closed

read_blob returns a reflector to an unrooted DOM object #21164

jdm opened this issue Jul 11, 2018 · 5 comments

Comments

@jdm
Copy link
Member

@jdm jdm commented Jul 11, 2018

This code creates a Blob, which is rooted in the current stack frame, then returns its reflector's JSObject pointer. This means that the blob object is no longer rooted, and depending on what the JS engine does with the return value of read_callback, the pointer could be pointing at a DOM object that gets GCed by the time it's used. We should look carefully at what Firefox does here.

@gterzian
Copy link
Member

@gterzian gterzian commented Jul 14, 2018

Is the rooting handled at the point of use? For example the below, where data is StructuredCloneData:

data.read(scope.upcast(), message.handle_mut());

@gterzian
Copy link
Member

@gterzian gterzian commented Jul 14, 2018

Or do you mean literally that the GC would happen 'right in the middle' such a call? Perhaps this is protected by the fact that the "reading" happens inside of this:

assert!(JS_ReadStructuredClone(cx,

I also remember pretty much copy pasting the whole structure from gecko when I worked on this :) (although perhaps not creating the blob just to return the pointer)

@gterzian
Copy link
Member

@gterzian gterzian commented Jul 14, 2018

Lastly, I remember reading through the actual implementation of JS_ReadStructuredClone(for some reason it seemed like the SpiderMonkey code was right inside the Gecko codebase), and the way it works is that the JS engine will try to internally handle the cloning, and only if it doesn't recognize the type of the clone will it invoke the read_callback. It seemed to all happen 'in sync' too, so there is no ayncness involved. I would assume that the JS_ReadStructuredClone itself would function as some sort of lock and ensure there is no GC in the meantime...

@jdm
Copy link
Member Author

@jdm jdm commented Jul 14, 2018

Yes, there is no risk of GC happening randomly or in parallel. It can happen as a result of any new JS allocation or certain API calls.

@gterzian
Copy link
Member

@gterzian gterzian commented Jul 14, 2018

In Gecko, they actually seem to keep track of cloned Blobs with this nsTArray<RefPtr> mBlobImplArray, stored inside a StructuredCloneHolder.

So what they do is also 'creating a blob and then returning a pointer to it's raw JsObject', however they always keep the corresponding BlobImpl in this Array(This BlobImpl I guess corresponds to a DomRoot<Blob> in our case?).

The StructuredCloneHolder is available from the callbacks, because they pass it around as the closure argument when calling JS_WriteStructuredClone/JS_ReadStructuredClone.

In the callbacks, they write/read the index of a given Blob in the array using JS_WriteBytes/JS_ReadBytes(see here), so that they can access it later.

So perhaps we could store the DomRoot<Blob> inside a StructuredCloneHolder as well, and wrap it around the current StructuredCloneData, at least for cases were the callbacks will be invoked.

Actually we might want to remove the callbacks from the 'regular' call to JS_ReadStructuredClone for stuff like #structureddeserialize.

I guess the big question is where to put that StructuredCloneHolder? For example with PostMessage, currently we pass around a Vec<u8> which corresponds to the buffer in a StructuredCloneData. Could we instead pass around a StructuredCloneHolder containing that data, as well as keeping the DomRoot alive(not sure if that is Send and so on)? Or would we need to store the DomRoot elsewhere and access it on the other side of post message when reading? Would we need to keep the rooted dom alive after a successful read on the other side(I guess so, since it will still be used in JS at that point)?

bors-servo added a commit that referenced this issue Aug 7, 2018
Use a structured clone holder to store rooted clones

<!-- Please describe your changes on the following line: -->

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

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

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- 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/21218)
<!-- Reviewable:end -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

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