Skip to content

Add missing upper-bound validation for glTF bufferView/buffer indices - #1247

Open
Subhan1794 wants to merge 7 commits into
google:mainfrom
Subhan1794:fix-gltf-bufferview-index-validation
Open

Subhan1794 wants to merge 7 commits into
google:mainfrom
Subhan1794:fix-gltf-bufferview-index-validation

Conversation

@Subhan1794

@Subhan1794 Subhan1794 commented Oct 9, 2026 •

Copy link
Copy Markdown

This PR adds missing upper-bound validation for attacker-controlled indices in the glTF decoder.

Part 1: bufferView/buffer indices
Five sites indexed model.bufferViews[] and model.buffers[] with attacker-controlled indices, checking only < 0. A malicious glTF with out-of-range indices causes heap OOB read via std::vector::operator[].

Fixed in:

  • TinyGltfUtils::CopyDataAsFloatImpl
  • CopyDataAsUint32, CopyDataAs (x2), CopyDataFromBufferView

Part 2: accessor indices
Five more sites indexed model.accessors[] with attacker-controlled indices without upper-bound checks:

  • GltfDecoder::DecodePrimitiveAttributeCount
  • GltfDecoder::DecodePrimitiveIndicesCount
  • GltfDecoder::DecodePrimitiveIndices (else branch)
  • GltfDecoder::DecodePrimitive (attribute loop)
  • GltfDecoder::DecodeSkin (inverseBindMatrices)

Some loops already had the check (e.g. line 862), proving the pattern was known but not applied consistently.

The September fix (241ac66) added byte-range validation to TinyGltfUtils
but missed index validation. Five sites indexed model.bufferViews[] and
model.buffers[] with attacker-controlled indices, checking only < 0.

A malicious glTF with out-of-range bufferView/buffer indices causes a
heap out-of-bounds read via std::vector::operator[].

This adds the missing >= size() checks to:
- TinyGltfUtils::CopyDataAsFloatImpl (tiny_gltf_utils.h)
- CopyDataAsUint32, CopyDataAs (x2), CopyDataFromBufferView (gltf_decoder.cc)
Five sites indexed model.accessors[] with attacker-controlled indices,
checking only < 0 (or nothing). A malicious glTF with out-of-range
accessor indices causes a heap out-of-bounds read via
std::vector::operator[].

This adds the missing >= size() checks to:
- GltfDecoder::DecodePrimitiveAttributeCount
- GltfDecoder::DecodePrimitiveIndicesCount
- GltfDecoder::DecodePrimitiveIndices (else branch)
- GltfDecoder::DecodePrimitive (attribute loop)
- GltfDecoder::DecodeSkin (inverseBindMatrices)

Follows the same pattern as the bufferView/buffer fix (ab77d8c).
9 additional sites with missing upper-bound checks for attacker-controlled
indices:
- node.mesh -> meshes[] (4 sites): only checked >= 0
- scene.nodes[i] -> nodes[] (2 sites): no validation
- node.children[i] -> nodes[] (1 site): no validation
- node_index params -> nodes[] (2 sites): no validation

Same bug class as bufferView/buffer/accessor fixes.
2 additional sites:
- texture_index -> textures[]: only checked < 0
- input_texture.sampler -> samplers[]: only checked >= 0

Same OOB bug class.
2 sites checked upper bound but not < 0, allowing negative index OOB.
4 sites where buffer byte offsets were used without validation,
allowing OOB read via memcpy/pointer arithmetic.
- Check for overflow in accessor.count * num_components
- Reject negative accessor.count before vector resize

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant