From ceb3afc75e5365bbd2673d4b92d385b7176c3539 Mon Sep 17 00:00:00 2001 From: mohammadmseet-hue Date: Sat, 4 Apr 2026 21:32:18 +0200 Subject: [PATCH] Fix signed integer overflow in attribute decoder byte position counters Several attribute decoder paths use `int` (32-bit signed) for byte position counters (`out_byte_pos`) and value ID counters (`val_id`, `quant_val_id`). When decoding meshes with large numbers of vertices/attributes, these counters can exceed INT32_MAX, causing signed integer overflow (undefined behavior in C++). The overflowed negative values are then passed to DataBuffer::Write() as byte offsets, leading to heap buffer underflow writes. Affected code paths: - SequentialIntegerAttributeDecoder::StoreTypedValues (out_byte_pos, val_id) - SequentialAttributeDecoder::DecodeValues (out_byte_pos) - AttributeQuantizationTransform::InverseTransformAttribute (out_byte_pos, quant_val_id) - KdTreeAttributesDecoder dequantization loop (out_byte_pos, quant_val_id) - MeshPredictionSchemeParallelogram vertex offset computation (v_*_off) Fix: widen all affected counters from `int`/`int32_t` to `int64_t` to prevent overflow for large meshes. --- src/draco/attributes/attribute_quantization_transform.cc | 4 ++-- .../compression/attributes/kd_tree_attributes_decoder.cc | 4 ++-- .../mesh_prediction_scheme_parallelogram_shared.h | 6 +++--- .../compression/attributes/sequential_attribute_decoder.cc | 2 +- .../attributes/sequential_integer_attribute_decoder.cc | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/draco/attributes/attribute_quantization_transform.cc b/src/draco/attributes/attribute_quantization_transform.cc index 667e34431..05feb4435 100644 --- a/src/draco/attributes/attribute_quantization_transform.cc +++ b/src/draco/attributes/attribute_quantization_transform.cc @@ -80,8 +80,8 @@ bool AttributeQuantizationTransform::InverseTransformAttribute( const int num_components = target_attribute->num_components(); const int entry_size = sizeof(float) * num_components; const std::unique_ptr att_val(new float[num_components]); - int quant_val_id = 0; - int out_byte_pos = 0; + int64_t quant_val_id = 0; + int64_t out_byte_pos = 0; Dequantizer dequantizer; if (!dequantizer.Init(range_, max_quantized_value)) { return false; diff --git a/src/draco/compression/attributes/kd_tree_attributes_decoder.cc b/src/draco/compression/attributes/kd_tree_attributes_decoder.cc index 51c41cf7a..fa34591dd 100644 --- a/src/draco/compression/attributes/kd_tree_attributes_decoder.cc +++ b/src/draco/compression/attributes/kd_tree_attributes_decoder.cc @@ -553,8 +553,8 @@ bool KdTreeAttributesDecoder::TransformAttributesToOriginalFormat() { const int num_components = att->num_components(); const int entry_size = sizeof(float) * num_components; const std::unique_ptr att_val(new float[num_components]); - int quant_val_id = 0; - int out_byte_pos = 0; + int64_t quant_val_id = 0; + int64_t out_byte_pos = 0; Dequantizer dequantizer; if (!dequantizer.Init(transform.range(), max_quantized_value)) { return false; diff --git a/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h b/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h index fd10fb524..ebf331b31 100644 --- a/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h +++ b/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h @@ -56,9 +56,9 @@ inline bool ComputeParallelogramPrediction( if (vert_opp < data_entry_id && vert_next < data_entry_id && vert_prev < data_entry_id) { // Apply the parallelogram prediction. - const int v_opp_off = vert_opp * num_components; - const int v_next_off = vert_next * num_components; - const int v_prev_off = vert_prev * num_components; + const int64_t v_opp_off = static_cast(vert_opp) * num_components; + const int64_t v_next_off = static_cast(vert_next) * num_components; + const int64_t v_prev_off = static_cast(vert_prev) * num_components; for (int c = 0; c < num_components; ++c) { const int64_t in_data_next_off = in_data[v_next_off + c]; const int64_t in_data_prev_off = in_data[v_prev_off + c]; diff --git a/src/draco/compression/attributes/sequential_attribute_decoder.cc b/src/draco/compression/attributes/sequential_attribute_decoder.cc index b4ba24f2d..8cd72b88e 100644 --- a/src/draco/compression/attributes/sequential_attribute_decoder.cc +++ b/src/draco/compression/attributes/sequential_attribute_decoder.cc @@ -103,7 +103,7 @@ bool SequentialAttributeDecoder::DecodeValues( const int entry_size = static_cast(attribute_->byte_stride()); std::unique_ptr value_data_ptr(new uint8_t[entry_size]); uint8_t *const value_data = value_data_ptr.get(); - int out_byte_pos = 0; + int64_t out_byte_pos = 0; // Decode raw attribute values in their original format. for (int i = 0; i < num_values; ++i) { if (!in_buffer->Decode(value_data, entry_size)) { diff --git a/src/draco/compression/attributes/sequential_integer_attribute_decoder.cc b/src/draco/compression/attributes/sequential_integer_attribute_decoder.cc index 927a4a52f..71bf1ca47 100644 --- a/src/draco/compression/attributes/sequential_integer_attribute_decoder.cc +++ b/src/draco/compression/attributes/sequential_integer_attribute_decoder.cc @@ -218,8 +218,8 @@ void SequentialIntegerAttributeDecoder::StoreTypedValues(uint32_t num_values) { const std::unique_ptr att_val( new AttributeTypeT[num_components]); const int32_t *const portable_attribute_data = GetPortableAttributeData(); - int val_id = 0; - int out_byte_pos = 0; + int64_t val_id = 0; + int64_t out_byte_pos = 0; for (uint32_t i = 0; i < num_values; ++i) { for (int c = 0; c < num_components; ++c) { const AttributeTypeT value =