← All issues

ScopedArgumentsTable moved out of Primitive Gigacage

72272dc

Source/JavaScriptCore/runtime/ScopedArgumentsTable.cpp

- result->m_length = length;
- result->m_arguments = ArgumentsPtr::tryCreate(length);
- if (!result->m_arguments) [[unlikely]]
+ if (!result->m_arguments.tryGrow(length)) [[unlikely]]
return nullptr;
- result->m_watchpointSets.fill(nullptr, length);
+ if (!result->m_watchpointSets.tryGrow(length)) [[unlikely]]
+ return nullptr;
+ std::ranges::fill(result->m_watchpointSets.mutableSpan(), nullptr);
...
- m_watchpointSets.resize(newLength); // 잠긴(immutable) 테이블을 변경함
- for (unsigned i = std::min(m_length, newLength); i--;) {
- result->at(i) = this->at(i);
+ for (unsigned i = std::min<uint32_t>(m_arguments.size(), newLength); i--;) {
+ result->m_arguments[i] = this->m_arguments[i];
result->m_watchpointSets[i] = this->m_watchpointSets[i];
}

Gigacage는 JSC의 공간 안전성 완화 기법으로, 특정 heap allocation을 예약된 가상 주소 영역 안에 격리합니다. 이 cage에 한정된 pointer는 손상되더라도 해당 영역 밖으로 탈출할 수 없습니다. Primitive cage는 TypedArray storage, ArrayBuffer 내용 등 사용자 페이로드 버퍼만을 위한 전용 공간입니다. ScopeOffset은 함수의 인자 목록에서 named argument의 위치를 JSLexicalEnvironment 내부의 변수 슬롯 인덱스로 매핑하는 engine 내부 메타데이터입니다.

이 commit은 ScopedArgumentsTable::m_argumentsCagedUniquePtr<ScopeOffset, Gigacage::Primitive>에서 일반 Vector<ScopeOffset>으로 변경했습니다. 이를 통해 engine 내부 scope 메타데이터가 Primitive Gigacage 밖으로 이동되었습니다. 아울러 trySetLength에서 잠겨 있어야 할(immutable) 테이블 인스턴스를 변경하던 낡은 m_watchpointSets.resize(newLength) 호출도 함께 제거되었습니다.

이 변경은 알려진 cage-pivot 경로를 차단합니다. 기존에는 Primitive Gigacage에 대한 write primitive(예: 손상된 TypedArray backing store를 통한 경우)로 ScopeOffset 메타데이터에 도달하는 것이 이론적으로 가능했습니다. 이 경우 해당 값이 JSLexicalEnvironment::variables()에 대한 unchecked index로 전환되어, 범위를 벗어난 변수 슬롯 접근이 발생할 수 있었습니다.

🔒

Cage-mismatch pattern may recur elsewhere in JSC; JIT layout assumptions and locked-table invariants have additional audit-worthy edge cases.

더 확인하려면 구독해 주세요