[2] WebGPU vertex buffer OOB read via flipped comparison in index validation
Severity: High | Component: WebGPU Buffer | 15ddef0
Rated High because the observable effect is a GPU-side out-of-bounds read from vertex buffer memory reachable from any web page via the WebGPU API, and the bypass mechanism (a flipped comparison operator in a security-critical validation function) is confirmed at confidence 0.95 from the single-line diff — unverified claims concern GPU memory exfiltration via shader output and cross-context data leakage, which depend on platform-specific GPU memory models.
The comparison m_maxUshortIndex > maxUshortIndex in Buffer::needsIndexValidation was flipped — it should have been maxUshortIndex > m_maxUshortIndex. The fix replaces both branches with a single needsUpdate expression using || of the two comparisons (both in the correct direction) and updates both cached values via 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
The patch replaces a two-branch update-and-check pattern with a single boolean expression. The old code had two if-blocks: the first for maxUnsignedIndex (correct: maxUnsignedIndex > m_maxUnsignedIndex) and the second for maxUshortIndex (flipped: m_maxUshortIndex > maxUshortIndex). The flipped branch had two effects: it triggered only when the cached max exceeded the new max (the opposite of what's needed), and it then assigned the smaller new value downward into m_maxUshortIndex. The fix uses a single const bool needsUpdate with both comparisons in the correct direction, then unconditionally updates both cached values via std::max.
Background
WebGPU's drawIndexed call uses an index buffer to select which vertices to process. The index buffer contains integer indices into the vertex buffer. Before issuing the draw, the implementation must validate that the maximum index in the index buffer does not exceed the vertex buffer's bounds — otherwise the GPU would read past the end of the vertex data.
Buffer::needsIndexValidation is an optimization: it caches the previously validated maximum index (m_maxUnsignedIndex for uint32 indices, m_maxUshortIndex for uint16 indices) and returns whether a new validation pass is needed — i.e., whether the current draw's maximum index exceeds the previously validated maximum. A true return means the caller must re-validate; false means the previously validated bound still covers the current draw.
Analysis
Flipped comparison operands in an index-validation guard, causing the validation gate to open when it should close and vice versa.
The ushort comparison m_maxUshortIndex > maxUshortIndex fires only when the cached max exceeds the new max — the opposite of the safety-relevant direction. When a draw call presents a larger ushort index than previously seen (the dangerous case), the condition is false, needsUpdate stays false, and m_maxUshortIndex is not updated. The function tells the caller no re-validation is needed when it absolutely is. Conversely, when a draw call presents a smaller index (already validated), the condition is true, the cached max is downgraded to the smaller value, and the function spuriously reports that re-validation is needed. The uint32 path was correct while the uint16 path was flipped — a classic copy-paste error where the operand order was reversed in the second branch.
Aaa Aaaaaaa Aa Aaaaaaaaaaaaaaaa Aa Aaaaaaaa Aaaaaaa a Aaaaaa Aaaaaaaa Aaaa a Aaaaaa Aaaaa Aaaaaaa Aaaa Aaaaa Aaaaa a Aaaaaaaaaaaaa Aaaa Aaaa a Aaaaa Aaaaaaa Aaaaa a Aaaaaaaaaa Aaaaaaa Aaaaaaa Aaa Aaaaa Aaaa Aaaa Aaaa Aaaaa a Aaaaaa Aaaaaaaaaaaaa Aaaa a Aaaa Aaaaaa Aaaaaaa Aaaaa Aaaa Aaaaaaa Aaa Aaaaaa Aaaaaa Aaaaaaa Aaaaaaa Aaa Aaaaaaaaaa Aa Aaaaaaaa Aaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaaa a Aa Aaaaaaaaaaaaa Aaaaaa a Aaa Aaa Aaa Aaaaaaaa Aaa Aaaa Aaaa Aaa Aaaaaaaa Aaa Aaaaaa Aaaaa Aaaaaa Aaaaaaaaaa Aaaa Aaaaaa Aaaaaa Aaa Aaaaaa Aaaaaa Aaaaaaaaaaa Aaa Aaaaaaaa Aaaaaaaa Aaaa Aaaa Aaa Aaaaaa Aaaaaa Aa a Aaaaaaa Aaaaaa Aa Aaaaaa Aaaaaa Aaa Aaa Aaa Aaaaaa Aaaaa Aaaaaaa a Aaaa Aaaaaaa Aa Aaaaaaaaaaaaaaaaa Aaaaaaaaaa
Aaaa Aaa Aaaaaaaa Aa Aaa Aaa Aaaaaaa Aaaaaa Aaaaaa Aaaaaaaa Aaa Aaaaaaaaa Aa Aaa Aaa Aaaaaaa Aaa Aaaaaaaa Aa Aaa Aaaaa Aaa Aaaaa Aaaaa Aaaaaa Aaaaaaaaaaa Aaaaaa Aaaaaaaaa Aaaaaaaaa Aa Aaa Aaaaaaaaaa Aaa Aaaaaa Aaaaaa Aaaaaaaa Aaaaaaaaaaa Aaaa Aaaaa Aaaaaaaa Aaaaaaaa Aaaaa Aaaaaaaaaaa Aa Aaaaaaaa a Aaaaaa Aaaa Aa Aaaaaaaaaaaaaaaaaa Aaa Aaa Aaaaaaaa Aaaa Aaa Aaaa Aaaaaa
Aaaa Aaaaaaaaaaaaa Aaaaaaa Aaa Aaaaaa Aaaaaa Aaaaaaaa Aaaaaaa Aaaaaa Aaaaaa Aaaaaaaaa Aaa Aaaaaaaaaaaaaa Aaaaaaa Aaa Aaaaaa Aaaa Aaaaaaaa Aaaa Aaaaa Aaaaaaaaaa Aaaaaaaa Aaaaaaa Aaaa Aaaaaaa Aaaaaa Aaaaaa Aaaa Aaaaaa Aaa Aaaaa Aaaaaaa Aaaaaa Aaa Aaaa Aa Aaaaaaaa Aaaaa Aaaaaa Aaaaaaaa Aaaaa Aaaaaaa Aaaa Aaaaaaaaaaaaa Aaaaa Aaaaaa Aaaa Aaaaaaaaaaa Aaaaaaaa Aaa Aaaaaa Aaaa Aa Aaaaaaa Aaa Aaaaa Aaaa Aaaaaa Aaaaaaaa Aaaa Aaaaa Aaaa Aaaaaaaa Aaa Aaaaaa Aaaaaaaa Aaaa Aa Aaa Aaaaaaaa Aaa Aaaaaa Aaaaaaa
Aaaa Aa a Aaaaaaaaaaaaaaaa Aaaaa Aa Aaa a Aaaaaaaa Aaa Aaaaaaa Aaaaa Aa a Aaaaaaaaaa a Aaaa Aaaaaaaaaa Aaaaaaa Aa Aaaaaaaaa Aaaaaaaa Aaaaaaaaaaa Aaa Aaaaaaaaaaaaa Aaaaaaa Aaaaaaaaaaaa Aaaa Aaaaaaaaaa Aa a Aaaaaaaaaaaaaaaaa Aaaaa Aaaa Aaaaaaaaaaaaa Aa Aaaaaaaaaa Aaaa Aaaaaaa Aaaaa Aaaaaaaaa
🔒Explores the exploitation mechanics of bypassing GPU index validation and what GPU memory could be disclosed
Subscribe to read more
Audit directions
a Aaaaaaaa Aaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaaaaaaaa Aaaa Aaaaaaa Aaaaaaaaaaaaa Aaaa Aaaaa Aaaaaaaaa Aaaaaaaa Aa Aaaaaaaaa Aaa Aaaaaaa Aaaaa Aaaaaaaaaa Aaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaa Aaaa Aaaa Aaaaaaaa Aaaaaaa Aaaaaaaaaaa Aaaaa Aaaaa Aaaaaa Aaaaaaaaaa Aaaaaa a Aaaaaaaaaaaa Aaa Aaaa Aaaaa Aaaaaaaaaa Aa Aaaaaaaaaaaa Aaaaa Aaa Aaaa Aaaa Aaaaaaaaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaa Aaaaaaaaa Aaaaa Aaaaaaaa Aa Aaaaaaaa Aaa Aaaaaa Aaaa Aaaaaaaaaa Aaa Aaaaaa Aaaaaaaaaa
a Aaaaaaaa Aaaaaaaaa Aaaaaaaa Aa Aaa Aaaaa Aaaaa Aaaaaaa Aa Aaaaaaa Aa Aaa Aaaa Aaaaaaaaa Aaaaa Aaa Aaaa Aa Aaaaaaa Aaa Aaa Aaaaa Aaa a Aaaaaaaa Aaaa Aaaaa Aaaaa Aaaaaa Aaaa Aaaa Aaaaaaa Aaaa Aaaaaaaa Aaa Aaaaaaaa Aaaaa Aaaaaaa a Aaaaaa Aaa Aaaaaaaaa Aaaa Aaaaaa Aa Aaaaa Aaaaaaa Aaaa Aa Aaaa Aaaa Aaaaaaaa Aaaaaa Aaa Aaaaaaaaaaaaaaa Aaaaaaaaaaa Aaaaaa Aaaa Aaaaaaaaaa Aaaaaaaaa Aaa Aaaaaaaaaa Aaaaaaa Aaa Aaaaaaaaaa Aaaaaa Aaaa Aaaaaaaaa
a Aaaaa Aaa Aaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaa Aa Aaaaaa Aaaa a Aaaaaaa Aaaaaa Aaaaa Aaaaa Aaa Aaaaaaaaaa Aaaaaaa Aa Aaa Aaaaaa Aa Aaa Aaaaaa Aaaaaa Aaa Aaaaaa Aaaaa Aa Aaaaaaaaaaa Aaaaaaa Aaaaaa Aaaa Aaaaaaaaaaa Aaaaaaaa Aaa Aaaaaaa Aaaaaaaaaa Aaaaa Aaaa Aa Aaaa Aaaaaaa Aaaaaaa Aaaaa Aaaa Aaaaaaaaaaaaa Aaaaaaa Aa Aaa Aaaaaaaaaa Aaaa Aa Aaa Aaa Aaaa Aaaaa Aaaaaa
Aaaaaaaaa Aaaa Aaaaaaa a Aaaaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaa Aa Aaaaaa Aaaa Aaa Aaaaaaaaaaaaa Aaaa Aaaaa a Aaaaa Aaaaaa Aaaaa Aaaaaaaaaa Aaaaaaaaa Aaa Aaaa Aaaaaa Aaaaaa Aaa Aaaaaaaaaa Aaaaaaaa Aaaaaa Aaaa a Aaa Aaaaaaaa Aaaa Aaaaaaaaaaaaa Aaaaaaaaaa Aaa Aaaa Aaa Aaaaaaaa Aaaaaaaaaaa Aaa Aaa Aaaaaa Aaaaaa Aaaaaaaaaaa Aaaaaa Aaa Aaaaaa Aaaaaaaa Aaa Aaaaaaaa Aaaaaaaaa Aa Aaa Aaaaa
🔒Multiple audit patterns identified for WebGPU validation caching and index-type handling, with concrete search targets
Subscribe to read more