Open Bug 1613912 Opened 5 years ago Updated 1 year ago

Implement Cross-Origin-Embedder-Policy (COEP) for shared/service workers

Categories

(Core :: DOM: Workers, enhancement, P2)

enhancement

Tracking

()

People

(Reporter: annevk, Unassigned)

References

(Blocks 2 open bugs)

Details

(Keywords: dev-doc-needed)

Once we have general COEP support (bug 1613061) it would be good to also have it for shared/service workers. This would allow shared workers to use shared memory with nested dedicated workers and would allow the same for service workers if those would at some point allow nested dedicated workers too.

Tests: html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-sharedworker-failure.https.html and html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-serviceworker-failure.https.html at least for some postMessage() aspects.

Priority: -- → P2
Blocks: 1636073

when this is fixed, please edit these files to move them from backlog to normal implementation-status:
testing/web-platform/meta/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/blob-data.https.html.ini
testing/web-platform/meta/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-sharedworker-failure.https.html.ini

just delete the first two lines:
implementation-status: backlog
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1613912

the tests run on m-c so we will see these tests unexpected-pass. Also because of that they do not run on try by default, so it would be a good idea to test these while implementing this :)

Assignee: nobody → echuang
Severity: normal → S3

I think this means to implement cross-origin-isolated ServiceWorker/SharedWorker.

According to the previous discussion in the project meeting, the ServiceWorkers/SharedWorkers with COEP header should be cross-origin-isolated ServiceWorkers/SharedWorkers, which means these workers should be executed on the process with the specific prefix "webCOOP+COEP".

However, according to the current implementation, the Worker's COEP is handled and saved in WorkerPrivate when loading the worker script.

https://searchfox.org/mozilla-central/rev/8ccea36c4fb09412609fb738c722830d7098602b/dom/workers/ScriptLoader.cpp#604

That also means it happens after we spawn the worker to a selected process.

The probably solution is

  1. Checking COEP header and the process's remote type in ScriptResponseHeaderProcessor::ProcessCrossOriginEmbedderPolicyHeader.
    If COEP is "corp-required" and the process's remote type has no prefix "webCOOP+COEP", return a specific error, i.e. "NS_ERROR_REMOTE_TYPE_WEB_COOP+COEP_REQUIRED"
  2. When the parent process receives this specific error, rerun the spawn worker logic and ask to spawn on the target process.

I am not sure if I missed something, Andrew and Anne, could you help to confirm this is what we want or anything I missed? Thank you.

Flags: needinfo?(bugmail)
Flags: needinfo?(annevk)

I think we should change things around so that we first fetch a worker and then instantiate a process for it. The above proposal would put bytes (those from the worker) into an attacker process.

Flags: needinfo?(annevk)

We'd been talking about creating a Worker analog of DocumentChannel for this purpose (ex: WorkerChannel), possibly helping to normalize the ServiceWorker script loading by hiding the details of accessing the Cache API for storage purposes. However, I've been rethinking this somewhat as we've dealt with Response pass-through issues in bug 1622451.

Rather than using OMT (Off the Main Thread) channels for fetch, we might want to create a PFetch protocol as proposed in bug 1351231. If we do that, it implies a scheme where we have:

  • PFetch provides a PBackground mechanism to run a fetch that takes an IPCInternalRequest and returns an IPCInternalResponse (see https://searchfox.org/mozilla-central/source/dom/fetch/FetchTypes.ipdlh) which use a PIPCBlobInputStream for parent-to-child purposes which means that the responses can be handed back to the parent process, avoiding the response's body wastefully flowing to the child to be sent back to the parent.
  • PFetch would build on top of a FetchService mechanism that could be run from either the parent process main thread or parent process PBackground thread.
    • One of the constraints of the current OMT work is that nsHttpChannel still needs to be created on the main thread of the parent process. At least some of this is owing to use of the observer service by devtools and webextensions.
    • This could probably reuse the existing main thread dom/fetch implementation without a ton of changes, although we'd definitely need to be compensating for the fact that we're acting on behalf of a global that lives in another process and for which we only have a ClientInfo.

So if we have a FetchService that can produce responses on either main thread or PBackground, this makes it possible for the parent process to initiate the fetch of the top-level script and then hand off the Response after making a process decision. Note that there'd basically be 3 cases here, only 2 of which where we run fetch in the parent.

  1. SharedWorker launching. We always would need to do the fetch in the parent.
  2. ServiceWorker Update algorithm where we always need to be going to the network always needs to do the fetch in the parent.
  3. ServiceWorker Run Service Worker where we're dealing with an already installed ServiceWorker can trust the registration. (Additionally, it's a lot simpler and more sane if the parent process doesn't need to actually be opening the Cache API itself.) This does imply that the ServiceWorkerRegistrar needs to be modified to track the state of the header. (And we probably want to avoid just creating a new line for this... perhaps the fetchFlag line could instead be a series of space delimited positive-evidence terms (ex: "fetch" for fetch handled and not coep, "' for fetch not handled, "coep" for fetch not handled and COEP used, "fetch coep" for fetch handled and COEP.)?

In terms of the where:

  • In theory RemoteWorkerController could be involved in issuing the fetches, but given the above asymmetry between SharedWorker and ServiceWorker, I think it makes more sense to have the implementations make that specific decision themselves.
  • For ServiceWorkers, ServiceWorkerManager/ServiceWorkerInfo/ServiceWorkerPrivateImpl are all currently main-thread, but will move to PBackground in the future. So it makes sense for them to just do the fetch from the main thread for now.
  • For SharedWorkers, SharedWorkerManager's creation involves the main thread and is an async process, so the fetch could happen from the main thread, with the creation only completing once the response is received. However, things are PBackground after that, so the fetch could also happen from PBackground.

The worker script-loader when initiated from the RemoteWorkerService would then require a Response for the initial script load unless it's handed an existing cache to load from (and with network fetches forbidden).

:perry, what are your thoughts on this?

Flags: needinfo?(bugmail) → needinfo?(perry)
Depends on: 1567556
No longer depends on: 1567556

Andrew, thanks for the detailed comment.

If I understand correctly, the solution in comment 4 is providing fetch() passes request/response through a new IPC actor PFetch (managed by PBackground), and creates a nsHttpChannel in the parent process through FetchService. And thanks for FetchService, we can also load the the worker's initial script in the parent process and save in ScriptCache, then we can spawn the worker to the correct process.

It seems to be doable. I will investagate if there are any problems about global lives in another process.

Another problem is https://bugzilla.mozilla.org/show_bug.cgi?id=1351231#c1. I am not familiar with how devtool communicates with workers, could you describe what we might meet with PFetch and FetchService?

Depends on: 1567556, 1351231
Flags: needinfo?(bugmail)
No longer depends on: 1567556
Blocks: 1359745

Comment 4 all makes sense to me, and it seems like it would be pretty useful for implementing navigation preload responses too.

Flags: needinfo?(perry)
Assignee: echuang → ttung

Consult some initial questions from Kershaw. He reminded me that I mind need to be care of LoadGroup should stay in the content process.
My naive thoughts for this is like:

Fetch/FetchDriver -> FetchChannelChild -> FetchChannelParent -> OpenChannalOnParent

On the content process, we might need to have a mechanisum to propagate message to the parent process if the LoadGroup is canceled.

Another note for this is that FetchChannel might be similar as DocumentChannel and HttpChannelChild and live in the background thread. (It will need to bounce to the main thread to open the channel though)

I will need some more time to think about the plan for this.

Assignee: shes050117 → nobody
Blocks: 1731778

(Eden's comment in question 5 is now moot since Eden addressed I think a bunch of the issues in bug 1819570.)

I should also note that since Eden implemented PFetch we're closer to being able to implement this, with the sketch in comment 4 about initiating fetches of the worker scripts in the parent process still holding.

Flags: needinfo?(bugmail)
You need to log in before you can comment on or make changes to this bug.