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

apitrou 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 b1bb4808d2 GH-45180: [C++][Fuzzing] Fix Negation bug discovered by 
fuzzing (#45181)
b1bb4808d2 is described below

commit b1bb4808d2a9e7d136c8a10bf544d67f3b906fb6
Author: Matt Topol <[email protected]>
AuthorDate: Mon Jan 13 04:59:51 2025 -0500

    GH-45180: [C++][Fuzzing] Fix Negation bug discovered by fuzzing (#45181)
    
    
    
    ### Rationale for this change
    If the value for Decimal32 or Decimal64 is `INT32_MIN` or `INT64_MIN` 
respectively, then UBSAN reports an issue when calling Negate on them due to 
overflow.
    
    ### What changes are included in this PR?
    Have the `Negate` methods of Decimal32 and Decimal64 use 
`arrow::internal::SafeSignedNegate`.
    
    ### Are these changes tested?
    Unit tests were added for both cases which were able to reproduce the 
problem when UBSAN was on without the fix.
    
    ### Are there any user-facing changes?
    No.
    
    * OSS-Fuzz issue: https://issues.oss-fuzz.com/issues/371239168
    
    * GitHub Issue: #45180
    
    Authored-by: Matt Topol <[email protected]>
    Signed-off-by: Antoine Pitrou <[email protected]>
---
 cpp/src/arrow/util/basic_decimal.cc | 16 ++++++++++++++++
 cpp/src/arrow/util/basic_decimal.h  | 10 ++--------
 cpp/src/arrow/util/decimal_test.cc  | 22 ++++++++++++++++++++++
 testing                             |  2 +-
 4 files changed, 41 insertions(+), 9 deletions(-)

diff --git a/cpp/src/arrow/util/basic_decimal.cc 
b/cpp/src/arrow/util/basic_decimal.cc
index 22db1e7051..e82078055f 100644
--- a/cpp/src/arrow/util/basic_decimal.cc
+++ b/cpp/src/arrow/util/basic_decimal.cc
@@ -50,6 +50,11 @@ static constexpr uint64_t kInt64Mask = 0xFFFFFFFFFFFFFFFF;
 static constexpr uint64_t kInt32Mask = 0xFFFFFFFF;
 #endif
 
+BasicDecimal32& BasicDecimal32::Negate() {
+  value_ = arrow::internal::SafeSignedNegate(value_);
+  return *this;
+}
+
 DecimalStatus BasicDecimal32::Divide(const BasicDecimal32& divisor,
                                      BasicDecimal32* result,
                                      BasicDecimal32* remainder) const {
@@ -152,6 +157,11 @@ BasicDecimal32::operator BasicDecimal64() const {
   return BasicDecimal64(static_cast<int64_t>(value()));
 }
 
+BasicDecimal64& BasicDecimal64::Negate() {
+  value_ = arrow::internal::SafeSignedNegate(value_);
+  return *this;
+}
+
 DecimalStatus BasicDecimal64::Divide(const BasicDecimal64& divisor,
                                      BasicDecimal64* result,
                                      BasicDecimal64* remainder) const {
@@ -253,12 +263,18 @@ const BasicDecimal64& 
BasicDecimal64::GetHalfScaleMultiplier(int32_t scale) {
 bool BasicDecimal32::FitsInPrecision(int32_t precision) const {
   DCHECK_GE(precision, 0);
   DCHECK_LE(precision, kMaxPrecision);
+  if (value_ == INT32_MIN) {
+    return false;
+  }
   return Abs(*this) < 
DecimalTraits<BasicDecimal32>::powers_of_ten()[precision];
 }
 
 bool BasicDecimal64::FitsInPrecision(int32_t precision) const {
   DCHECK_GE(precision, 0);
   DCHECK_LE(precision, kMaxPrecision);
+  if (value_ == INT64_MIN) {
+    return false;
+  }
   return Abs(*this) < 
DecimalTraits<BasicDecimal64>::powers_of_ten()[precision];
 }
 
diff --git a/cpp/src/arrow/util/basic_decimal.h 
b/cpp/src/arrow/util/basic_decimal.h
index b5404bb7bc..638c4870f1 100644
--- a/cpp/src/arrow/util/basic_decimal.h
+++ b/cpp/src/arrow/util/basic_decimal.h
@@ -276,10 +276,7 @@ class ARROW_EXPORT BasicDecimal32 : public 
SmallBasicDecimal<int32_t> {
   using ValueType = int32_t;
 
   /// \brief Negate the current value (in-place)
-  BasicDecimal32& Negate() {
-    value_ = -value_;
-    return *this;
-  }
+  BasicDecimal32& Negate();
 
   /// \brief Absolute value (in-place)
   BasicDecimal32& Abs() { return *this < 0 ? Negate() : *this; }
@@ -429,10 +426,7 @@ class ARROW_EXPORT BasicDecimal64 : public 
SmallBasicDecimal<int64_t> {
   using ValueType = int64_t;
 
   /// \brief Negate the current value (in-place)
-  BasicDecimal64& Negate() {
-    value_ = -value_;
-    return *this;
-  }
+  BasicDecimal64& Negate();
 
   /// \brief Absolute value (in-place)
   BasicDecimal64& Abs() { return *this < 0 ? Negate() : *this; }
diff --git a/cpp/src/arrow/util/decimal_test.cc 
b/cpp/src/arrow/util/decimal_test.cc
index d2f8ae3b7a..e0aa0b2b85 100644
--- a/cpp/src/arrow/util/decimal_test.cc
+++ b/cpp/src/arrow/util/decimal_test.cc
@@ -219,6 +219,28 @@ class DecimalFromStringTest : public ::testing::Test {
   }
 };
 
+TEST(Decimal32Test, TestIntMinNegate) {
+  Decimal32 d(INT32_MIN);
+  auto neg = d.Negate();
+  ASSERT_EQ(neg, Decimal32(arrow::internal::SafeSignedNegate(INT32_MIN)));
+}
+
+TEST(Decimal32Test, TestIntMinFitsPrecision) {
+  Decimal32 d(INT32_MIN);
+  ASSERT_FALSE(d.FitsInPrecision(9));
+}
+
+TEST(Decimal64Test, TestIntMinNegate) {
+  Decimal64 d(INT64_MIN);
+  auto neg = d.Negate();
+  ASSERT_EQ(neg, Decimal64(arrow::internal::SafeSignedNegate(INT64_MIN)));
+}
+
+TEST(Decimal64Test, TestIntMinFitsPrecision) {
+  Decimal64 d(INT64_MIN);
+  ASSERT_FALSE(d.FitsInPrecision(18));
+}
+
 TYPED_TEST_SUITE(DecimalFromStringTest, DecimalTypes);
 
 TYPED_TEST(DecimalFromStringTest, Basics) { this->TestBasics(); }
diff --git a/testing b/testing
index 4d209492d5..d2a1371230 160000
--- a/testing
+++ b/testing
@@ -1 +1 @@
-Subproject commit 4d209492d514c2d3cb2d392681b9aa00e6d8da1c
+Subproject commit d2a13712303498963395318a4eb42872e66aead7

Reply via email to