From 5f8ddeb879e3b1f0b7486296b3f48666b457296f Mon Sep 17 00:00:00 2001 From: Dricmoy Date: Wed, 16 Sep 2026 17:11:50 -0600 Subject: [PATCH 1/2] GH-51222: [C++] Avoid overflow in binary_slice output sizing --- cpp/src/arrow/compute/kernels/scalar_string_ascii.cc | 3 ++- cpp/src/arrow/compute/kernels/scalar_string_test.cc | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/cpp/src/arrow/compute/kernels/scalar_string_ascii.cc b/cpp/src/arrow/compute/kernels/scalar_string_ascii.cc index 8e7b626837fe..6b4d01c66ed9 100644 --- a/cpp/src/arrow/compute/kernels/scalar_string_ascii.cc +++ b/cpp/src/arrow/compute/kernels/scalar_string_ascii.cc @@ -2642,7 +2642,8 @@ struct SliceBytesTransform : StringSliceTransformBase { // on the resulting slice lengths, so return a worst case estimate. return input_bytes; } - int64_t max_slice_bytes = (opt.stop - opt.start + opt.step - 1) / opt.step; + const int64_t stop = std::clamp(opt.stop, -input_bytes, input_bytes); + int64_t max_slice_bytes = (stop - opt.start + opt.step - 1) / opt.step; return std::min(input_bytes, ninputs * std::max(0, max_slice_bytes)); } diff --git a/cpp/src/arrow/compute/kernels/scalar_string_test.cc b/cpp/src/arrow/compute/kernels/scalar_string_test.cc index 74650c1d76c5..66eef0965a7e 100644 --- a/cpp/src/arrow/compute/kernels/scalar_string_test.cc +++ b/cpp/src/arrow/compute/kernels/scalar_string_test.cc @@ -2702,6 +2702,13 @@ TYPED_TEST(TestBinaryKernels, SliceBytesBasic) { } TYPED_TEST(TestBinaryKernels, SliceBytesPosPos) { + SliceOptions options_default_stop{/*start=*/1}; + const std::string long_input(300, 'x'); + this->CheckUnary( + "binary_slice", this->template MakeArray({"abcdef", long_input}), + this->template MakeArray({"bcdef", std::string(299, 'x')}), + &options_default_stop); + SliceOptions options{2, 4}; this->CheckUnary( "binary_slice", From 749b54206bb8476b247fe7ea81e22f81c2952f4d Mon Sep 17 00:00:00 2001 From: Dricmoy Date: Wed, 14 Oct 2026 11:11:42 -0600 Subject: [PATCH 2/2] GH-51222: Address binary slice review feedback --- .../compute/kernels/scalar_string_ascii.cc | 2 ++ .../compute/kernels/scalar_string_test.cc | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/cpp/src/arrow/compute/kernels/scalar_string_ascii.cc b/cpp/src/arrow/compute/kernels/scalar_string_ascii.cc index 6b4d01c66ed9..a743cb171899 100644 --- a/cpp/src/arrow/compute/kernels/scalar_string_ascii.cc +++ b/cpp/src/arrow/compute/kernels/scalar_string_ascii.cc @@ -2642,6 +2642,8 @@ struct SliceBytesTransform : StringSliceTransformBase { // on the resulting slice lengths, so return a worst case estimate. return input_bytes; } + // SliceOptions uses INT64_MIN/MAX for open-ended bounds. Clamp stop before + // arithmetic so equivalent out-of-range indices cannot overflow the size estimate. const int64_t stop = std::clamp(opt.stop, -input_bytes, input_bytes); int64_t max_slice_bytes = (stop - opt.start + opt.step - 1) / opt.step; return std::min(input_bytes, ninputs * std::max(0, max_slice_bytes)); diff --git a/cpp/src/arrow/compute/kernels/scalar_string_test.cc b/cpp/src/arrow/compute/kernels/scalar_string_test.cc index 66eef0965a7e..733373c25d77 100644 --- a/cpp/src/arrow/compute/kernels/scalar_string_test.cc +++ b/cpp/src/arrow/compute/kernels/scalar_string_test.cc @@ -2702,12 +2702,18 @@ TYPED_TEST(TestBinaryKernels, SliceBytesBasic) { } TYPED_TEST(TestBinaryKernels, SliceBytesPosPos) { + constexpr auto max = std::numeric_limits::max(); SliceOptions options_default_stop{/*start=*/1}; const std::string long_input(300, 'x'); this->CheckUnary( "binary_slice", this->template MakeArray({"abcdef", long_input}), this->template MakeArray({"bcdef", std::string(299, 'x')}), &options_default_stop); + SliceOptions options_default_stop_step{/*start=*/1, /*stop=*/max, /*step=*/5}; + this->CheckUnary("binary_slice", + this->template MakeArray({"abcdef", long_input}), + this->template MakeArray({"b", std::string(60, 'x')}), + &options_default_stop_step); SliceOptions options{2, 4}; this->CheckUnary( @@ -2755,6 +2761,18 @@ TYPED_TEST(TestBinaryKernels, SliceBytesPosNeg) { } TYPED_TEST(TestBinaryKernels, SliceBytesNegNeg) { + constexpr auto min = std::numeric_limits::min(); + const std::string long_input(300, 'x'); + SliceOptions options_min{/*start=*/-1, /*stop=*/min}; + this->CheckUnary("binary_slice", + this->template MakeArray({"abcdef", long_input}), + this->template MakeArray({"", ""}), &options_min); + SliceOptions options_min_step_neg{/*start=*/-1, /*stop=*/min, /*step=*/-5}; + this->CheckUnary("binary_slice", + this->template MakeArray({"abcdef", long_input}), + this->template MakeArray({"fa", std::string(60, 'x')}), + &options_min_step_neg); + SliceOptions options{-2, -1}; this->CheckUnary( "binary_slice",