[2] WebGPU vertex buffer OOB read via flipped comparison in index validation
Severity: High | Component: WebGPU Buffer | 15ddef0
WebGPU API를 통해 임의의 웹 페이지에서 접근 가능한 vertex buffer 메모리에 대해 GPU 측 OOB read가 직접 관찰되므로 High로 평가됩니다. Bypass 메커니즘(보안에 중요한 validation 함수 내 비교 연산자 방향 오류)은 단일 라인 diff에서 confidence 0.95로 확인됩니다. 다만 shader output을 통한 GPU memory exfiltration과 cross-context data leakage는 플랫폼별 GPU memory model에 의존하므로, 이 부분은 확인되지 않은 사항입니다.
Buffer::needsIndexValidation에서 m_maxUshortIndex > maxUshortIndex라는 비교 조건의 피연산자 순서가 뒤집혀 있었습니다. 올바른 방향은 maxUshortIndex > m_maxUshortIndex입니다. 수정 후에는 두 if 블록을 단일 needsUpdate 표현식으로 대체했습니다. 두 비교 모두 올바른 방향의 || 조합으로 구성되었으며, cached 값은 std::max를 통해 무조건 갱신됩니다.
Source/WebGPU/WebGPU/Buffer.mm
bool Buffer::needsIndexValidation(uint32_t maxUnsignedIndex, uint16_t maxUshortIndex)
{
- bool needsUpdate = false;
- if (maxUnsignedIndex > m_maxUnsignedIndex) {
- m_maxUnsignedIndex = maxUnsignedIndex;
- needsUpdate = true;
- }
- if (m_maxUshortIndex > maxUshortIndex) {
- m_maxUshortIndex = maxUshortIndex;
- needsUpdate = true;
- }
+ const bool needsUpdate = maxUnsignedIndex > m_maxUnsignedIndex || maxUshortIndex > m_maxUshortIndex;
+ m_maxUnsignedIndex = std::max(m_maxUnsignedIndex, maxUnsignedIndex);
+ m_maxUshortIndex = std::max(m_maxUshortIndex, maxUshortIndex);
return needsUpdate;
}
Patch Details
기존 코드는 두 개의 if 블록으로 구성된 update-and-check 패턴이었습니다. 첫 번째 블록은 maxUnsignedIndex를 처리하며 비교 방향이 올바랐습니다(maxUnsignedIndex > m_maxUnsignedIndex). 두 번째 블록은 maxUshortIndex를 처리했는데, 피연산자 순서가 뒤집혀 있었습니다(m_maxUshortIndex > maxUshortIndex).
이 뒤집힌 조건은 두 가지 문제를 동시에 유발했습니다. 먼저 cached max가 새 max보다 클 때만 조건이 성립하는데, 이는 필요한 방향과 정반대입니다. 또한 조건이 성립하면 더 작은 새 값이 m_maxUshortIndex에 하향 할당됩니다.
수정 후에는 단일 const bool needsUpdate로 두 비교를 올바른 방향으로 결합하고, 두 cached 값을 std::max로 무조건 갱신합니다.
Background
WebGPU의 drawIndexed는 index buffer를 통해 처리할 vertex를 선택합니다. Index buffer에는 vertex buffer에 대한 정수 인덱스가 담겨 있습니다. Draw를 실행하기 전, 구현은 index buffer의 최대 인덱스가 vertex buffer의 범위를 초과하지 않는지 검증해야 합니다. 이 검증이 없으면 GPU가 vertex data 영역 밖을 읽게 됩니다.
Buffer::needsIndexValidation은 최적화를 위한 함수입니다. 이전에 검증된 최대 인덱스를 cached 값으로 보관하며, uint32 인덱스는 m_maxUnsignedIndex에, uint16 인덱스는 m_maxUshortIndex에 저장됩니다. 함수는 현재 draw의 최대 인덱스가 이전에 검증된 최댓값을 초과하는지, 즉 새로운 validation 수행이 필요한지 여부를 반환합니다. true이면 호출자가 재검증을 수행해야 하고, false이면 기존에 검증된 범위가 현재 draw를 이미 포함하는 것으로 간주됩니다.
Analysis
Index validation guard에서 비교 피연산자 순서가 뒤집혀, 닫혀야 할 때 열리고 열려야 할 때 닫히는 역전 현상이 발생한 패턴.
m_maxUshortIndex > maxUshortIndex 조건은 cached max가 새 max를 초과할 때만 성립합니다. 보안상 중요한 방향과 정반대입니다. draw call이 이전보다 큰 ushort 인덱스를 제시하는 경우(위험한 상황)에는 조건이 false가 되어 needsUpdate가 false로 유지되고, m_maxUshortIndex도 갱신되지 않습니다. 결과적으로 재검증이 반드시 필요한 상황에서 함수는 호출자에게 불필요하다고 알립니다.
반대로 더 작은 인덱스(이미 검증된)가 제시될 때는 조건이 true가 됩니다. 이때 cached max가 더 작은 값으로 하향 갱신되고, 함수는 불필요하게 재검증이 필요하다고 보고합니다. uint32 경로는 올바른 반면 uint16 경로만 뒤집혀 있었는데, 두 번째 블록을 작성할 때 피연산자 순서가 역전된 전형적인 copy-paste 오류입니다.
a Aaa Aaaa Aaa Aaaaaa Aa Aaaa Aaaaaa Aaaaa Aaaaaaa Aaaa Aaaaaa Aaaaaaaaa Aaaaaa a Aa Aaaaaaaaaaaaa Aaaaa Aa Aa Aaaa Aaa Aaaaaaaaaaa Aaaaaa Aa Aa Aaaaaa Aaaaaa Aa a Aa Aaaaaaaaaaaaa Aaaa Aaaaaa Aaaaaa Aaa Aa Aaaa a Aa Aaaa Aaaaaa Aa Aaa Aaa Aa Aaa Aaaaaaaaaaaaaaaaaaaaaaa Aaaaaa Aaaaaa Aaaa Aaaa Aaa Aaaa Aaa Aaaa Aaaaa Aaaaaa Aa Aaaaaaa Aaaaaa Aaaaaa Aa Aa Aa Aaaaa Aaaaaa Aaaaaaaaaa Aaaaa Aaaa a Aaaa Aaaaaaa Aaaaaaa Aaaaaa Aaaaaaa Aa Aaaaaa Aaaaaaa Aa Aaa a Aaaaa Aa Aa Aaa Aaaaaa Aaaaaa Aaaa Aaa Aaaa Aaaa Aaa Aa Aaaaaa
a Aaa Aaa Aaaaaaa Aaaa Aaaaaa Aaaaaa Aaa Aaa Aaaaaaaaaa Aaa Aaaaa Aaaa Aaaa Aaa Aaaaa Aaaa Aaa Aaaaaaaa Aaa Aaa Aaaaaa Aaaa Aaa Aaaaaa Aaaaaa Aaaa Aa Aa Aaaaa Aa Aaaaaaaaaaa Aa Aaaa Aaaaa Aa Aa Aaa Aaaaaa Aaaaaaaa Aaaa Aaaaa
a Aaaaaaaaaaaaaa Aaaaaa Aaaaaa Aaa Aaa Aa Aaa Aaa Aaaaaa Aaaaaa Aaa Aaaaaaa Aaaaaa Aaa Aaaaa Aaaaaaaaaaa Aa Aaaaaaa Aaaaaa Aaaaaaa Aaaaa Aa Aa Aa Aaaa Aaaaaa Aa Aaaa Aaaaaaaaaaaaaaa Aaaaaaaaaaaaaa Aaaaa Aaaaaaa Aaaa Aaaa a Aaa Aaa a Aaaa Aaa Aaaaaa Aaa Aaaaaa Aaaaaaa Aa Aaa Aaaaa Aaaa Aaa Aaaaaaa Aa Aa Aaa Aaa Aaa Aaa Aaaaaa Aaaaaaa Aa Aaaaa Aaa Aaaa Aaaaa
Aaaa Aaa Aaa a a Aa Aaa Aaa Aaa Aa Aaa Aaa Aaaa Aaaaaa Aa Aa Aaaa Aaaa Aaaaaaa Aaaa Aaa Aaa Aaa Aaaa Aaaaa Aaaaaaaaaa Aa Aa Aa Aaaa Aa Aa Aa Aaa Aaa Aaaaa
🔒Explores the exploitation mechanics of bypassing GPU index validation and what GPU memory could be disclosed
더 확인하려면 구독해 주세요
Audit directions
a Aaa Aa Aaa Aa Aaaa Aaaa Aaaaaaa Aaaa Aaa Aa Aa Aaaaaaaaaa Aaa Aaaaaaa Aaa Aa Aaa Aa Aa Aaaa Aaa Aaaaa Aa Aaaaaaaaaaa Aaa Aaaaaaaa Aaaa Aaaa Aaaaaaaaaaa Aa Aaaaaaaaaa Aa Aaaaaaaaaaaa Aaa Aaaa Aaaaaa Aaaaaaaaaa Aaaaaa Aaaaa Aaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaa Aa Aaaaaaaaaa Aaa Aa Aaa Aaaaa a Aaa Aa Aaa Aaaaaaa
a Aaa Aa Aa Aaa a Aaa Aaaaaaaaa Aa Aaaaaaaa Aa Aaaaa a Aaa Aaaa Aa Aaaa Aaaa Aaa Aaaa Aaa Aaaaaaaaa Aaaaaaaa Aaaaa Aaa Aa Aaaa Aaaaaa Aaa Aaaa Aaaa Aaa Aa Aaa Aa Aaaaa Aaaaaaaaaaaaaaa Aaa Aa Aaaa Aaa Aaa Aaaaaaa a Aaa Aa Aa Aaaa Aa Aaa Aaaa Aaaa Aaaa
a Aaaaaaaaaaaaaaaaaaaaaaa Aa Aaa Aaaa Aaaaaaa Aaa Aa Aaaa Aaa Aaaa Aaaaa Aaaaa Aaaa Aaaa Aaaa Aaaa Aaa Aa Aaaa Aa Aaa Aa Aaaa Aaaa Aaa Aaaa Aaa Aaa Aa Aaa a Aaaaaa Aaaaaaaaaaaaaaa Aaaaaaaaaa Aaaaa Aa Aaaaa Aaaaaa Aaaaaaa
Aaaaaaaaa Aa Aa Aaa Aaaaa Aaa Aaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaa Aaaa Aaaa Aaaaa Aa a Aaa Aaa Aaaaaaa Aa Aaa Aaaaaa Aaaaaaa Aa Aaa Aaaaa Aaaaaa Aaaaa Aaa a Aaa Aa Aa Aaaaaa Aa Aaaaaa Aaaa a Aaaa Aaaaaaaaa Aaaa Aa Aaaaaaaaaa Aaaaaaaa Aaaaaa Aa Aaaaaa
🔒Multiple audit patterns identified for WebGPU validation caching and index-type handling, with concrete search targets
더 확인하려면 구독해 주세요