Skip to content

Commit 816b785

Browse files
committed
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.
1 parent 422a1c4 commit 816b785

File tree

1 file changed

+108
-5
lines changed

1 file changed

+108
-5
lines changed

libs/gltfio/src/ResourceLoader.cpp

Lines changed: 108 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "Utility.h"
2525
#include "extended/ResourceLoaderExtended.h"
2626

27+
#include <limits>
2728
#include <filament/BufferObject.h>
2829
#include <filament/Engine.h>
2930
#include <filament/IndexBuffer.h>
@@ -163,9 +164,40 @@ inline void normalizeSkinningWeights(cgltf_data const* gltf) {
163164
LOG(WARNING) << "Cannot normalize weights, unsupported attribute type.";
164165
return;
165166
}
167+
if (!data->buffer_view || !data->buffer_view->buffer ||
168+
!data->buffer_view->buffer->data) {
169+
LOG(WARNING) << "Cannot normalize weights, missing buffer data.";
170+
return;
171+
}
172+
const cgltf_size bufferSize = data->buffer_view->buffer->size;
173+
const cgltf_size viewOffset = data->buffer_view->offset;
174+
const cgltf_size accessorOffset = data->offset;
175+
const cgltf_size totalOffset = viewOffset + accessorOffset;
176+
177+
if (totalOffset >= bufferSize) {
178+
LOG(WARNING) << "Cannot normalize weights, accessor offset exceeds buffer size.";
179+
return;
180+
}
181+
182+
const cgltf_size availableBytes = bufferSize - totalOffset;
183+
const cgltf_size stride = data->stride;
184+
185+
cgltf_size maxCount = 0;
186+
if (stride > 0 && availableBytes >= sizeof(float4)) {
187+
maxCount = 1 + (availableBytes - sizeof(float4)) / stride;
188+
}
189+
190+
cgltf_size safeCount = data->count;
191+
if (safeCount > maxCount) {
192+
LOG(WARNING) << "Skinning weights accessor count (" << safeCount
193+
<< ") exceeds buffer capacity (" << maxCount
194+
<< "), clamping to prevent out-of-bounds access.";
195+
safeCount = maxCount;
196+
}
197+
166198
uint8_t* bytes = (uint8_t*) data->buffer_view->buffer->data;
167-
bytes += data->offset + data->buffer_view->offset;
168-
for (cgltf_size i = 0, n = data->count; i < n; ++i, bytes += data->stride) {
199+
bytes += totalOffset;
200+
for (cgltf_size i = 0, n = safeCount; i < n; ++i, bytes += stride) {
169201
float4* weights = (float4*) bytes;
170202
const float sum = weights->x + weights->y + weights->z + weights->w;
171203
*weights /= sum;
@@ -243,8 +275,17 @@ inline void uploadBuffers(FFilamentAsset* asset, Engine& engine,
243275

244276
// For morph targets without buffer_view, use cgltf_accessor_unpack_floats to unpack data directly
245277
if (!accessor->buffer_view && isMorphTarget) {
246-
const size_t floatsCount = accessor->count * cgltf_num_components(accessor->type);
278+
const size_t components = cgltf_num_components(accessor->type);
279+
if (components > 0 && accessor->count > std::numeric_limits<size_t>::max() / components) {
280+
continue;
281+
}
282+
const size_t floatsCount = accessor->count * components;
283+
284+
if (floatsCount > std::numeric_limits<size_t>::max() / sizeof(float)) {
285+
continue;
286+
}
247287
const size_t floatsByteCount = sizeof(float) * floatsCount;
288+
248289
float* floatsData = (float*)malloc(floatsByteCount);
249290
cgltf_accessor_unpack_floats(accessor, floatsData, floatsCount);
250291

@@ -279,8 +320,39 @@ inline void uploadBuffers(FFilamentAsset* asset, Engine& engine,
279320
}
280321
if (slot.vertexBuffer) {
281322
if (utility::requiresConversion(accessor)) {
282-
const size_t floatsCount = accessor->count * cgltf_num_components(accessor->type);
323+
const cgltf_size bufferSize = accessor->buffer_view->buffer->size;
324+
const cgltf_size totalOffset = accessor->buffer_view->offset + accessor->offset;
325+
326+
if (totalOffset >= bufferSize) {
327+
continue;
328+
}
329+
330+
const cgltf_size availableBytes = bufferSize - totalOffset;
331+
const cgltf_size stride = accessor->stride;
332+
const cgltf_size elementSize = cgltf_calc_size(accessor->type, accessor->component_type);
333+
334+
cgltf_size maxCount = 0;
335+
if (stride > 0 && availableBytes >= elementSize) {
336+
maxCount = 1 + (availableBytes - elementSize) / stride;
337+
}
338+
339+
cgltf_size safeCount = accessor->count;
340+
if (safeCount > maxCount) {
341+
LOG(WARNING) << "Accessor count exceeds buffer capacity, clamping.";
342+
safeCount = maxCount;
343+
}
344+
345+
const size_t components = cgltf_num_components(accessor->type);
346+
if (components > 0 && safeCount > std::numeric_limits<size_t>::max() / components) {
347+
continue;
348+
}
349+
const size_t floatsCount = safeCount * components;
350+
351+
if (floatsCount > std::numeric_limits<size_t>::max() / sizeof(float)) {
352+
continue;
353+
}
283354
const size_t floatsByteCount = sizeof(float) * floatsCount;
355+
284356
float* floatsData = (float*) malloc(floatsByteCount);
285357
cgltf_accessor_unpack_floats(accessor, floatsData, floatsCount);
286358
BufferObject* bo = BufferObject::Builder().size(floatsByteCount).build(engine);
@@ -317,8 +389,39 @@ inline void uploadBuffers(FFilamentAsset* asset, Engine& engine,
317389
assert(slot.morphTargetBuffer);
318390

319391
if (utility::requiresPacking(accessor)) {
320-
const size_t floatsCount = accessor->count * cgltf_num_components(accessor->type);
392+
const cgltf_size bufferSize = accessor->buffer_view->buffer->size;
393+
const cgltf_size totalOffset = accessor->buffer_view->offset + accessor->offset;
394+
395+
if (totalOffset >= bufferSize) {
396+
continue;
397+
}
398+
399+
const cgltf_size availableBytes = bufferSize - totalOffset;
400+
const cgltf_size stride = accessor->stride;
401+
const cgltf_size elementSize = cgltf_calc_size(accessor->type, accessor->component_type);
402+
403+
cgltf_size maxCount = 0;
404+
if (stride > 0 && availableBytes >= elementSize) {
405+
maxCount = 1 + (availableBytes - elementSize) / stride;
406+
}
407+
408+
cgltf_size safeCount = accessor->count;
409+
if (safeCount > maxCount) {
410+
LOG(WARNING) << "Accessor count exceeds buffer capacity, clamping.";
411+
safeCount = maxCount;
412+
}
413+
414+
const size_t components = cgltf_num_components(accessor->type);
415+
if (components > 0 && safeCount > std::numeric_limits<size_t>::max() / components) {
416+
continue;
417+
}
418+
const size_t floatsCount = safeCount * components;
419+
420+
if (floatsCount > std::numeric_limits<size_t>::max() / sizeof(float)) {
421+
continue;
422+
}
321423
const size_t floatsByteCount = sizeof(float) * floatsCount;
424+
322425
float* floatsData = (float*) malloc(floatsByteCount);
323426
cgltf_accessor_unpack_floats(accessor, floatsData, floatsCount);
324427
if (accessor->type == cgltf_type_vec3) {

0 commit comments

Comments
 (0)