[5] ANGLE IndexRange integer truncation bypasses vertex bounds check
Severity: High | Component: ANGLE (WebGL) | 56c55dd
Rated High because the regression test confirms a maximal 0xFFFFFFFF index was accepted against an undersized buffer before the fix, producing an out-of-bounds vertex fetch in the GPU process; escalation past information disclosure is limited because no write primitive is evident and the truncating consumer arithmetic is not in this diff.
IndexRange marked up an index range with uint32_t start, uint32_t count, which cannot easily represent range [0, 0xFFFFFFFF]. Switch to start/end markup, with start > end marking an 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; }
+ // Range: [0, 0xFFFFFFFF] == 0x100000000 (needs size_t).
+ size_t vertexCount() const
+ {
+ // Note: unsigned underflow ok on isEmpty() == true.
+ 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 now rejected
Patch Details
gl::IndexRange is rewritten from a (mStart, mEnd, mCount) representation to a pure (mStart, mEnd) pair. The constructor no longer precomputes mCount = end - start + 1; isEmpty() becomes mStart > mEnd; default initializers become mStart{1}, mEnd{0} so a default range is empty. vertexCount() changes from uint64_t to a size_t computed on demand, with an explicit comment that [0, 0xFFFFFFFF] yields 0x100000000. The hand-written operator== is deleted in favour of a defaulted member-wise one. New unit tests exercise full-range values, and WebGLCompatibilityTest.cpp asserts that glDrawElements with 0xFFFFFFFF against a small buffer returns GL_INVALID_OPERATION.
Truncation of an inclusive full-width integer range's cardinality (which needs one extra bit) in downstream draw-bounds arithmetic, collapsing the maximal range to an apparently-empty/in-bounds value and defeating the vertex bounds check.
Background
WebGL drawElements issues an indexed draw: an element-array buffer holds integer indices that select vertices from the bound vertex attribute arrays. Before dispatching, ANGLE computes the minimum and maximum index actually referenced and checks the maximum is within the number of vertices the bound buffers supply; an out-of-range maximum must produce GL_INVALID_OPERATION. gl::IndexRange carries that inclusive [min, max] span; its cardinality for an inclusive range is max - min + 1, so the full [0, 0xFFFFFFFF] range has 0x100000000 vertices — a value needing 33 bits that does not fit in a 32-bit integer. ComputeTypedIndexRange scans the index buffer to produce the range; with primitive restart disabled, 0xFFFFFFFF is a real index. In WebKit, ANGLE runs inside the sandboxed GPU process.
Analysis
This is integer truncation/overflow leading to a bounds-check bypass and out-of-bounds read. The inclusive full-width range [0, 0xFFFFFFFF] has cardinality 0x100000000, requiring 33 bits. Notably, the IndexRange code removed by this diff did not itself wrap — the old mCount was a uint64_t that correctly held 0x100000000. The truncation-to-zero therefore occurs in consumer arithmetic that takes this cardinality (or reconstructs an upper bound) and stores it in a narrower type such as uint32_t/GLsizei.
Aaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaa Aaaaaaaaaaaaaa Aaaaaaaaaaaa Aaa a Aaaaaaaaaaaaaaaaa Aaaaaa Aaaaaaaaaa Aaaaaaaaaaaaa a Aaaaaaaaaa Aaaaaaaaa Aaaa Aaaaaaa Aaa Aaaaaaa Aaaaaaaaaa Aaaaa Aaaa Aaaaaa Aaa Aaaaaaaa Aaaaaaa Aaa Aaaaa Aaaaaaaaa Aaaaaaa Aa Aaaa Aaaaa Aa Aaaaaaaaa Aaa Aaa Aaaaaaa Aaaaaaaaaaa Aa Aaaaaa Aaaaaa Aaaaaaaaaaaaa Aaaaaaaaa Aa Aaaa Aaaaaa Aaa Aaaaaaa Aaaaa Aaaaaa Aaaaa Aa Aaaaaaaaa Aaaaaaaaaa Aa Aaa Aaaaa Aa Aaa Aaaaaaaa Aaa Aaa Aaaa Aaaaaaaaa Aaa Aaaaa Aaaaaaaaaa Aaaa Aaaaa Aa Aaa Aaaaa Aaaaaaaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaa Aaaaa Aa Aaa Aaaa Aa Aaaa Aaaaa Aa Aaaa Aaaaaaaa Aaaaaaaaaa Aa Aaaaaaaa Aaaa Aaa Aaaa Aaaa Aaa Aaaaaaaaaaaaaa Aaaaaaa Aaa Aaaaaaaaaa Aaaa Aaaaaaaa Aaa Aaaaaaaaaa Aaaaaaaaaa Aaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaaaaaaaaaaaaaaa Aaa Aaaa Aaaa Aaaaaaaaaaaa Aaaaaaa a Aaaa Aaaaaa Aaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaa
Aaaa Aa Aaaaaaaaaaa Aa a Aaaaaaaaaa Aaaaaaaaaaaaa Aaaa Aa Aaaaaa Aaaaaaaaa Aaaaaa Aa Aaa Aaa Aaaaaaaa Aaaaaaaaa Aaaa Aaa Aaaaaaa Aaaaaaa Aaaaa Aaaaaaaaaaaaaaa Aaaa Aaa Aaaaaaaaaaaaa Aaaaaaaa Aaaaaaaaaa Aa Aaaaaaaaaaaaaaa Aaaaaaaaaaaa Aaaaaaa Aa Aaaaa Aaaaaaaaa Aa Aaaaaaaa Aaa Aaaaaa Aaaaaaaaa Aaaaa Aaaa Aaaaaa Aaaa Aaaaa Aaaaaaa Aa Aaaaaa Aaaaaaaaaaa Aaa Aaaaaaaa Aaaaaa Aa Aaaaaaaaaa Aaaa Aaa Aaaaa Aaaaaaaaaaaa Aaaaaaaa Aaa Aa Aaaaaaaaaa Aaaaaaaaa Aaa Aaaaaaa Aa Aaaa Aaaaa
Aaaa Aaaaaaaaaaaaa Aaaaaaa Aaa Aaaaaaaaaaaaa Aaaaaa Aaaaaaaaaaa Aa Aaaaa Aaaaaaa Aaaaaa Aaa Aaaaa Aaaaaaa Aaaaa Aaaaaaa Aaa Aaaa Aaaaa Aaaaa Aaaaaa Aaaaaaaaaa a Aaaaaa Aaaaaaa Aaa Aaaaaaa Aaaaaaaaa Aaaaaaaa Aaaaaa Aaa Aaa Aaaa Aaaaaaaaa Aaa Aaaaaaaa Aaa Aaa Aaaaaaa Aaaaaa Aaaaaa Aaaaa Aaaaa Aaaa Aa Aaa Aaa Aaaaaaaa Aaa Aaa Aaaa Aa Aaaaaaaaaaa Aaaaaaaaaa Aaaaaa Aaa Aaaaaaaaaaa Aaaaaaaa Aaa Aaaaaaaaaa Aa Aa Aaaaaaa Aaaaaaaaaa Aaaaa Aaaaaaa a Aaaaaaaa Aaaaaaaaaaa Aaaaaaa
Aaaaaaaaa Aaa Aaaaaaa Aaaaaaaa Aaaaaaaaaa Aaaa Aaaaaaaaa Aaaaaaaaaaaaa Aa Aaa Aa Aaaaaaaa Aaaa Aaa Aaaaaaaaaaaaaa Aaaaaa Aaa Aaa Aaaaaaaaaa Aaaa Aaaaaa Aaaa Aaaaaaaa Aaaaaaaa Aaa Aaaaaaaaaa Aaaaaaaaaaaa Aaaaaa Aaa Aaa Aaa Aaa Aaaaaaaaa Aa Aaa Aaaaa
🔒Where the maximal-index range actually loses precision, and how far that propagates into the GPU draw pipeline, is traced in depth.
Subscribe to read more
Audit directions
a Aaaaaa Aaaaaa Aaaaaaaaa Aa a Aaaaaaaaaaaa Aaaaaaaaaaa a Aaaaaaaaaa Aaaaa Aaaaaaaa Aaaaaaaaa Aa a Aaaa Aaaa Aaaa Aaaaaaaaa Aaaa a Aaaaaaaa Aaaa Aa a Aaaaaa Aaaaaa Aaaaa Aaaaa Aaaaaaaaaaaaaaa Aaaaaaaaa Aa Aaaaaaaaaaaaaaaa Aaa Aaaaaa Aaaaaaaaa Aaaaaaaaaaaaaaa Aa Aaaa a Aaaaa a Aa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaa Aaaa Aaaaaaaaa Aaa Aaaaaaa Aaa Aaaaaaaaaaa Aaaaa Aaa Aaaa Aaaaaaa Aa a Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaaaa Aaaa a Aaaaaa Aaaaaaa
a Aaaaaaaaaaa Aaaaaaaaaaa Aaaaaa Aaaaaaaaaa Aa a Aaaaaaaaaa Aaaaaaaa Aaaa Aaaaa Aaa Aaaaaa Aaaaaaaaaaa Aaaaaaaaaaaa Aaaaaaa Aaaaaaaa Aaa Aa Aaa Aa Aaa Aaa Aaaaaa a Aaaaaa Aaaa Aaaaaaaaaaa Aaaaaaaaaaaa Aaaaaa Aaa Aaaaaaaaaa Aaaa a Aaaaaaaa Aaaaa Aaaaa Aaaaaaa Aaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaa Aaaaaaa Aaaaaaaaaa Aa Aaa Aa Aaa Aaaaaaaa
a Aaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaa Aaaaa Aaa Aaaaaaaaaaaaaa Aaaaaaaaa Aaaaaaaaaaaaaaaaa Aaaa Aa Aaaaaaa Aaaaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaa Aaaaaaaaa Aaaaaaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaa Aa Aaaaaaa Aaaa Aaaaaaaaaaaa a Aaaaaaa Aaaaa Aaaa a Aaaaaa Aaaaaa Aa Aaaaaaa Aaa Aaaaaaaaaaa Aaaaaa Aaa Aaaaaa Aaaaaaaaaaa
🔒Multiple reusable audit patterns identified for integer-cardinality truncation at bounds-check boundaries, with concrete ANGLE starting points for variant discovery.
Subscribe to read more