← All issues

[8] GetByStatus walks prototype chain for direct/private property opcodes

Severity: Medium | Component: JSC bytecode IC status + DFG | b07dca9

DFG가 prototype-walk 가정 하에 GetByIdDirect/GetPrivateNameById를 특수화할 수 있었던 JIT IC status helper를 수정한 패치입니다. non-own slot에서 controlled property fetch로 이어지는지 여부는 downstream lowering에 달려 있으며, diff만으로는 확인되지 않습니다. 이러한 점에서 Medium으로 평가합니다.

GetByStatus를 계산하는 과정에서는, prototype walk를 수행하기 전에 먼저 direct property access인지 확인해야 합니다. direct access는 prototype을 참조해서는 안 되기 때문입니다.

Source/JavaScriptCore/bytecode/GetByStatus.cpp

-GetByStatus GetByStatus::computeFor(JSGlobalObject* globalObject, const StructureSet& set, CacheableIdentifier identifier)
+GetByStatus GetByStatus::computeFor(JSGlobalObject* globalObject, const StructureSet& set, CacheableIdentifier identifier, GetByStatus::LookupMode mode)
{
- // 이 코드는 GetByIdDirect에서도 사용됩니다. 이 함수는 direct property만 조회하므로,
- // prototype chain을 지원하려면 GetById와 GetByIdDirect를 별도로 처리해야 합니다.
...
- if (auto result = attempToFold())
- return result.value();
+ // prototype walk는 일반 access인 경우에만 수행해야 합니다.
+ // direct access에서는 prototype을 참조하지 않습니다.
+ if (mode == GetByStatus::LookupMode::Normal) {
+ if (auto result = attempToFold())
+ return result.value();
+ }

Source/JavaScriptCore/dfg/DFGNode.h

+ GetByStatus::LookupMode propertyLookupMode()
+ {
+ switch (op()) {
+ case GetByIdDirect:
+ case GetByIdDirectFlush:
+ case GetPrivateNameById:
+ return GetByStatus::LookupMode::Direct;
+ case GetById:
+ case GetByIdFlush:
+ case GetByIdMegamorphic:
+ return GetByStatus::LookupMode::Normal;
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+ }

GetByStatus에 새로운 LookupMode enum(Normal/Direct)이 추가되었고, computeFor의 파라미터로도 포함되었습니다. computeFor 내부의 prototype walk(attempToFold() 블록)는 이제 mode == LookupMode::Normal인 경우에만 실행됩니다. Node::propertyLookupMode()가 추가되어 DFG opcode를 적절한 mode에 매핑합니다. DFGAbstractInterpreterInlines.h::executeEffectsDFGConstantFoldingPhase::foldConstants는 node로부터 mode를 결정하도록 수정되었습니다. DFGByteCodeParserop_get_from_scopeNormal로 고정됩니다. global property lookup은 global object의 prototype chain을 순회해야 하기 때문입니다. 함수가 direct property만 조회한다고 기술한 낡은 주석은 제거되었습니다.

JIT IC status 계산 과정의 speculation-soundness 위반: prototype 순회를 금지하는 opcode에 공유 helper가 prototype-chain 의미론을 적용하는 패턴.

GetByStatusGetById 계열 bytecode가 런타임에서 수행한 동작의 요약입니다. 관찰된 structure, offset, prototype walk 정보를 담아, DFG가 load를 inline할지, 결과를 constant-fold할지, 아니면 generic IC로 전환할지를 결정하는 근거가 됩니다. GetById는 ECMAScript [[Get]]을 완전히 수행하며 prototype chain을 순회합니다. 반면 GetByIdDirect는 own property에만 접근하며, 해당 property가 own property가 아닌 경우 반드시 undefined를 반환해야 합니다. GetPrivateNameById는 private class field를 읽는 opcode로, receiver의 brand를 키로 사용하며 스펙상 own property만 접근하도록 제한됩니다. computeFor 내부의 attempToFold는 prototype chain을 순회해 property를 탐색하고, 발견 시 Simple variant로 fold하는 역할을 맡습니다. DFG의 AbstractInterpreterConstantFoldingPhase는 반환된 GetByStatus를 바탕으로 IR을 특수화합니다.

GetByStatus::computeFor는 항상 attempToFold()를 호출했으며, 이 함수는 prototype chain을 순회합니다. 문제는 DFG pass에서 모든 GetBy* node에 대해 예외 없이 호출되었다는 점입니다. GetByIdDirect, GetByIdDirectFlush, GetPrivateNameById처럼 런타임 의미론상 own property만 접근해야 하는 opcode도 마찬가지였습니다. 낡은 주석은 이 함수가 direct property만 조회한다고 기술하고 있었는데, 작성 당시에는 정확한 설명이었습니다. 하지만 attempToFold가 나중에 추가되면서 사실과 달라졌습니다.

🔒

The speculation-soundness implications of conflating own-property and prototype-walking semantics in a shared JIT helper, and the conditions under which this can escalate beyond a pure correctness bug

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

🔒

Several reusable audit patterns identified across JSC's IC status computation layer, with concrete starting points for finding variants in sibling status helpers

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