deno_core icon indicating copy to clipboard operation
deno_core copied to clipboard

Support for realms/contexts in `deno_core` (in preparation for `ShadowRealm`)

Open andreubotella opened this issue 3 years ago • 10 comments

In JS, code execution is always tied to some realm (or "context" in V8 terminology), which provides a global object and a set of built-ins (Object, Array, Error, etc.) which is different from those of other realms. Multiple realms can share an event loop (= a thread), and those that do share one may be able to access objects from a different realm. In the web, a same-origin <iframe> is a different realm from the top-level page (iframe.contentWindow.Array !== window.Array). Currently there is no way to create realms or run code in them purely with JS built-ins, that's something that host environments (the web / Node.js / Deno) would have to provide, so it's fine for Deno to not support them.

But there is currently a stage-3 TC39 proposal called ShadowRealm that would allow creating realms, as a way of sandboxing untrusted code. This proposal is not close to shipping anywhere, and it's only now starting to get implemented in V8, but currently Deno assumes that there's a single V8 context available at all times, and it's better to start refactoring things to support realms with plenty of time to spare.

Since ShadowRealms are meant to be a sandbox primitive, there would be no way to make objects from the parent realm available inside the ShadowRealm and vice versa. This simplifies to some extent the requirements needed, because that way there is no need to comb through the JS code looking for wrong uses of instanceof. That said, these are the requirements that seem to be needed for ShadowRealm (and I'm sure I'm forgetting some):

  • [x] Add a Rust API to create V8 contexts associated with a JsRuntime and run scripts in them (similar to JsRuntime::execute_script), to simplify running tests. Modules can be left for later. denoland/deno#14019
  • [x] Make sure bindings and extension scripts are initialized correctly in the context, both when using snapshots and when not. This includes making sure the ops cache in different realms can't go out of sync. denoland/deno#14019
  • [x] Make sure any object return values of bindings and ops, as well as any exceptions they throw, are objects in the current realm (i.e. their prototype is not, say, a different realm's Object.prototype). denoland/deno#14750, denoland/deno#17050.
  • [x] Make sure async ops don't resolve a promise in the wrong realm. Originally landed on denoland/deno#14734, then reverted, now relanded in denoland/deno#17204.
  • [x] js_promise_reject_cb, js_uncaught_exception_cb and js_wasm_streaming_cb should probably be realm → callback (weak)maps. Originally landed on denoland/deno#15599, then reverted, now relanded in denoland/deno#17422.
  • [ ] Modules. Per the HTML spec, every realm has its own module map, although module fetches can be cached across realms. denoland/deno#15760 denoland/deno_core#41
    • [x] Add Rust APIs to load modules in a context (similar to JsRuntime::load_main_module and JsRuntime::load_side_module).
    • [x] There should be a module map per realm, rather than one for the entire JsRuntime. (Should there be a single module loader, though? Blobs should probably not be cached across realms, but non-blob fetches probably should, so it's not clear what to do about denoland/deno#12458.)
    • [x] Make sure module evaluation in realms integrates with the event loop.
    • [X] Dynamic imports and import.meta.
  • [ ] Review every use of serde_v8::Value to make sure objects from a realm can't leak into another.
  • [ ] Implement the ShadowRealm constructor. denoland/deno#16211
  • [ ] Fix the inspector to properly support multiple realms. denoland/deno_core#353

andreubotella avatar Dec 30 '21 14:12 andreubotella