@@ -510,17 +510,16 @@ namespace {
510510// Build a SparseTensor IPC message carrying a CSF index with caller-controlled
511511// buffer counts. This lets us exercise the reader's validation directly, since
512512// such inconsistent indices can only be produced by hand-crafted flatbuffers.
513- Result<std::shared_ptr<Message>> MakeCSFSparseTensorMessage (int num_dims,
514- int num_indptr_buffers,
515- int num_indices_buffers,
516- int axis_order_size) {
513+ Result<std::shared_ptr<Message>> MakeCSFSparseTensorMessage (
514+ const std::vector<int64_t >& shape, int num_indptr_buffers, int num_indices_buffers,
515+ int axis_order_size, int64_t non_zero_length = 0 ) {
517516 flatbuffers::FlatBufferBuilder fbb;
518517
519518 auto value_type = flatbuf::CreateInt (fbb, 64 , /* is_signed=*/ true );
520519
521520 std::vector<flatbuffers::Offset<flatbuf::TensorDim>> dims;
522- for (int i = 0 ; i < num_dims; ++i ) {
523- dims.push_back (flatbuf::CreateTensorDim (fbb, /* size= */ 4 , /* name=*/ 0 ));
521+ for (int64_t dim_size : shape ) {
522+ dims.push_back (flatbuf::CreateTensorDim (fbb, dim_size , /* name=*/ 0 ));
524523 }
525524 auto fb_shape = fbb.CreateVector (dims);
526525
@@ -540,7 +539,7 @@ Result<std::shared_ptr<Message>> MakeCSFSparseTensorMessage(int num_dims,
540539
541540 flatbuf::Buffer data (0 , 0 );
542541 auto sparse_tensor = flatbuf::CreateSparseTensor (
543- fbb, flatbuf::Type_Int, value_type.Union (), fb_shape, /* non_zero_length= */ 0 ,
542+ fbb, flatbuf::Type_Int, value_type.Union (), fb_shape, non_zero_length,
544543 flatbuf::SparseTensorIndex::SparseTensorIndex_SparseTensorIndexCSF, csf.Union (),
545544 &data);
546545
@@ -555,33 +554,85 @@ Result<std::shared_ptr<Message>> MakeCSFSparseTensorMessage(int num_dims,
555554 return std::shared_ptr<Message>(std::move (message));
556555}
557556
557+ IpcPayload MakeSparseTensorPayload (const std::shared_ptr<Message>& message,
558+ int num_body_buffers) {
559+ IpcPayload payload;
560+ payload.metadata = message->metadata ();
561+ payload.body_buffers .assign (num_body_buffers, Buffer::FromString (" " ));
562+ return payload;
563+ }
564+
558565} // namespace
559566
560567TEST (TestSparseCSFIndex, RejectInconsistentBufferCounts) {
561568 // ndim == 1 is not a valid CSF index (it has no indptr buffers), and used to
562569 // reach SparseCSFIndex's constructor with an empty indptr vector.
563- ASSERT_OK_AND_ASSIGN (
564- auto message, MakeCSFSparseTensorMessage (/* num_dims =*/ 1 , /* num_indptr_buffers=*/ 0 ,
565- /* num_indices_buffers=*/ 1 ,
566- /* axis_order_size=*/ 1 ));
570+ ASSERT_OK_AND_ASSIGN (auto message,
571+ MakeCSFSparseTensorMessage (/* shape =*/ { 4 } , /* num_indptr_buffers=*/ 0 ,
572+ /* num_indices_buffers=*/ 1 ,
573+ /* axis_order_size=*/ 1 ));
567574 ASSERT_RAISES (Invalid, ReadSparseTensor (*message));
568575
569576 // Too many indices buffers for the declared number of dimensions, which used
570577 // to write past the end of the fixed-size index vectors.
571578 ASSERT_OK_AND_ASSIGN (
572- message, MakeCSFSparseTensorMessage (/* num_dims =*/ 2 , /* num_indptr_buffers=*/ 1 ,
579+ message, MakeCSFSparseTensorMessage (/* shape =*/ { 4 , 4 } , /* num_indptr_buffers=*/ 1 ,
573580 /* num_indices_buffers=*/ 3 ,
574581 /* axis_order_size=*/ 2 ));
575582 ASSERT_RAISES (Invalid, ReadSparseTensor (*message));
576583
577584 // axisOrder and indicesBuffers lengths disagree (out-of-bounds read).
578585 ASSERT_OK_AND_ASSIGN (
579- message, MakeCSFSparseTensorMessage (/* num_dims =*/ 2 , /* num_indptr_buffers=*/ 1 ,
586+ message, MakeCSFSparseTensorMessage (/* shape =*/ { 4 , 4 } , /* num_indptr_buffers=*/ 1 ,
580587 /* num_indices_buffers=*/ 2 ,
581588 /* axis_order_size=*/ 3 ));
582589 ASSERT_RAISES (Invalid, ReadSparseTensor (*message));
583590}
584591
592+ TEST (TestSparseCSFIndex, RejectInconsistentPayloadBufferCounts) {
593+ ASSERT_OK_AND_ASSIGN (auto message,
594+ MakeCSFSparseTensorMessage (/* shape=*/ {4 }, /* num_indptr_buffers=*/ 0 ,
595+ /* num_indices_buffers=*/ 1 ,
596+ /* axis_order_size=*/ 1 ));
597+ ASSERT_RAISES (Invalid,
598+ internal::ReadSparseTensorPayload (MakeSparseTensorPayload (message, 2 )));
599+
600+ ASSERT_OK_AND_ASSIGN (
601+ message, MakeCSFSparseTensorMessage (/* shape=*/ {4 , 4 }, /* num_indptr_buffers=*/ 1 ,
602+ /* num_indices_buffers=*/ 3 ,
603+ /* axis_order_size=*/ 3 ));
604+ ASSERT_RAISES (Invalid,
605+ internal::ReadSparseTensorPayload (MakeSparseTensorPayload (message, 4 )));
606+ }
607+
608+ TEST (TestSparseCSXIndex, RejectIndptrLengthOverflow) {
609+ auto empty = Buffer::FromString (" " );
610+ ASSERT_RAISES (Invalid,
611+ SparseCSRIndex::Make (int64 (), {std::numeric_limits<int64_t >::max (), 1 },
612+ /* non_zero_length=*/ 0 , empty, empty));
613+ ASSERT_RAISES (Invalid,
614+ SparseCSCIndex::Make (int64 (), {1 , std::numeric_limits<int64_t >::max ()},
615+ /* non_zero_length=*/ 0 , empty, empty));
616+ }
617+
618+ TEST (TestSparseTensor, RejectNegativeShapeAndNonZeroLength) {
619+ // A negative non_zero_length must be rejected by GetSparseTensorMetadata,
620+ // otherwise the negative size product bypasses the index buffer-size guards.
621+ ASSERT_OK_AND_ASSIGN (
622+ auto message, MakeCSFSparseTensorMessage (/* shape=*/ {4 , 4 }, /* num_indptr_buffers=*/ 1 ,
623+ /* num_indices_buffers=*/ 2 ,
624+ /* axis_order_size=*/ 2 ,
625+ /* non_zero_length=*/ -1 ));
626+ ASSERT_RAISES (Invalid, ReadSparseTensor (*message));
627+
628+ // A negative dimension size must likewise be rejected.
629+ ASSERT_OK_AND_ASSIGN (
630+ message, MakeCSFSparseTensorMessage (/* shape=*/ {-1 , 4 }, /* num_indptr_buffers=*/ 1 ,
631+ /* num_indices_buffers=*/ 2 ,
632+ /* axis_order_size=*/ 2 ));
633+ ASSERT_RAISES (Invalid, ReadSparseTensor (*message));
634+ }
635+
585636} // namespace test
586637} // namespace ipc
587638} // namespace arrow
0 commit comments