This is an automated email from the ASF dual-hosted git repository.

rok pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/main by this push:
     new a3d630fc19a GH-50077: [C++][IPC] Avoid int64 overflow in 
ReadSparseCSXIndex (#50038)
a3d630fc19a is described below

commit a3d630fc19af967761b84b87bb3aa3f0c26f3235
Author: jmestwa-coder <[email protected]>
AuthorDate: Tue Jun 30 17:33:05 2026 +0530

    GH-50077: [C++][IPC] Avoid int64 overflow in ReadSparseCSXIndex (#50038)
    
    ### Rationale for this change
    
    `ReadSparseCSXIndex` validates the SparseTensor `indices`/`indptr` buffer 
sizes against the claimed shape using `int64` products (`non_zero_length * 
byte_width` and `(shape[axis] + 1) * byte_width`). Both inputs come unchecked 
from the flatbuffer via `GetSparseTensorMetadata`, so a value near `INT64_MAX` 
overflows the signed product (UBSan-confirmed), wraps to a small value, and the 
size guard passes. The index `Tensor` is then built over a buffer smaller than 
its shape, enabling an o [...]
    
    ### What changes are included in this PR?
    
    Compute the `indices`/`indptr` byte counts (and the `shape[axis] + 1` term) 
with `MultiplyWithOverflow`/`AddWithOverflow`, the same checked helpers already 
used for this size math in `tensor.cc`, and return `Status::Invalid` when the 
computation overflows. Also reject a negative `non_zero_length` up front, since 
the bare `Tensor` constructor used here skips the positive-shape check in 
`CheckTensorValidity`.
    
    ### Are these changes tested?
    
    Covered by the existing sparse tensor IPC round-trip tests; the change only 
adds overflow/negative-input guards on the existing validation path.
    
    ### Are there any user-facing changes?
    
    No.
    
    * GitHub Issue: #50077
    
    Authored-by: jmestwa-coder <[email protected]>
    Signed-off-by: Rok Mihevc <[email protected]>
---
 cpp/src/arrow/ipc/reader.cc | 34 ++++++++++++++++++++++++++--------
 1 file changed, 26 insertions(+), 8 deletions(-)

diff --git a/cpp/src/arrow/ipc/reader.cc b/cpp/src/arrow/ipc/reader.cc
index f8bbf6c13d3..b964c998dd9 100644
--- a/cpp/src/arrow/ipc/reader.cc
+++ b/cpp/src/arrow/ipc/reader.cc
@@ -79,6 +79,7 @@ namespace flatbuf = org::apache::arrow::flatbuf;
 using internal::AddWithOverflow;
 using internal::checked_cast;
 using internal::checked_pointer_cast;
+using internal::MultiplyWithOverflow;
 
 namespace ipc {
 
@@ -2331,6 +2332,9 @@ Result<std::shared_ptr<SparseIndex>> ReadSparseCSXIndex(
   if (shape.size() != 2) {
     return Status::Invalid("Invalid shape length for a sparse matrix");
   }
+  if (non_zero_length < 0) {
+    return Status::Invalid("Invalid non-zero length for a sparse matrix");
+  }
 
   auto* sparse_index = sparse_tensor->sparseIndex_as_SparseMatrixIndexCSX();
 
@@ -2350,16 +2354,24 @@ Result<std::shared_ptr<SparseIndex>> ReadSparseCSXIndex(
                                      /*allow_short_read=*/false));
 
   std::vector<int64_t> indices_shape({non_zero_length});
-  const auto indices_minimum_bytes = indices_shape[0] * 
indices_type->byte_width();
-  if (indices_minimum_bytes > indices_buffer->length()) {
+  const auto indices_minimum_bytes =
+      MultiplyWithOverflow<int64_t>({indices_shape[0], 
indices_type->byte_width()});
+  if (!indices_minimum_bytes.has_value() ||
+      indices_minimum_bytes.value() > indices_buffer->length()) {
     return Status::Invalid("shape is inconsistent to the size of indices 
buffer");
   }
 
   switch (sparse_index->compressedAxis()) {
     case flatbuf::SparseMatrixCompressedAxis::SparseMatrixCompressedAxis_Row: {
-      std::vector<int64_t> indptr_shape({shape[0] + 1});
-      const int64_t indptr_minimum_bytes = indptr_shape[0] * indptr_byte_width;
-      if (indptr_minimum_bytes > indptr_buffer->length()) {
+      const auto indptr_length = AddWithOverflow<int64_t>({shape[0], 1});
+      if (!indptr_length.has_value()) {
+        return Status::Invalid("shape is inconsistent to the size of indptr 
buffer");
+      }
+      std::vector<int64_t> indptr_shape({indptr_length.value()});
+      const auto indptr_minimum_bytes =
+          MultiplyWithOverflow<int64_t>({indptr_shape[0], indptr_byte_width});
+      if (!indptr_minimum_bytes.has_value() ||
+          indptr_minimum_bytes.value() > indptr_buffer->length()) {
         return Status::Invalid("shape is inconsistent to the size of indptr 
buffer");
       }
       return std::make_shared<SparseCSRIndex>(
@@ -2367,9 +2379,15 @@ Result<std::shared_ptr<SparseIndex>> ReadSparseCSXIndex(
           std::make_shared<Tensor>(indices_type, indices_data, indices_shape));
     }
     case 
flatbuf::SparseMatrixCompressedAxis::SparseMatrixCompressedAxis_Column: {
-      std::vector<int64_t> indptr_shape({shape[1] + 1});
-      const int64_t indptr_minimum_bytes = indptr_shape[0] * indptr_byte_width;
-      if (indptr_minimum_bytes > indptr_buffer->length()) {
+      const auto indptr_length = AddWithOverflow<int64_t>({shape[1], 1});
+      if (!indptr_length.has_value()) {
+        return Status::Invalid("shape is inconsistent to the size of indptr 
buffer");
+      }
+      std::vector<int64_t> indptr_shape({indptr_length.value()});
+      const auto indptr_minimum_bytes =
+          MultiplyWithOverflow<int64_t>({indptr_shape[0], indptr_byte_width});
+      if (!indptr_minimum_bytes.has_value() ||
+          indptr_minimum_bytes.value() > indptr_buffer->length()) {
         return Status::Invalid("shape is inconsistent to the size of indptr 
buffer");
       }
       return std::make_shared<SparseCSCIndex>(

Reply via email to