[5] ANGLE IndexRange integer truncation bypasses vertex bounds check
Severity: High | Component: ANGLE (WebGL) | 56c55dd
fix 이전에 크기가 부족한 buffer에 대해 최대값인 0xFFFFFFFF index가 허용되었음이 regression test로 확인되었습니다. GPU process에서 out-of-bounds vertex fetch가 발생했다는 점에서 High로 평가합니다. 다만 write primitive가 드러나지 않고, truncation이 발생하는 consumer 산술 연산이 이 diff에 포함되어 있지 않기 때문에, 정보 유출을 넘어선 확장은 제한적입니다.
IndexRange는 index 범위를 uint32_t start와 uint32_t count로 표현했는데, 이 방식으로는 [0, 0xFFFFFFFF] 범위를 올바르게 나타내기 어렵습니다. start/end 방식으로 전환하고, start > end인 경우를 empty range로 정의하도록 변경되었습니다.
Source/ThirdParty/ANGLE/src/common/mathutil.h
- IndexRange(uint32_t start, uint32_t end)
- : mStart(start), mEnd(end), mCount(static_cast<uint64_t>(end - start) + 1)
- { ASSERT(start <= end); }
- bool isEmpty() const { return mCount == 0; }
+ IndexRange(uint32_t start, uint32_t end) : mStart(start), mEnd(end) { ASSERT(mStart <= mEnd); }
+ bool isEmpty() const { return mStart > mEnd; }
...
- uint64_t vertexCount() const { return mCount; }
+ // 범위: [0, 0xFFFFFFFF] == 0x100000000 (size_t 필요).
+ size_t vertexCount() const
+ {
+ // 참고: isEmpty() == true일 때 unsigned underflow는 허용됨.
+ return static_cast<size_t>(mEnd) - mStart + 1u;
+ }
private:
- uint32_t mStart{0};
+ uint32_t mStart{1};
uint32_t mEnd{0};
- uint64_t mCount{0};
+ friend bool operator==(const IndexRange &a, const IndexRange &b) noexcept = default;
Source/ThirdParty/ANGLE/src/tests/gl_tests/WebGLCompatibilityTest.cpp
+ constexpr GLuint kIndexData2[] = { 0, std::numeric_limits<GLuint>::max() };
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(kIndexData2), kIndexData2, GL_DYNAMIC_DRAW);
+ glDrawElements(GL_LINES, 2, GL_UNSIGNED_INT, 0);
+ EXPECT_GL_ERROR(GL_INVALID_OPERATION); // 0xFFFFFFFF index 이제 거부됨
Patch Details
gl::IndexRange가 (mStart, mEnd, mCount) 표현에서 순수한 (mStart, mEnd) 쌍으로 재작성되었습니다. 생성자에서는 더 이상 mCount = end - start + 1을 미리 계산하지 않으며, isEmpty()는 mStart > mEnd로 변경되었습니다. 기본 초기화 값은 mStart{1}, mEnd{0}으로 설정되어 기본 range가 empty 상태가 됩니다. vertexCount()는 uint64_t에서 size_t로 변경되어 요청 시 계산되며, [0, 0xFFFFFFFF]가 0x100000000을 산출한다는 명시적인 주석이 추가되었습니다. 직접 작성된 operator==는 삭제되고 기본 멤버별 비교로 대체되었습니다. 새로운 단위 테스트가 full-range 값을 검증합니다. 또한 WebGLCompatibilityTest.cpp에서는 소형 buffer에 대해 0xFFFFFFFF index로 glDrawElements를 호출했을 때 GL_INVALID_OPERATION이 반환됨을 확인합니다.
downstream draw-bounds 산술 연산에서 inclusive full-width integer range의 cardinality가 truncation되는 패턴. 이 값은 33비트가 필요하지만, truncation으로 인해 최대 범위가 apparently empty 또는 in-bounds로 축소되어 vertex bounds check가 무력화됩니다.
Background
WebGL의 drawElements는 indexed draw를 수행합니다. element-array buffer에는 bound된 vertex attribute array에서 vertex를 선택하는 정수 index가 담겨 있습니다. draw를 실행하기 전, ANGLE은 실제로 참조되는 index의 최솟값과 최댓값을 계산합니다. 이때 최댓값이 bound된 buffer가 제공하는 vertex 수를 초과하면 GL_INVALID_OPERATION이 반환되어야 합니다.
gl::IndexRange는 이 inclusive [min, max] 범위를 담습니다. inclusive range의 cardinality는 max - min + 1이므로, [0, 0xFFFFFFFF] 전체 범위에는 0x100000000개의 vertex가 존재합니다. 이 값은 33비트가 필요하기 때문에 32비트 정수에는 들어가지 않습니다. ComputeTypedIndexRange는 index buffer를 스캔하여 range를 생성합니다. primitive restart가 비활성화된 경우 0xFFFFFFFF는 실제 index로 처리됩니다. WebKit에서 ANGLE은 샌드박스 처리된 GPU process 내부에서 실행됩니다.
Analysis
이 문제는 integer truncation/overflow로 인해 bounds check가 우회되고 out-of-bounds read가 발생하는 취약점입니다. Inclusive full-width range [0, 0xFFFFFFFF]의 cardinality는 0x100000000으로, 33비트가 필요합니다. 주목할 점은, 이 diff에서 제거된 IndexRange 코드 자체에서는 wrap이 발생하지 않았다는 것입니다. 기존의 mCount는 uint64_t로 0x100000000을 정확히 보관하고 있었습니다. 따라서 0으로의 truncation은 이 cardinality를 가져와(또는 upper bound를 재구성하여) uint32_t나 GLsizei처럼 더 좁은 타입에 저장하는 consumer 산술 연산에서 발생합니다.
Aaaaaaaaaaaaaaaaa Aaaaaaa Aaaaaaaaaaaaa Aaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaa Aaaaaaaaaaaaa Aaaaaa Aaaaaaaaaa Aaaaaaaaaa Aaa Aa Aaaaaa Aaaaaa Aaaaaaaaa Aaaaaa Aaaaa Aaaaaa a Aaaa Aaaa Aaaa a Aaaa Aaaaaa Aaaaaaaaaaaa Aaaa Aaa Aaaaa Aaaaaaaaaaaaaa Aaaaa Aaaaaaaaaaaaaa a Aa Aa Aaa Aaaaa Aa Aaaaaaaaaaa Aaa Aa Aaaaa Aaa Aaaa Aa Aaaaa Aaaaaa Aaaaaaaaaaa Aaaa Aaa Aaa Aaaaa Aaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaa Aaaaaa a Aaa Aa Aaaaa Aaaa Aa Aaaaa Aaa Aa Aa Aaa Aaa a Aa Aa Aaaaaa Aaaaaa Aaaaaaaaaa Aaaaaaa Aaaa Aa Aaa Aaa Aaa a Aaaaa Aa Aaaaaaa Aa Aaaa Aaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaaaaaaaaaaaaaaa Aaaa Aaaa Aa Aaaaaaaaaaaaaaaaaaaaaaa Aaaaaa
a Aaaa Aaaaa Aaaaaaaaaaaaaaa Aa Aaa Aaaaaaaaa Aa Aaa Aaaa Aaa Aaaaaaa a Aaaaaa Aaaaaaaaa Aaaa Aa Aaaaaaaaaa Aaaaaaaaaaaaa Aaaaa Aaa Aaaa Aaaaa Aaaaaaaaaaaaa Aaa Aaaa Aaa Aaaaaaaaaaaa Aa Aa Aaaaa Aaaaa Aaaaaaaaaa Aaaa Aaaaa Aaa Aaaa Aaaaaa Aa a Aaaa Aaa Aaa a Aaa Aaaaaa Aaaaaaaa Aaa Aaaaa Aaaaaaaaaaaa Aaaaaaaaa Aaaaaa Aa Aa Aaaaa Aaaa Aa Aaaaaaaaaa Aaaaaa
a Aaaa Aaaaa Aaaaaaa Aaaaa Aaa Aaa Aaaaa Aaa Aaaaaaa Aaaaaa Aaaaaaa Aaaaaaaaa Aaaaaaa Aaa Aaa Aaaaaaa Aaaa Aaaaa Aaaa Aaa Aaaa Aaaaa Aaa Aaaa Aa Aaaa Aaaaaaa a Aaaa Aaaaaaaa Aaaaaa Aaa Aaaaaaaaa Aaaa Aaa Aaa Aaaaa Aaaaaaaaaaa Aaaaaaa Aa Aa Aaa Aaaaaa Aa a Aaaa Aaa Aaaaa Aaa Aaaaaaaaaaa Aaaaaaa Aaaaaa
Aaaaaaaaa Aaaaaaaaaaaaaa Aaaaa Aaaaaaaaaaaa Aaa Aaaaaaaa Aa Aaa Aa Aa Aaa Aaaaaaaaaa Aaaaaaa Aaa Aaaa Aaaaaa Aa Aaaaa Aaaaa Aaaaaa Aaaaa Aaa a Aaaa Aaaa Aa Aaaaaa
🔒Where the maximal-index range actually loses precision, and how far that propagates into the GPU draw pipeline, is traced in depth.
더 확인하려면 구독해 주세요
Audit directions
a Aaaaaa Aaaaaa Aaaaaaaaa Aa a Aaaaaaaaaaaa Aaaaaaaaaaa Aa Aaaa Aaaa Aaa Aaaaaaaaaa Aaaaaa Aaaaaa Aaaaa Aaaa a Aa Aaaa Aaaaaaaaaaaa Aaaaaa Aaaaaaaaaaaaaaaaa Aaaa Aaaaa Aaaaaaaaaaaaaaa Aaa Aaa Aaa Aaaaa Aa Aaaaaaaaaaaaaaaa Aaaa a Aaaaa a Aaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaa Aaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaa Aaaaaaaaaa Aaaaa Aaaaaaaaaaa Aaaaaa Aa Aaaa Aaaaaa Aa Aaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaa Aaaaa Aaaa Aaaa
a Aaaaaaaaaaa Aaaaaaaaaaa Aaaaaa Aaaaaaaaaa Aa a Aaaaaaaaaa Aaaaaaaa Aaaaaa Aaaaaa Aaaaaaaaaaaa Aaaaaaaaaaaa Aaaaaaa Aaaaaaaaaa Aaaaaa Aa Aa Aaa Aa Aaa Aa Aaa Aaaaaa a Aaaaaaa Aaaaa Aa Aa Aaa a Aa Aaa Aaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaaaaaaaaa Aaa Aaa Aaa Aa Aaa Aaaa Aaaa
a Aaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaa Aaaaa Aaa Aaaaaaaaaaaaaa Aaaaaaaaa Aaaaaaaaaaaaaaaaaa Aaaaaaa Aaaaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaaa Aaaaaaaaa Aaaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaa Aaaaa Aaaaaa Aa Aa Aaa Aaaaaaa Aa Aaaaaa Aaaaaa Aaaaaaaaaaaa Aaa Aaa Aaa Aaaa Aaaa
🔒Multiple reusable audit patterns identified for integer-cardinality truncation at bounds-check boundaries, with concrete ANGLE starting points for variant discovery.
더 확인하려면 구독해 주세요