Skip to content

Instantly share code, notes, and snippets.

@mystor
Last active December 20, 2019 07:04
Show Gist options
  • Save mystor/5739e222e398efc6c29108be55eb6fe3 to your computer and use it in GitHub Desktop.
Save mystor/5739e222e398efc6c29108be55eb6fe3 to your computer and use it in GitHub Desktop.

Motivation

On 32-bit operating systems, we often run into issues where the address space is too fragmented, and complex web applications, such as games, are unable to allocate large contiguous memory regions, such as WASM heaps or asm.js. In addition to 32-bit address space, other OS process-bound resources can become exhausted, depending on the device and system.

This problem can be solved by User Agents loading the games into a fresh unfragmented process. Unfortunately, by the time the allocation has failed it is too late to change which process the load is performed in.

The Large-Allocation response header

We would add a new response header, the Large-Allocation response header. This response header would tell the browser that the page being loaded is going to want to perform a large allocation. This response header should not have any semantic effect, except it may improve the page's chances to successfully allocate a large memory allocation.

The User Agent MAY, when it believes that such a large allocation would fail, load the page in a new process. This new process would have a less fragmented address space, thus increasing the chance that the allocation would succeed.

The Large-Allocation response header accepts an argument, which is the expected size of the allocation to be performed, in megabytes. The value of 0 is a special value which represents uncertainty as to what the size of the allocation is. It should be assumed to be a large allocation. This information can be used to avoid creating a new process when it would not be necessary, as well as potentially reserve memory in the new process for the allocation.

Examples

A Large-Allocation response header on a document which is going to do an arbitrary large-size allocation.

Large-Allocation: 0

A Large-Allocation response header on a document which is going to do a 500MB allocation.

Large-Allocation: 500

NOTE: Megabytes were chosen for the units because a 1MB allocation isn't going to fail unless the address space is super fragmented, and so it felt silly to allow for precision smaller than that.

Restrictions

A cross-process navigation (a navigation during which the process which a browsing context is running in changes), such as one which would be requested by the Large-Allocation response header, is not possible when there are other non-child browsing contexts within the navigating browsing contexts' unit of related similar-origin browsing contexts.

In the situation where there are other non-child browsing contexts within the navigating browsing contexts' unit of related browsing contexts, but not unit of related similar-origin browsing contexts, it is possible to perform a process-changing load, so long as cross-process window proxies are avaliable. I expect that most User Agents will be unable and/or unwilling to implement this for Large-Allocation response headers, at least initially. For that reason I would generally consider this situation to also be a situation where Large-Allocation is not supported.

Thus, we can generally say that User Agents will be unable to Large-Allocation cross process loads when they are loaded in an iframe, or when any other non-child browsing context has a reference to the active browsing context.

This means that operations which are performed by previous pages loaded within the browsing context of the active page can impact the page's ability to allocate memory, which is sub-optimal. For example, if a previously loaded website within the current browsing context opened a window with <a target=_blank>, or if the browsing context itself was opened with an opener, then the page loaded, potentially much later, would be unable to perform a cross-process navigation.

Interactions with Isolation

This constraint can be relaxed by a form of window proxy isolation such as the per-origin isolation proposal. One of the goals of this proposal seems to be to make toplevel loads to an origin which is isolated able to be reliably performed in another process, which would mean that the User Agent could reliably create a new process when necessary to allow Large-Allocation load to succeed.

This is achieved through disowning all references to the document, which is desirable for security purposes, as it prevents other browsing contexts from navigating you.

The following are some examples of how references to a toplevel browsing context could leak to other non-child browsing contexts within its unit of related browsing contexts. Any of these references could prevent a document from being loaded with a cross-process load:

  • If the browsing context performing the load was opened by another browsing context, then the window.opener property acts as a mechanism for two-way communication between the current browsing context and its opener. This property would likely be nulled.

  • If the browsing context performing the load previously opened another browsing context without noopener or noreferrer, then that browsing context has a reference to the navigating browsing context. These references cannot be remotely nulled, so it may instead be more convenient to make them appear 3rd-party, and make all operations performed on them into no-ops.

  • If the browsing context performing the load was opened by another browsing context, the other browsing context, or one of its related browsing contexts, may have a reference to the navigating browsing context, either through the return value of window.open, or due to the source property of a postMessage message. These references also cannot be remotely nulled, so they may become no-op dummy references.

For general memory-intensive WebAssembly applications which still use storage, and/or interactions with 3rd party authentication services, such as OAuth, it would be desirable to gain access to the window proxy isolation features of the per-origin isolation proposal, which allow for these Large-Allocation cross process loads to occur even if there are existing references to the navigating browsing context, without also isolating the origin with regards to storage.

This would mean that the only situation which would still inhibit performing a Large-Allocation cross process load for an isolated origin would be if the Large-Allocation document is loaded in an iframe, because isolation only works on toplevel documents.

Additional Benefits to Window Proxy Isolation

In addition to supporting Large-Allocation loads, window proxy isolation also enables other forms of cross process loads. For example, a window proxy isolated document can reliably preload in a new process, ensuring faster page loads. It could also, depending on the implementation, be used as a source of optimization for the JavaScript JIT, by giving it the invariant that window always aliases the global object while script is executing.

It seems like a version of window proxy isolation without storage isolation could be very useful as a general good practice for ensuring that your origin is nice to the User Agent, giving it the chance to switch you between processes, render things more quickly, and potentially even improve javascript performance.

@mozfreddyb
Copy link

Can we move this to an officially named document, i.e., something that is not a secret gist?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment