[JSC] Due to IPInt, we need to ensure that wasm multi-memory does not support memory64
The memory64 check that only looked at the memory it was holding.
Component: JSC WebAssembly | d319ee7
Wasm의 multi-memory proposal은 하나의 모듈이 여러 개의 linear memory를 선언할 수 있게 해줍니다. 반면 memory64는 개별 memory가 64비트 주소 체계를 사용할 수 있게 합니다. WebKit은 현재 두 기능을 함께 사용하는 것을 막고 있는데, memory64 모듈은 반드시 memory를 하나만 가져야 합니다. In-place wasm interpreter인 IPInt가 bounds-checking fast path에서 memory 0의 주소 폭을 하드코딩해 두고, 실제로 어떤 memory에 접근하든 상관없이 이 값을 모든 memory access에 균일하게 적용하기 때문입니다.
Source/JavaScriptCore/wasm/WasmSectionParser.cpp
JSTests/wasm/stress/memory64-multi-memory-rejected.js
기존 guard는 현재 파싱 중인 memory가 64비트일 때만 동작했습니다. 그러다 보니 memory i64가 먼저 선언되면 그 시점에 memoryCount() == 0이라 검사를 통과해버렸고, 반대로 memory32가 뒤에 오는 경우에는 isMemory64가 설정되지 않아 역시 통과했습니다. 새 조건문은 이 로직을 뒤집습니다. 먼저 "이미 memory가 하나 존재하는가"를 기준으로 삼고, 그 상태에서 현재 memory나 memory 0 중 어느 하나라도 64비트이면 거부하도록 바뀌었습니다. 이렇게 해서 선언 순서에 따라 결과가 달라지던 문제가 사라졌습니다. 함께 추가된 두 줄짜리 테스트는 두 가지 순서를 모두 고정해 검증합니다.
Significance
패치 이전에는 (module (memory i64 1) (memory 1))이 validation을 그대로 통과했고, 이후 IPInt는 두 번째 32비트 memory의 bounds check를 memory 0에서 가져온 64비트 기반 주소 폭 로직으로 수행하게 되었습니다. 이런 불일치는 interpreter tier에서 OOB read/write primitive로 이어질 이론적 가능성이 있으며, 특별히 정교한 준비 없이도 조작된 모듈만으로 도달 가능한 경로입니다.
Audit directions
이 패턴은 memory 0의 속성이 선언된 모든 memory에 그대로 일반화된다고 가정하는 validator invariant이면서, 정작 런타임에는 memory 0만 참조하는 tier에서 벌어진 문제입니다. 좁게 보면, memory 0을 기준으로 삼는 다른 IPInt 및 wasm-validator invariant들을 나열해 볼 필요가 있습니다. shared 여부, page limit, growability 같은 속성들이 첫 번째 memory만 샘플링하는 대신 선언된 모든 memory에 걸쳐 가정을 검증하고 있는지 하나씩 확인해야 합니다. 조금 더 넓게 보면, JIT tier에도 같은 질문을 던져볼 수 있습니다. BBQ/OMG에도 memory별 다른 속성에 대해 이와 동일한 single-source-of-truth 문제가 있는지, 즉 memory 0에서 도출한 컴파일 타임 상수가 memory n에 대한 access에 그대로 적용되는 경우가 있는지 점검할 필요가 있습니다. 가장 넓게 보면, 모듈 전체에 걸친 제약을 항목 단위 루프로 강제하는 validator는 그 제약을 누적된 집합 전체에 대해 표현하지 않는 이상 선언 순서에 취약해질 수밖에 없습니다. 리뷰 시 눈여겨봐야 할 신호는, 형제 항목들에 걸쳐 있어야 할 제약을 정작 loop variable 하나만 읽고 검증하는 validation predicate가 파싱 루프 안에 들어있는 경우입니다.