From 9b726bdb9d7b561f209a07d94aac043c9fb21ff8 Mon Sep 17 00:00:00 2001 From: Lucas Wang Date: Mon, 8 Jun 2026 23:34:54 +0800 Subject: [PATCH] Fix heap OOB read in PLY reader from unchecked list entry count PlyReader::ParseElementData uses a file-supplied list entry count to compute a copy size without checking the buffer has that many bytes. A truncated binary PLY with a large list count causes a heap OOB read. - Check Decode() return value for the list count - Reject negative entry counts - Validate num_bytes_to_read against remaining_size() - Add remaining_size() check for non-list property reads --- src/draco/io/ply_reader.cc | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/draco/io/ply_reader.cc b/src/draco/io/ply_reader.cc index 0da9ab5fe..eda6067b4 100644 --- a/src/draco/io/ply_reader.cc +++ b/src/draco/io/ply_reader.cc @@ -194,20 +194,32 @@ bool PlyReader::ParseElementData(DecoderBuffer *buffer, int element_index) { if (prop.is_list()) { // Parse the number of entries for the list element. int64_t num_entries = 0; - buffer->Decode(&num_entries, prop.list_data_type_num_bytes()); + if (!buffer->Decode(&num_entries, prop.list_data_type_num_bytes())) { + return false; + } + if (num_entries < 0) { + return false; + } + const int64_t entry_size = prop.data_type_num_bytes(); + if (entry_size <= 0 || + num_entries > buffer->remaining_size() / entry_size) { + return false; + } + const int64_t num_bytes_to_read = entry_size * num_entries; // Store offset to the main data entry. prop.list_data_.push_back(prop.data_.size() / prop.data_type_num_bytes_); // Store the number of entries. prop.list_data_.push_back(num_entries); - // Read and store the actual property data - const int64_t num_bytes_to_read = - prop.data_type_num_bytes() * num_entries; + // Read and store the actual property data. prop.data_.insert(prop.data_.end(), buffer->data_head(), buffer->data_head() + num_bytes_to_read); buffer->Advance(num_bytes_to_read); } else { - // Non-list property + // Non-list property. + if (buffer->remaining_size() < prop.data_type_num_bytes()) { + return false; + } prop.data_.insert(prop.data_.end(), buffer->data_head(), buffer->data_head() + prop.data_type_num_bytes()); buffer->Advance(prop.data_type_num_bytes());