From f139540a286eb79fb0dce6a056a85776e8e705e9 Mon Sep 17 00:00:00 2001 From: Sharad Boni Date: Tue, 28 Apr 2026 11:55:58 -0700 Subject: [PATCH] Fix missing bounds checks on decoded face indices in sequential mesh decoder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mesh_sequential_decoder.cc:81 — face indices decoded from untrusted input were never validated against num_points, allowing an out-of-bounds index (e.g. 255 with num_points=2) to be used as an array index. Add `if (val >= num_points) return false;` after every raw index decode path. mesh_sequential_decoder.cc:170 — delta-decoded connectivity indices in the entropy-compressed path were also unbounded. Add a range check against num_points in DecodeAndDecompressIndices, which now accepts num_points as a second parameter. Update the declaration in mesh_sequential_decoder.h accordingly. --- .../mesh/mesh_sequential_decoder.cc | 21 +++++++++++++++++-- .../mesh/mesh_sequential_decoder.h | 3 ++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/draco/compression/mesh/mesh_sequential_decoder.cc b/src/draco/compression/mesh/mesh_sequential_decoder.cc index 124df3345..790e3ceb9 100644 --- a/src/draco/compression/mesh/mesh_sequential_decoder.cc +++ b/src/draco/compression/mesh/mesh_sequential_decoder.cc @@ -65,7 +65,7 @@ bool MeshSequentialDecoder::DecodeConnectivity() { return false; } if (connectivity_method == 0) { - if (!DecodeAndDecompressIndices(num_faces)) { + if (!DecodeAndDecompressIndices(num_faces, num_points)) { return false; } } else { @@ -78,6 +78,9 @@ bool MeshSequentialDecoder::DecodeConnectivity() { if (!buffer()->Decode(&val)) { return false; } + if (val >= num_points) { + return false; + } face[j] = val; } mesh()->AddFace(face); @@ -91,6 +94,9 @@ bool MeshSequentialDecoder::DecodeConnectivity() { if (!buffer()->Decode(&val)) { return false; } + if (val >= num_points) { + return false; + } face[j] = val; } mesh()->AddFace(face); @@ -105,6 +111,9 @@ bool MeshSequentialDecoder::DecodeConnectivity() { if (!DecodeVarint(&val, buffer())) { return false; } + if (val >= num_points) { + return false; + } face[j] = val; } mesh()->AddFace(face); @@ -118,6 +127,9 @@ bool MeshSequentialDecoder::DecodeConnectivity() { if (!buffer()->Decode(&val)) { return false; } + if (val >= num_points) { + return false; + } face[j] = val; } mesh()->AddFace(face); @@ -138,7 +150,8 @@ bool MeshSequentialDecoder::CreateAttributesDecoder(int32_t att_decoder_id) { new LinearSequencer(point_cloud()->num_points()))))); } -bool MeshSequentialDecoder::DecodeAndDecompressIndices(uint32_t num_faces) { +bool MeshSequentialDecoder::DecodeAndDecompressIndices(uint32_t num_faces, + uint32_t num_points) { // Get decoded indices differences that were encoded with an entropy code. std::vector indices_buffer(num_faces * 3); if (!DecodeSymbols(num_faces * 3, 1, buffer(), indices_buffer.data())) { @@ -167,6 +180,10 @@ bool MeshSequentialDecoder::DecodeAndDecompressIndices(uint32_t num_faces) { } } const int32_t index_value = index_diff + last_index_value; + if (index_value < 0 || + static_cast(index_value) >= num_points) { + return false; + } face[j] = index_value; last_index_value = index_value; } diff --git a/src/draco/compression/mesh/mesh_sequential_decoder.h b/src/draco/compression/mesh/mesh_sequential_decoder.h index 3a86c75d5..7c3acdc50 100644 --- a/src/draco/compression/mesh/mesh_sequential_decoder.h +++ b/src/draco/compression/mesh/mesh_sequential_decoder.h @@ -30,8 +30,9 @@ class MeshSequentialDecoder : public MeshDecoder { private: // Decodes face indices that were compressed with an entropy code. + // |num_points| is used to validate that every decoded index is in range. // Returns false on error. - bool DecodeAndDecompressIndices(uint32_t num_faces); + bool DecodeAndDecompressIndices(uint32_t num_faces, uint32_t num_points); }; } // namespace draco