-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Incumbent seems broken for pending promises #5213
Comments
I will not be able to get to this until Monday next week, unfortunately (have some pre-existing commitments). If you haven't heard from me by end of (US East Coast) day then, please ping me! |
For what it's worth I wrote tests in web-platform-tests/wpt#21206, assuming Web IDL-like semantics, which seem to pass in both Chrome and Firefox. In Chrome's case, this is surprising; based on @syg's code inspection we seem to be doing something rather different. Maybe if I just introduce even more realms (e.g. promise constructor realm is currently the same as entry realm) we'll find issues... Edit: we found that cases depending on the backup incumbent settings object stack fail in Chrome. |
(As an aside, is realm 1:1 with "settings object"? Saying "settings object" is rather verbose.) |
For Chrome, what I think is going on is that enqueuing a Promise microtask always saves the isolate's current realm. For pending Promises, the current realm at the time of enqueuing the task is the resolve function's realm, since the resolve function is at the top of the stack. In other words, it'll always be the Promise's creation realm. I believe that explains the Chrome failure in web-platform-tests/wpt#21206: for pending promises, the incorrectly saved incumbent realm is the fulfilled Promises's creation realm. What @domenic has pointed out, and what I agree with, is that this is super edge casey. A difference is observed only when there is no user code on the JS stack, like when builtins are bound with This also raises the point that since Chrome/V8 is buggy here and that is unlikely that the incumbent being used in this way in Promise callbacks is depended upon in the web, and that we still have existing sentiment to simplify the incumbent concept, is there possibility of change here? For instance, just saving the callback's creation realm instead of the incumbent. |
Well, we'd like to remove uses of it. I don't think introducing two slightly-different incumbent concepts, one used by promises and another by all other callbacks on the platform, would be much of a simplification. |
My thoughts are tending in the direction of @syg's comment. Applying HTML's concept of "incumbent" to JS here would complicate the specification and implementation in certain ways that I don't understand the motivation for (even if some implementations already do it). How would a web developer run into this sort of issue? A bit of a tangent, but in addition to Promises and WeakRefs, would the idea of threading incumbent realm through be relevant for iterator helpers (on async iterables) and Observables? |
Web developers run into this issue whenever they use incumbent-dependent APIs. See #1430 for a relatively-complete list. A lot of the uses there can be considered "legacy", i.e. we don't like that they're using incumbent, and would like to switch to relevant or current if web compatible. However, there are some notable ones where incumbent makes sense, notably the security checks involved in navigating via the In particular, consider a case like someOtherWindow.postMessage.call(someThirdWindow, ...args); This will eventually fire a Currently, if you send So to be concrete about the web developer impact:
Great find. I think it would. This argues that we either need general host hooks for all callback storage and use (I think it only matters for potentially-async callbacks?), or we should try to move the entry and incumbent concepts down into the ES layer, as something that both the ES spec and hosts maintain, but hosts take advantage of. |
To the extent that anyone ever examines an incumbent realm, the idea is that any time a function that examines it is captured in some way the incumbent should be captured as well.
Indeed. The idea is that We could, I guess, move the incumbent handling into |
Yep, that's what I was suggesting. It still strikes me as odd that, if the ultimate motivation is to counter spoofing for a specific set of APIs, that the countermeasures are not built into those APIs but into all callbacks. |
How would you do it in the API? |
I see your point. I can imagine something fairly intrusive, such as instead of building the logic into Edit: missed operative "instead of" |
This is a bit of a tangent, but it's hard for me to see what you could do to I'm trying to understand the strength of this "no spoofing" goal. We're talking about similar-origin windows that could reach into each other's globals and do all kinds of manipulation, right? This feels like something nice for regularity, but is it intended to be considered a security property? |
The issue is not the receiver. The issue is what the callee perceives as "the caller".
That is an interesting question, yes. |
Er, Tuesday. I forgot that Monday is a US holiday. |
OK, so I am still trying to page all the Promise stuff back in. @jswalden or @tschneidereit might have this more at the top of their heads, maybe. Looking at the Gecko code, I think it works like this:
That is, this basically does implement WebIDL semantics, as far as I can see. |
Thanks Boris! That's what I suspected. I'd really like to do that at the spec level too, and in Chromium and WebKit, so that promises aren't weird here. @syg and @littledan, does that seem like a reasonable medium-term goal? I understand it's probably not super high priority to do the kind of ES262 spec work, or V8 work, that would be involved in threading the incumbent through here. (And I don't think this kind of stuff should block work on WeakRef callbacks.) But I'd like to get a sense as to whether it's acceptable as a goal, even if not as something on the roadmap. |
My reading of the V8 code is that correctly threading an incumbent global through is possible with a little work. Isolates already have a GetIncumbentContext() method and there's an RAII thing embedders can use to deal with the backup incumbent context stack, but it is not clear to me that the blink side is using this correctly in all places... Correctness aside, I have two other concerns:
I'm kind of on the fence. I'm not fully convinced yet of the usefulness of the incumbent concept, and it's only observable when My current position is something like: more interop is always great, so I'm not against trying to implement incumbents in V8 Promises. But as @domenic has said, this is going to be pretty low priority. However, if there are performance regressions that come from this, I envision a hard time justifying engineering effort to optimize for incumbent realm tracking. Edit: Missed an operative 'not' |
… for promise jobs, a=testonly Automatic update from web-platform-tests Test entry and incumbent settings object for promise jobs Follows whatwg/html#5212 and whatwg/html#5213. -- wpt-commits: bc4ddbbddc566c3602d1af8d73c0225f8335855d wpt-pr: 21206
… for promise jobs, a=testonly Automatic update from web-platform-tests Test entry and incumbent settings object for promise jobs Follows whatwg/html#5212 and whatwg/html#5213. -- wpt-commits: bc4ddbbddc566c3602d1af8d73c0225f8335855d wpt-pr: 21206
… for promise jobs, a=testonly Automatic update from web-platform-tests Test entry and incumbent settings object for promise jobs Follows whatwg/html#5212 and whatwg/html#5213. -- wpt-commits: bc4ddbbddc566c3602d1af8d73c0225f8335855d wpt-pr: 21206 UltraBlame original commit: 3017b5083bf29149178a2c1357796936a5a95065
… for promise jobs, a=testonly Automatic update from web-platform-tests Test entry and incumbent settings object for promise jobs Follows whatwg/html#5212 and whatwg/html#5213. -- wpt-commits: bc4ddbbddc566c3602d1af8d73c0225f8335855d wpt-pr: 21206 UltraBlame original commit: 3017b5083bf29149178a2c1357796936a5a95065
… for promise jobs, a=testonly Automatic update from web-platform-tests Test entry and incumbent settings object for promise jobs Follows whatwg/html#5212 and whatwg/html#5213. -- wpt-commits: bc4ddbbddc566c3602d1af8d73c0225f8335855d wpt-pr: 21206 UltraBlame original commit: 3017b5083bf29149178a2c1357796936a5a95065
* Editorial: remove redundant "the" * Meta: default branch rename Also correct a broken link. Not even w3.org URLs are that cool. Helps with whatwg/meta#174. * Editorial: clean up calls to "parse a URL" It actually takes a string, so calls should be clear about that. * Review Draft Publication: January 2021 * Simplify <link>s In particular, remove their activation behavior, stop them from matching :link and :visited, and stop suggesting that they be focusable areas. This also includes a slight expansion and rearrangement of the link element's section to make it clearer what hyperlinks created by <link> are meant for, contrasting them to <a> and <area> hyperlinks. Closes whatwg#4831. Closes whatwg#2617. Helps with whatwg#5490. * Meta: remove demos/offline/* (whatwg#6307) These are no longer needed as of e4330d5. * Meta: minor references cleanup Use more HTTPS and drop obsolete HTML Differences reference. * Editorial: anticlockwise → counterclockwise We use en-US these days. Spotted in https://twitter.com/iso2022jp/status/1352601086519955456. * Use :focus-visible in the UA stylesheet See w3c/csswg-drafts#4278. * Editorial: align with WebIDL and Infra * Fix "update a style block" early return The new version matches implementation reality and CSSWG resolution. The algorithm was also inconsistent, as it looked at whether the element was in a shadow tree or in the document tree, but it was only specified to be re-run if the element becomes connected or disconnected. The CSSWG discussed this in w3c/csswg-drafts#3096 (comment) and http://wpt.live/shadow-dom/ShadowRoot-interface.html tests this. This also matches closer the definition of <link rel="stylesheet">, which does use connectedness (though it uses "browsing-context connected", which is a bit different): https://html.spec.whatwg.org/#link-type-stylesheet * Modernize and refactor simple dialogs This contains a small bug fix, in that confirm() and prompt() said "return" in some cases instead of "return false" or "return null" as appropriate. Other notable changes, all editorial, are: * Factoring out repeated "cannot show simple dialogs" steps, which will likely expand over time (see e.g. whatwg#6297). * Separating out and explaining the no-argument overload of alert(). * Passing the document through to the "printing steps", instead of just having them talk about "this Window object". * Meta: add definition markup for MessageEvent * Remove <marquee> events They are only supported by one engine (Gecko). Closes whatwg#2957. * Clarify when microtasks happen * Ignore COEP on non-secure contexts Fixes whatwg#6328. * Editorial: update URL Standard integration * Editorial: only invoke response's location URL once Complements whatwg/fetch#1149. * Track the incumbent settings and active script in Promise callbacks Closes whatwg#5213. * createImageBitmap(): stop clipping sourceRect to source's dimensions It has been found in whatwg#6306 that this was an oversight at the time of its introduction. Current behavior goes against author expectations and no implementer has opposed the change to "no-clip". Tests: web-platform-tests/wpt#27040. Closes whatwg#6306. * Remove CSP plugin-types blocking With Flash not being supported anymore, the CSP directive plugin-types has lost its main reason for being and is being removed from the Content Security Policy specification: w3c/webappsec-csp#456. This change removes references to the relevant algorithm from the Content Security Policy spec. * Meta: set more dfn types A follow-up to: * whatwg#5694 * whatwg#5916 * Editorial: occuring → occurring * Make all plugin-related APIs no-ops Part of whatwg#6003. * Disallow simple dialogs from different-origin domain iframes Closes whatwg#5407. * Revive @@iterator for PluginArray/MimeTypeArray/Plugin @@iterator is implicitly installed by defining an indexed property getter. Since there is no other way to define it exclusively, this restores some methods back to being indexed getters. This fixes an inadvertent observable behavior change in d4f07b8. * Adjust web+ scheme security considerations to account for FTP removal Also, network scheme is now reduced to HTTP(S) scheme. Helps with whatwg#5375, but form submission issue remains. See whatwg/fetch#1166 for context. * Meta: export pause Nobody but XMLHttpRequest take a dependency on this please. You have been warned. Context: whatwg/xhr#311. * Fix typo: ancestor → accessor Fixes whatwg#6374. Co-authored-by: Dominic Farolino <domfarolino@gmail.com> Co-authored-by: Anne van Kesteren <annevk@annevk.nl> Co-authored-by: Domenic Denicola <d@domenic.me> Co-authored-by: Emilio Cobos Álvarez <emilio@crisal.io> Co-authored-by: Momdo Nakamura <xmomdo@gmail.com> Co-authored-by: Jake Archibald <jaffathecake@gmail.com> Co-authored-by: Yutaka Hirano <yhirano@chromium.org> Co-authored-by: Shu-yu Guo <syg@chromium.org> Co-authored-by: Kaiido <tristan.fraipont@gmail.com> Co-authored-by: Antonio Sartori <anton.sartori@gmail.com> Co-authored-by: Michael[tm] Smith <mike@w3.org> Co-authored-by: Ikko Ashimine <eltociear@gmail.com> Co-authored-by: Carlos IL <carlosjoan91@gmail.com> Co-authored-by: Kagami Sascha Rosylight <saschanaz@outlook.com> Co-authored-by: Simon Pieters <zcorpan@gmail.com>
I was working on fixing entry (#1426) but @syg pointed out that incumbent is not quite right either. @bzbarsky and I spent a lot of time on this in #1189 so it's possible it works in some non-obvious way, but as of right now it seems broken to me.
The specific problem is that EnqueueJob's choice of incumbent settings is wrong when job is a PromiseReactionJob that is being queued as a result of TriggerPromiseReactions (i.e., because someone did a
resolve()
on a pending promise). PromiseResolveThenableJobs work fine, as do PromiseReaction jobs triggered by PerformPromiseThen immediately enqueuing jobs (because someone did a.then()
on a settled promise.)In particular, to match Web IDL semantics (and the general expectations around how incumbent behaves) we want to capture the incumbent realm at the time
promise.then(handler)
is called, and then, when we run the PromiseReactionJob, prepare to call a callback using that settings object. But right now we just grab the incumbent at job-enqueuing time.Fixing this seems like it needs hooks deeper into the JS spec. (Even more deep than the arguments-introspection of #5212.) In particular we need a chance to store the incumbent in the [[Handler]] data structure in PerformPromiseThen.
@bzbarsky, can you check my analysis? Does Firefox implement this? Maybe we decided that it's OK for incumbent to be broken in these cases?
/cc @littledan as he may be able to help us with this cross-spec integration work.
The text was updated successfully, but these errors were encountered: