Skip to content

fix(orc): reject out-of-range DECIMAL precision/scale instead of aborting (FB-4175) - #48

Merged
lorenzhs merged 1 commit into
release-24.0.0from
moshap/fb-4175-orc-decimal-precision-scale
Sep 16, 2026
Merged

lorenzhs merged 1 commit into
release-24.0.0from
moshap/fb-4175-orc-decimal-precision-scale

Conversation

@moshap-firebolt

@moshap-firebolt moshap-firebolt commented Sep 16, 2026

Copy link
Copy Markdown

Problem

arrow::adapters::orc::GetArrowType()'s DECIMAL case reads a type's precision and scale verbatim from the (untrusted) ORC footer and forwards them to decimal128():

case liborc::DECIMAL: {
  const int precision = static_cast<int>(type->getPrecision());
  const int scale = static_cast<int>(type->getScale());
  if (precision == 0) return decimal128(38, 6);  // legacy HIVE sentinel
  return decimal128(precision, scale);           // no bound check
}

decimal128() constructs a Decimal128Type, whose constructor enforces 1 <= precision <= 38 via ARROW_CHECK_OK(ValidateDecimalPrecision<Decimal128Type>(precision)) — which logs FATAL and calls std::abort() rather than returning a Status. A malformed footer declaring e.g. precision 48 therefore crashes the process in any caller of ORCFileReader::ReadSchema() on otherwise well-formed input — a denial of service on untrusted ORC. Scale is not validated at all.

Found by a downstream (Firebolt) ORC schema fuzzer; reproduces against vanilla arrow on a 251-byte file.

Fix

Validate precision and scale in the DECIMAL case and return Status::TypeError on violation, keeping the precision == 0 legacy HIVE sentinel. The raw uint64 footer values are compared before the narrowing static_cast<int>, so a precision above INT_MAX cannot wrap into the valid range. GetArrowType() already returns Result<>, so the error propagates to ReadSchema() instead of aborting.

Test

adapter_test.cc gains GetArrowTypeRejectsOutOfRangeDecimal, driving GetArrowType() over createDecimalType() for: precision > 38, precision > INT_MAX, scale > precision, the precision == 0 sentinel, and valid decimals including scale == precision.


Note

Medium Risk
Changes untrusted-input handling on ORC schema read; behavior shifts from process abort to TypeError for bad decimals, with low risk to valid files.

Overview
Fixes a denial-of-service when reading untrusted ORC files: malformed DECIMAL metadata in the footer no longer crashes the process.

GetArrowType()’s DECIMAL branch used to narrow footer precision/scale to int and call decimal128() directly, which aborts on invalid precision via ARROW_CHECK. The change validates raw uint64 precision and scale first—rejecting precision above 38 (including values above INT_MAX that could wrap after cast), rejecting scale greater than precision, and preserving the legacy Hive precision == 0decimal128(38, 6) mapping—then returns Status::TypeError so schema read fails cleanly.

Adds GetArrowTypeRejectsOutOfRangeDecimal in adapter_test.cc for invalid and valid decimal type conversions.

Reviewed by Cursor Bugbot for commit f4c2f2b. Bugbot is set up for automated code reviews on this repo. Configure here.

…ting (FB-4175)

The ORC adapter's GetArrowType() reads a DECIMAL type's precision and scale
verbatim from the (untrusted) ORC footer and forwards them to decimal128().
decimal128() constructs a Decimal128Type, whose constructor enforces
1 <= precision <= 38 with ARROW_CHECK_OK(ValidateDecimalPrecision<...>) --
which logs FATAL and aborts the process rather than returning a Status. A
malformed footer declaring e.g. precision 48 therefore crashes any caller of
ORCFileReader::ReadSchema() on otherwise well-formed input, a denial of service
on untrusted ORC. Scale is not validated at all.

Validate precision and scale in the DECIMAL case and return Status::TypeError
on violation, keeping the precision == 0 legacy HIVE sentinel. The raw uint64
footer values are compared before the narrowing static_cast<int>, so a precision
above INT_MAX cannot wrap into the valid range. GetArrowType() already returns
Result<>, so the error propagates to ReadSchema() instead of aborting.

Add a self-contained adapter_test.cc unit test driving GetArrowType() over
createDecimalType() for precision > 38, precision > INT_MAX, scale > precision,
the HIVE precision == 0 sentinel, and valid decimals including scale == precision.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@moshap-firebolt
moshap-firebolt force-pushed the moshap/fb-4175-orc-decimal-precision-scale branch from dcc0755 to f4c2f2b Compare September 16, 2026 15:33

@lorenzhs lorenzhs left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice find!

@lorenzhs

Copy link
Copy Markdown
Collaborator

CI is broken due to minio mirror retirement, should fix but not caused by this PR

@lorenzhs
lorenzhs merged commit 50810d1 into release-24.0.0 Sep 16, 2026
8 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants