[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);
} };
})();
`)();
Multi-realm 환경(iframe, ShadowRealm, worker)에서는 각 realm이 자체적인 built-in constructor를 보유합니다. ECMAScript 명세는 각 microtask job의 realm을 생성 시점에 고정하도록 요구하는데, Await의 경우 이는 settle site가 아닌 Await 알고리즘의 4단계에서 결정됩니다. JSC는 같은 realm 내의 일반적인 경우를 위해 resolveWithInternalMicrotaskForAsyncAwait에 fast path를 마련해 두었습니다. 이 fast path는 PromiseResolve가 통상 요구하는 중간 promise 생성을 건너뜁니다.
이 commit은 두 가지 결함을 수정합니다. 먼저 await fast path에 realm 일치 여부 확인이 추가되어, vanilla promise를 직접 채택하기 전에 realm이 같은지 검증하게 됩니다. Cross-realm인 경우에는 thenable path로 처리되며(microtask가 1회 추가되고, V8과 호환됩니다), 자신의 realm이 유지됩니다. 한편 runInternalMicrotask 내부의 microtask handler는 이제 settle site의 globalObject 대신, 구동 대상 객체(generator, result promise, 또는 module)에서 realm을 가져오도록 변경되었습니다.
Significance
Cross-realm realm leakage가 발생하면, 이후 await된 thenable의 then(f,j)로 전달되는 resolving function이 예상치 못한 외부 realm의 constructor를 담게 될 가능성이 있습니다. 이는 JavaScript에서 감지 가능한 명세 위반이며, cross-realm 공격 시나리오에서 primitive로 활용될 수 있습니다.