← All issues

[JSC] Promise jobs must not run with the realm of a cross-realm settle site

51cc3fe

// JSTests/stress/cross-realm-await-thenable-resolve-realm.js
const other = createGlobalObject();
other.MainFunction = Function;
other.subject = promise; // cross-realm pending promise
new other.Function(`
    (async () => {
        await subject;
        await { then(f) {
            // Before patch: f.constructor === MainFunction  (foreign realm leaked)
            // After patch:  f.constructor === Function     (own realm)
            f(0);
        } };
    })();
`)();

In multi-realm environments (iframes, ShadowRealm, workers), each realm has its own built-in constructors. The ECMAScript spec requires each microtask job's realm to be fixed at creation time — for Await this is step 4 of the Await algorithm, not the settle site. JSC optimizes the common same-realm case with fast paths in resolveWithInternalMicrotaskForAsyncAwait that skip the intermediate promise normally required by PromiseResolve.

This commit fixes two defects. The await fast paths now require realm match before adopting vanilla promises directly; cross-realm cases take the thenable path (+1 microtask, V8-compatible) so the own realm is preserved. Internal microtask handlers in runInternalMicrotask now derive their realm from the driven object (generator, result promise, or module) rather than the settle site's globalObject.

Cross-realm realm leakage means resolving functions passed to a subsequently-awaited thenable's then(f,j) could carry constructors from an unexpected foreign realm — an observable spec violation detectable from JavaScript and a primitive that could be leveraged in cross-realm exploitation scenarios.

🔒

Realm-anchor changes across 28 files in Promise, Wasm, and module machinery — several fast-path and multi-site anchoring edge cases are worth investigating.

Subscribe to read more