← All issues

[2] [JSC YARR] Fix integer overflow in AssemblerBuffer leading to heap overflow

Severity: High | Component: JSC AssemblerBuffer / YARR | 7663d81

공격자가 조작한 regex 입력 하에서 32-bit wrap이 발생할 수 있었던 code-buffer 길이 연산이 이번 diff에서 확장되었습니다. 하위 단계의 putIntegralUnchecked는 실제 allocation 범위를 넘어 unchecked write를 수행합니다. 그 결과 JIT가 내보내는 내용을 인접 heap에 선형으로 기록하는 primitive가 성립합니다. High로 평가된 이유입니다.

m_indexm_capacity, 그리고 isAvailable(), putIntegral(), grow() 내의 산술 연산이 unsigned에서, YARR 구동 code emission 중 wrap이 발생하지 않는 너비로 확장되었습니다. 아울러 YARR에 TooManyCapturesFrameTooLarge 오류가 추가되었습니다. commit 메시지는 이 두 가지를 "not actually critical"로 명시하고 있으며, 패턴 컴파일 단계에서의 DoS 및 frame bookkeeping overflow에 해당합니다.

Source/JavaScriptCore/assembler/AssemblerBuffer.h

- bool isAvailable(unsigned space)
- {
- return m_index + space <= m_capacity;
- }
+ bool isAvailable(size_t space)
+ {
+ return m_index + space <= m_capacity;
+ }

JIT code buffer 길이 연산의 32-bit overflow: wrap된 nextIndex가 capacity check를 우회하고, putIntegralUnchecked가 실제 allocation 범위를 넘어 내보낸 machine code를 기록합니다.

buffer 너비가 확장되었습니다. isAvailable, putIntegral, grow의 산술 연산은 확장된 타입으로 재계산됩니다. YARR의 패턴 컴파일러에는 TooManyCapturesFrameTooLarge 조기 종료 조건이 추가되어, 비정상적인 패턴이 emitter에 도달하기 전에 실패하도록 처리됩니다.

AssemblerBuffer는 JSC의 모든 JIT 백엔드가 linking 전 machine code를 축적하는 emission buffer입니다. YARR, baseline, DFG, FTL, Wasm BBQ/OMG가 모두 이 buffer를 사용합니다.

isAvailable(n)putIntegralUnchecked<T> 호출의 사전 조건입니다. putIntegralUnchecked<T>WTF::unalignedStore<T>(m_storageBuffer + m_index, value)를 수행하고 m_index를 증가시킵니다. putIntegralisAvailable이 공간 부족을 보고하면 grow()를 호출합니다.

buffer의 크기는 unsigned 기준으로 관리되고 있었습니다. 그러나 YARR은 공격자가 조작한 패턴으로 emission 크기를 2^32 너머까지 유도할 수 있습니다. 이 불일치가 이번 취약점의 근원입니다.

패치 이전의 산술 연산 지점 세 곳은 모두 32-bit로 계산되었습니다:

isAvailable(space):   return m_index + space <= m_capacity;        // unsigned wrap
putIntegral(value):   nextIndex = m_index + sizeof(IntegralType);   // unsigned wrap, "nextIndex > capacity" guard 우회됨
grow(extra):          newCapacity = m_capacity + m_capacity/2 + extra;  // unsigned wrap, 실제 기록 크기보다 작은 크기로 allocation됨

YARR 패턴이 emission을 2^32 바이트 방향으로 몰아가면, 이 연산들에서 wrap이 발생합니다. wrap 이후에는 isAvailable이 true를 반환합니다. 이어서 putIntegralnextIndex > capacity 검사가 우회됩니다. grow()는 실제 기록 크기보다 작은 크기로 buffer를 조정합니다. 결과적으로 putIntegralUnchecked가 live allocation 범위를 넘어 인코딩된 machine code를 unaligned store로 기록하게 됩니다.

🔒

Detailed analysis of how a 4 GiB JIT-buffer size limit was being enforced, and what could happen when that enforcement folded under arithmetic wrap.

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

🔒

Multiple reusable audit patterns identified for integer-overflow-in-code-emission and parser-driven frame-layout accumulators, with concrete starting points across JSC's JIT pipelines.

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