[JSC] Promise jobs must not run with the realm of a cross-realm settle site
// 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.
Significance
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.
Audit directions
resolvePromisedeliberately retains its fast path without a realm check. The commit message justifies this by saying the spec has no such requirement and only fixes the thenable-job enqueue to use the promise's realm; differential testing against V8 here is warranted to confirm no observable realm escape remains.- GlobalObject pointer-equality guards. All realm guards are
globalObjectpointer-equality comparisons; any scenario where the same logical realm has multipleglobalObjectrepresentations (embedder recycling, ShadowRealm with a custom backing global, Wasm promising paths) could silently bypass the check. - 28 files touched span WebCore bindings, Wasm promising (
WebAssemblyPromising.cpp), module loading (CyclicModuleRecord,JSModuleRecord,ModuleRegistryEntry), andCodeGeneratorJS.pm— each site that now derives a realm from a driven object rather than aglobalObjectparameter is an independent opportunity for a misapplied anchor. - Extra microtask on the cross-realm path changes async ordering semantics. Microtask-order-dependent logic in ShadowRealm or embedder-level promise consumers may behave unexpectedly.