← All issues

[11] [JSC] Use span's length in genericTypedArrayViewProtoFuncSortImpl

Severity: High | Component: JSC TypedArray prototype | c852586

GSAB 기반 TypedArray length에 대한 TOCTOU 수정. 병렬 grow 연산이 length()typedSpan().size()를 desynchronize시키며, sort 본문이 두 값 모두 사용하여 element copy 중 OOB 발생. High 평가.

genericTypedArrayViewProtoFuncSortImplthisObject->length()thisObject->typedSpan()으로 length를 두 번 독립적으로 읽었습니다. Growable SharedArrayBuffer를 backing으로 사용하는 경우, 두 읽기 사이에 다른 agent가 grow()를 호출하면 두 값이 일치하지 않게 됩니다.

Source/JavaScriptCore/runtime/JSGenericTypedArrayViewPrototypeFunctions.h

- size_t length = thisObject->length();
+ auto originalSpan = thisObject->typedSpan();
+ size_t length = originalSpan.size();
+
if (length < 2)
return JSValue::encode(thisObject);
 
- auto originalSpan = thisObject->typedSpan();
-
Vector<typename ViewClass::ElementType, 256> vector;

순서가 바뀌었습니다. originalSpan을 먼저 생성하고, lengthoriginalSpan.size()에서 파생합니다. 결과적으로 단일 snapshot에서 단일 source of truth를 유지하게 됩니다.

공유 메모리 length에 대한 TOCTOU: race가 발생하기 쉬운 동일한 값을 단일 snapshot 대신 독립적인 두 번의 읽기로 파생하는 패턴.

maxByteLength를 지정해 생성한 Growable SharedArrayBufferSharedArrayBuffer.prototype.grow를 통해 확장할 수 있습니다. GSAB 위에 생성된 length-tracking TypedArray view는 현재 SAB의 byte length에서 파생된 length를 반환합니다. SAB는 agent(main thread와 Worker) 간에 공유되므로, length를 다시 파생할 때마다 구현 아래에서 변경될 수 있는 값을 읽게 됩니다.

std::span은 호출 시점에 캡처된 {pointer, size} 쌍입니다. %TypedArray%.prototype.sort는 사용자 comparator를 받으며, C++에서는 work buffer로 복사한 뒤 정렬하고 다시 복사하는 방식으로 구현됩니다.

length는 grow 이전 크기를 반영하고, originalSpan은 grow 이후를 반영합니다. sort 본문은 두 값 모두 사용합니다. work vector의 크기는 length 기준으로 잡으며, span은 element copy와 sort callback의 source이자 destination이 됩니다. 이로 인해 length == originalSpan.size() 불변식이 더 이상 유지되지 않습니다.

Exploit 형태는 다음과 같습니다. 먼저 초기 크기보다 훨씬 큰 maxByteLength로 GSAB를 생성하고, 그 위에 length-tracking Int32Array를 만듭니다. SAB를 Worker에 전달하여 sab.grow()를 반복 호출하게 합니다. main thread에서는 ta.sort(compareFn)을 반복 호출합니다. 그러면 sort 본문이 불일치하는 bounds로 동작하게 됩니다. grow 이전과 이후 length의 크기 차이 범위 안에서, element copy나 write-back 중 OOB read 또는 write가 발생합니다. regression test를 보면 이미 with/toReversed/toSorted에는 적용되어 있었으나, sort에는 누락되어 있었음을 확인할 수 있습니다.

이 vulnerability는 WebContent process 내부의 memory type safety를 약화시킵니다. main thread와 Worker를 모두 제어할 수 있는 공격자는 sort work vector 또는 GSAB 기반 storage에 인접한 bounded OOB primitive를 유도할 수 있습니다.