gltfio: Fix vulnerabilities in ResourceLoader.cpp

- Implement PR #9878 to add bounds checking in normalizeSkinningWeights,
  preventing heap OOB write by clamping the count to available buffer size.
- Fix 3 similar vulnerabilities in uploadBuffers where accessor->count was
  trusted for malloc allocation before cgltf_accessor_unpack_floats.
- Add overflow checks for allocation sizes in uploadBuffers.
- Clamp accessor count based on available buffer bytes in uploadBuffers to
  prevent OOB read.
This commit is contained in:
Mathias Agopian
2026-04-13 20:44:45 -07:00
committed by Mathias Agopian
parent a943b57c86
commit 8d16ad8882

View File

@@ -24,6 +24,7 @@
#include "Utility.h"
#include "extended/ResourceLoaderExtended.h"
#include <limits>
#include <filament/BufferObject.h>
#include <filament/Engine.h>
#include <filament/IndexBuffer.h>
@@ -163,9 +164,40 @@ inline void normalizeSkinningWeights(cgltf_data const* gltf) {
LOG(WARNING) << "Cannot normalize weights, unsupported attribute type.";
return;
}
if (!data->buffer_view || !data->buffer_view->buffer ||
!data->buffer_view->buffer->data) {
LOG(WARNING) << "Cannot normalize weights, missing buffer data.";
return;
}
const cgltf_size bufferSize = data->buffer_view->buffer->size;
const cgltf_size viewOffset = data->buffer_view->offset;
const cgltf_size accessorOffset = data->offset;
const cgltf_size totalOffset = viewOffset + accessorOffset;
if (totalOffset >= bufferSize) {
LOG(WARNING) << "Cannot normalize weights, accessor offset exceeds buffer size.";
return;
}
const cgltf_size availableBytes = bufferSize - totalOffset;
const cgltf_size stride = data->stride;
cgltf_size maxCount = 0;
if (stride > 0 && availableBytes >= sizeof(float4)) {
maxCount = 1 + (availableBytes - sizeof(float4)) / stride;
}
cgltf_size safeCount = data->count;
if (safeCount > maxCount) {
LOG(WARNING) << "Skinning weights accessor count (" << safeCount
<< ") exceeds buffer capacity (" << maxCount
<< "), clamping to prevent out-of-bounds access.";
safeCount = maxCount;
}
uint8_t* bytes = (uint8_t*) data->buffer_view->buffer->data;
bytes += data->offset + data->buffer_view->offset;
for (cgltf_size i = 0, n = data->count; i < n; ++i, bytes += data->stride) {
bytes += totalOffset;
for (cgltf_size i = 0, n = safeCount; i < n; ++i, bytes += stride) {
float4* weights = (float4*) bytes;
const float sum = weights->x + weights->y + weights->z + weights->w;
*weights /= sum;
@@ -243,8 +275,17 @@ inline void uploadBuffers(FFilamentAsset* asset, Engine& engine,
// For morph targets without buffer_view, use cgltf_accessor_unpack_floats to unpack data directly
if (!accessor->buffer_view && isMorphTarget) {
const size_t floatsCount = accessor->count * cgltf_num_components(accessor->type);
const size_t components = cgltf_num_components(accessor->type);
if (components > 0 && accessor->count > std::numeric_limits<size_t>::max() / components) {
continue;
}
const size_t floatsCount = accessor->count * components;
if (floatsCount > std::numeric_limits<size_t>::max() / sizeof(float)) {
continue;
}
const size_t floatsByteCount = sizeof(float) * floatsCount;
float* floatsData = (float*)malloc(floatsByteCount);
cgltf_accessor_unpack_floats(accessor, floatsData, floatsCount);
@@ -279,8 +320,39 @@ inline void uploadBuffers(FFilamentAsset* asset, Engine& engine,
}
if (slot.vertexBuffer) {
if (utility::requiresConversion(accessor)) {
const size_t floatsCount = accessor->count * cgltf_num_components(accessor->type);
const cgltf_size bufferSize = accessor->buffer_view->buffer->size;
const cgltf_size totalOffset = accessor->buffer_view->offset + accessor->offset;
if (totalOffset >= bufferSize) {
continue;
}
const cgltf_size availableBytes = bufferSize - totalOffset;
const cgltf_size stride = accessor->stride;
const cgltf_size elementSize = cgltf_calc_size(accessor->type, accessor->component_type);
cgltf_size maxCount = 0;
if (stride > 0 && availableBytes >= elementSize) {
maxCount = 1 + (availableBytes - elementSize) / stride;
}
cgltf_size safeCount = accessor->count;
if (safeCount > maxCount) {
LOG(WARNING) << "Accessor count exceeds buffer capacity, clamping.";
safeCount = maxCount;
}
const size_t components = cgltf_num_components(accessor->type);
if (components > 0 && safeCount > std::numeric_limits<size_t>::max() / components) {
continue;
}
const size_t floatsCount = safeCount * components;
if (floatsCount > std::numeric_limits<size_t>::max() / sizeof(float)) {
continue;
}
const size_t floatsByteCount = sizeof(float) * floatsCount;
float* floatsData = (float*) malloc(floatsByteCount);
cgltf_accessor_unpack_floats(accessor, floatsData, floatsCount);
BufferObject* bo = BufferObject::Builder().size(floatsByteCount).build(engine);
@@ -317,8 +389,39 @@ inline void uploadBuffers(FFilamentAsset* asset, Engine& engine,
assert(slot.morphTargetBuffer);
if (utility::requiresPacking(accessor)) {
const size_t floatsCount = accessor->count * cgltf_num_components(accessor->type);
const cgltf_size bufferSize = accessor->buffer_view->buffer->size;
const cgltf_size totalOffset = accessor->buffer_view->offset + accessor->offset;
if (totalOffset >= bufferSize) {
continue;
}
const cgltf_size availableBytes = bufferSize - totalOffset;
const cgltf_size stride = accessor->stride;
const cgltf_size elementSize = cgltf_calc_size(accessor->type, accessor->component_type);
cgltf_size maxCount = 0;
if (stride > 0 && availableBytes >= elementSize) {
maxCount = 1 + (availableBytes - elementSize) / stride;
}
cgltf_size safeCount = accessor->count;
if (safeCount > maxCount) {
LOG(WARNING) << "Accessor count exceeds buffer capacity, clamping.";
safeCount = maxCount;
}
const size_t components = cgltf_num_components(accessor->type);
if (components > 0 && safeCount > std::numeric_limits<size_t>::max() / components) {
continue;
}
const size_t floatsCount = safeCount * components;
if (floatsCount > std::numeric_limits<size_t>::max() / sizeof(float)) {
continue;
}
const size_t floatsByteCount = sizeof(float) * floatsCount;
float* floatsData = (float*) malloc(floatsByteCount);
cgltf_accessor_unpack_floats(accessor, floatsData, floatsCount);
if (accessor->type == cgltf_type_vec3) {