Skip to content

Fix duplicate Trusted Types policy creation when spawning workers - #27256

Merged
sbc100 merged 6 commits into
emscripten-core:mainfrom
Young-Lord:fix-trusted-types-duplicate-worker-policy
Jul 8, 2026
Merged

Fix duplicate Trusted Types policy creation when spawning workers#27256
sbc100 merged 6 commits into
emscripten-core:mainfrom
Young-Lord:fix-trusted-types-duplicate-worker-policy

Conversation

@Young-Lord

@Young-Lord Young-Lord commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

allocateUnusedWorker() and _emscripten_create_wasm_worker() called trustedTypes.createPolicy() on every Worker creation. The Trusted Types spec forbids reusing a policy name unless CSP allows duplicates, so programs with 2+ pthreads (or wasm workers) failed under Trusted Types enforcement.

Cache each policy once and pass the script URL through createScriptURL.


Reproducer (spawn 3 pthreads):

// main.c
#include <pthread.h>
#include <stdio.h>

static void *thread_fn(void *arg) {
  printf("thread %d running\n", *(int *)arg);
  return NULL;
}

int main(void) {
  enum { N = 3 };
  pthread_t t[N];
  int ids[N];
  for (int i = 0; i < N; i++) {
    ids[i] = i;
    if (pthread_create(&t[i], NULL, thread_fn, &ids[i])) return 1;
  }
  for (int i = 0; i < N; i++) pthread_join(t[i], NULL);
  printf("all %d threads completed\n", N);
  return 0;
}

Build:

emcc main.c -o main.js -pthread -sTRUSTED_TYPES=1 -sENVIRONMENT=web,worker -sPTHREAD_POOL_SIZE=3

Serve with Trusted Types CSP:

# serve.py
import http.server, os

DIR = os.path.dirname(os.path.abspath(__file__))

class Handler(http.server.SimpleHTTPRequestHandler):
    def __init__(self, *a, **kw):
        super().__init__(*a, directory=DIR, **kw)

    def end_headers(self):
        self.send_header("Cross-Origin-Opener-Policy", "same-origin")
        self.send_header("Cross-Origin-Embedder-Policy", "require-corp")
        self.send_header(
            "Content-Security-Policy",
            "require-trusted-types-for 'script'; "
            "trusted-types emscripten#workerPolicy2",
        )
        super().end_headers()

http.server.HTTPServer(("", 8080), Handler).serve_forever()
python3 serve.py
# open http://localhost:8080 in Chromium, DevTools console (F12)

Expected: thread 0/1/2 running, then all 3 threads completed.
Actual (without this patch): During startup, initMainThread() synchronously pre-creates the worker pool (PTHREAD_POOL_SIZE=3); the second allocateUnusedWorker() throws on duplicate createPolicy, aborting module initialization. Console outputs:

Creating a TrustedTypePolicy named 'emscripten#workerPolicy2' violates the following Content Security Policy directive because a TrustedTypePolicy with that name already exists and the directive does not contain'allow-duplicates': "trusted-types emscripten#workerPolicy2". The action has been blocked.
Uncaught TypeError: Failed to execute 'createPolicy' on 'TrustedTypePolicyFactory': Policy with name "emscripten#workerPolicy2" already exists.

Most of this PR is finished with AI, but I've confirmed this bug exists and the fix works. Please feel free to reject if you find it unacceptable.

I haven't included a test case, as I am not familiar with the test suite. I would appreciate any guidance or help from maintainers to add a test.

allocateUnusedWorker() and _emscripten_create_wasm_worker() called
trustedTypes.createPolicy() on every Worker creation. The Trusted Types
spec forbids reusing a policy name unless CSP allows duplicates, so
programs with 2+ pthreads (or wasm workers) failed under Trusted Types
enforcement.

Cache each policy once and pass the script URL through createScriptURL.
Comment thread src/lib/libpthread.js Outdated
The EXPORT_ES6 and non-EXPORT_ES6 worker creation paths are mutually
exclusive at compile time, so one policy cache is sufficient.
Comment thread src/lib/libwasm_worker.js Outdated
$_wasmWorkers: {},
#if TRUSTED_TYPES
// Cached Trusted Types policy for Wasm Worker creation.
$_emscriptenWasmWorkerPolicy: 'null',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does null not work here? (without the quotes)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null works. I misunderstood the addFromLibrary function before.
Changed in new commits.

Comment thread src/lib/libwasm_worker.js Outdated
);
worker = _wasmWorkers[wwID] = new Worker(p.createScriptURL('ignored'), {{{ wasmWorkerOptions }}});
_emscriptenWasmWorkerPolicy ??= trustedTypes.createPolicy(
'emscripten#workerPolicy1', { createScriptURL: (url) => url });

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put this all on one line. And maybe drop the 1?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dropped.
However, I found dropping the 1 may break existing CSP trusted-types allowlists that
list emscripten#workerPolicy1 (and pthread's emscripten#workerPolicy1/2). Is that acceptable?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have not idea who, if anyone, is using the TRUSTED_TYPES setting, or the implications of changing these strings. @aaronshim originally added this settting. Perhaps they have some insight here?

This is an automatic change generated by tools/maint/rebaseline_tests.py.

The following (10) test expectation files were updated by
running the tests with `--rebaseline`:

```
codesize/test_codesize_cxx_ctors1.json: 151786 => 151739 [-47 bytes / -0.03%]
codesize/test_codesize_cxx_ctors2.json: 151189 => 151142 [-47 bytes / -0.03%]
codesize/test_codesize_cxx_except.json: 195681 => 195624 [-57 bytes / -0.03%]
codesize/test_codesize_cxx_except_wasm.json: 166910 => 166863 [-47 bytes / -0.03%]
codesize/test_codesize_cxx_except_wasm_legacy.json: 164794 => 164747 [-47 bytes / -0.03%]
codesize/test_codesize_cxx_lto.json: 120616 => 120589 [-27 bytes / -0.02%]
codesize/test_codesize_cxx_mangle.json: 262160 => 262103 [-57 bytes / -0.02%]
codesize/test_codesize_cxx_noexcept.json: 153786 => 153739 [-47 bytes / -0.03%]
codesize/test_codesize_cxx_wasmfs.json: 179507 => 179460 [-47 bytes / -0.03%]
codesize/test_codesize_files_wasmfs.json: 63720 => 63720 [+0 bytes / +0.00%]

Average change: -0.02% (-0.03% - +0.00%)
```
…uplicate-worker-policy

# Conflicts:
#	test/codesize/test_codesize_cxx_wasmfs.json
@Young-Lord

Copy link
Copy Markdown
Contributor Author

The CI failure in test-core3 seems irrelevant, since TRUSTED_TYPES is not even enabled for it.

@sbc100
sbc100 merged commit 666e211 into emscripten-core:main Jul 8, 2026
39 checks passed
@Young-Lord
Young-Lord deleted the fix-trusted-types-duplicate-worker-policy branch July 9, 2026 00:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants