[Wasm] Use a lazy restore frame when returning from tail calls
5223ee5
JSTests/wasm/stress/tail-call-cross-instance-gc.js
+// cross-instance tail call chain 실행 중 GC가 발생해도
+// RestoreInstanceCallee thunk frame이 손상되지 않는지 검증한다.
+// thunk의 Callee slot은 RestoreInstanceCallee singleton을 보유하고,
+// CodeBlock은 wasmInstance pointer를 보유한다.
+// GC scanning 이후에도 두 값 모두 유효해야 한다.
+ function triggerGC() { $vm.gc(); }
+ assert.eq(instPing.exports.start(1), 255);
+ assert.eq(instPing.exports.start(11), 255);
+ for (let i = 0; i < wasmTestLoopCount; i++) {
+ assert.eq(instPing.exports.start(1), 255);
+ assert.eq(instPing.exports.start(11), 255);
+ }
각 WebAssembly instance는 memory base, memory bounds, instance pointer 등 instance별 데이터를 전용 레지스터에 고정합니다. tail call이 instance 경계를 넘을 때(return_call_indirect로 다른 module을 호출하는 경우 등), callee의 instance 정보가 해당 레지스터를 덮어씁니다. 이후 호출 chain이 원래의 non-tail caller로 복귀하는 경우, 실행을 재개하기 전에 그 레지스터를 반드시 복원해야 합니다.
이 commit은 기존의 compile-time transitive tail call clobbering 분석을 runtime "restore frame" 메커니즘으로 대체했습니다. Wasm tail call이 처음으로 instance 경계를 넘는 시점에, caller의 argument area 바로 위에 32바이트 frame이 지연 삽입됩니다. 이 frame에는 caller의 instance pointer와 원래 return address가 담기고, 나머지 frame 영역은 32바이트 아래로 이동됩니다. 복귀 시에는 전용 thunk(_wasm_restore_frame_return)가 instance 및 memory 레지스터를 재로드합니다. 아울러 callCanClobberInstance와 computeTransitiveTailCalls, 이에 따른 inlining 제한도 함께 제거되었습니다. 대신 ARM64E PAC re-signing 처리와 JIT cage gate thunk가 추가되었습니다. 한편 현재 return PC와 thunk 주소를 비교하는 재사용 확인 로직도 도입되어, 반복적인 cross-instance hop 시에도 restore frame이 중복 삽입되지 않도록 합니다. spec이 명시적으로 요구하는 동작입니다.
After (runtime restore frame, lazy insertion):
Caller frame (inst A)
arg area
[32-byte restore frame inserted above args, first cross-inst call only]
slot 0: original CallerFrameAndPC
slot 1: saved instance A
slot 2: RestoreFrameCallee
callee-saves <- entire frame shifted down 32 bytes
return -> new cfr -> _wasm_restore_frame_return -> reload inst A regs -> jump to original retPC
Reuse check: if retPC == thunk addr, skip insertion (no accumulation)
Significance
Cross-instance tail call chain이 이제 자유롭게 인라인될 수 있게 되었습니다. 다만 ABI 정확성은 새로운 runtime frame type에 의존하게 되었습니다. GC, stack unwinder, sampling profiler, 세 가지 Wasm backend(BBQ, OMG, IPInt) 모두가 이 frame type을 일관되게 처리해야 합니다. ARM64E PAC re-signing 처리도 이 범위에 포함됩니다. 전용 GC stress test를 함께 추가했다는 사실 자체가, 개발자들도 이 부분의 정확성 확보가 쉽지 않다는 점을 인식하고 있었음을 나타냅니다.
Audit directions
a Aaaaaaa Aaaaa Aaaaaaaa Aaaa Aaaaaaa Aaaaaaa Aaaaa Aa Aa Aaaaaaa Aa Aaaaaaaaaaaaaaaaaa Aaaa Aaaaa Aaaaa Aaa Aaaa Aaaa Aaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaa Aaaaaa Aaa Aaaaaaa Aa Aa Aaaaaaaa Aaaa Aaaaa Aaaa Aa Aaaaaaaaaaa Aaaaa Aa Aaaaaa Aaaa Aaa Aaaa Aaaaaaaaaa Aaa Aaaaa Aa Aaaaa Aa Aaaaaaaaaaa Aaaaa Aaa Aaaaa Aa Aaaa Aaa Aaaa Aaaaa
a Aaaaaaaa Aaa Aaaaaaaaaaaaa Aa Aaaaaa Aaa Aaaaaa Aaaaaa Aaa Aaaaaaa Aaaaaaaaa Aaaaaaa Aaaaaa Aaa Aaaaaaaa Aaaaaa Aaaa a Aaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aa Aaaa Aaaaaa Aaa Aaaaaaaa Aaaa Aaaaaa Aaaaaaaaaaaaaa Aaaa Aa Aaaa Aaa Aaa Aa Aaa Aaaaaa Aaaaaaaa Aaaaa Aaa Aaaaaaa Aaa Aaaa Aaaaa
a Aaaaaaaaaaaaa Aaaaaaaaaaa Aaaaaa Aaaaaaaaaaa Aaa Aa Aaa Aa Aaa Aaaa Aaaaaaaaa Aa Aaaaaaaaaaaaaa Aaa Aaaaaa a Aaa Aaaa Aaaa Aaaaaa Aaa Aaaa Aaaa Aaaaa Aaaa Aaaa Aaaa Aaaa Aaaaaa Aaaaaaaa Aaaaa Aaa Aaaaaa Aaaa Aaaaa Aaa Aa Aaa Aa Aaa Aaaaaaa Aaaaaa Aaaaaaaaaaa Aaaaaaaa Aaa Aa Aaaaaaaaa Aa Aaaaa Aaa Aaa Aaa Aaaa Aaaaa
a Aaaa Aaa Aaaaaaaa Aaaaaaaaaaaaaa Aaaaaaa Aaaaaa Aaa Aaaaa Aaaaaaaa Aaa Aa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaa Aaa Aaaaaaaaaa Aaaa Aaaaa Aaaaaaaaa Aaaaaaaa Aaaaaaaa Aa a Aaaaa Aaaaa Aaaa Aaaa Aaaa Aa Aa Aaa Aaaaa Aaaa Aaaaa a Aaa Aaaa Aaaaaaaaaaaaaaa Aaaaaaaa Aaaaaaaaaaaaa Aaa Aaa Aaaa Aaaaa
a Aaaaa Aaaa Aaaa Aaaaa Aaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaa Aaaa Aaaaaa Aaa Aaaa Aaa Aaaaaa Aaa Aaa Aaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaa Aaa Aa Aaa Aaaaaaaa Aaaaaaaa Aaaa Aaaa Aaaaaaaaa Aaa Aaaaa a Aaa Aaaa Aaaaa
🔒New runtime Wasm stack frame mechanism with cross-backend ABI and pointer-auth implications — several security audit directions included.
더 확인하려면 구독해 주세요