diff --git a/cpp/src/arrow/adapters/orc/adapter_test.cc b/cpp/src/arrow/adapters/orc/adapter_test.cc index b671f26a8df5..69f913c6d5f5 100644 --- a/cpp/src/arrow/adapters/orc/adapter_test.cc +++ b/cpp/src/arrow/adapters/orc/adapter_test.cc @@ -658,6 +658,37 @@ TEST(TestAdapterReadWrite, ThrowWhenTZDBUnavaiable) { Raises(StatusCode::Invalid, testing::HasSubstr(expect_str))); } +// An out-of-range DECIMAL precision/scale from a malformed footer must return an error +// Status, not abort the process via decimal128()'s ARROW_CHECK. +TEST(TestAdapterRead, GetArrowTypeRejectsOutOfRangeDecimal) { + namespace orc_adapter = adapters::orc; + + EXPECT_THAT(orc_adapter::GetArrowType( + liborc::createDecimalType(Decimal128Type::kMaxPrecision + 1, 2).get()), + Raises(StatusCode::TypeError, testing::HasSubstr("decimal precision"))); + // Above INT_MAX: rejected on the raw uint64, not wrapped into range by static_cast. + EXPECT_THAT( + orc_adapter::GetArrowType(liborc::createDecimalType(uint64_t{1} << 40, 2).get()), + Raises(StatusCode::TypeError, testing::HasSubstr("decimal precision"))); + EXPECT_THAT(orc_adapter::GetArrowType(liborc::createDecimalType(10, 20).get()), + Raises(StatusCode::TypeError, testing::HasSubstr("decimal scale"))); + + // precision == 0 is the legacy HIVE 0.11/0.12 "max precision" sentinel. + ASSERT_OK_AND_ASSIGN(auto hive_sentinel, + orc_adapter::GetArrowType(liborc::createDecimalType(0, 0).get())); + AssertTypeEqual(*decimal128(38, 6), *hive_sentinel); + + ASSERT_OK_AND_ASSIGN(auto ok_mid, + orc_adapter::GetArrowType(liborc::createDecimalType(18, 4).get())); + AssertTypeEqual(*decimal128(18, 4), *ok_mid); + ASSERT_OK_AND_ASSIGN( + auto ok_max, + orc_adapter::GetArrowType(liborc::createDecimalType(Decimal128Type::kMaxPrecision, + Decimal128Type::kMaxPrecision) + .get())); + AssertTypeEqual(*decimal128(38, 38), *ok_max); +} + // Trivial class TestORCWriterTrivialNoWrite : public ::testing::Test {}; diff --git a/cpp/src/arrow/adapters/orc/util.cc b/cpp/src/arrow/adapters/orc/util.cc index c926998b0e17..85dd2ed0862e 100644 --- a/cpp/src/arrow/adapters/orc/util.cc +++ b/cpp/src/arrow/adapters/orc/util.cc @@ -1197,13 +1197,23 @@ Result> GetArrowType(const liborc::Type* type) { case liborc::DATE: return date32(); case liborc::DECIMAL: { - const int precision = static_cast(type->getPrecision()); - const int scale = static_cast(type->getScale()); + // decimal128() aborts via ARROW_CHECK when precision is out of [1, 38], so validate + // the untrusted footer values (as raw uint64, before the narrowing cast) first. + const uint64_t precision = type->getPrecision(); + const uint64_t scale = type->getScale(); if (precision == 0) { // In HIVE 0.11/0.12 precision is set as 0, but means max precision return decimal128(38, 6); } - return decimal128(precision, scale); + if (precision > static_cast(Decimal128Type::kMaxPrecision)) { + return Status::TypeError("Invalid ORC decimal precision ", precision, + ": must be in [1, ", Decimal128Type::kMaxPrecision, "]"); + } + if (scale > precision) { + return Status::TypeError("Invalid ORC decimal scale ", scale, + ": must be in [0, precision=", precision, "]"); + } + return decimal128(static_cast(precision), static_cast(scale)); } case liborc::LIST: { if (subtype_count != 1) {