From 1b80bc6b33cd681e494b4f6a7d0cec2e3cdba266 Mon Sep 17 00:00:00 2001 From: Groene AI <270696204+groeneai@users.noreply.github.com> Date: Thu, 8 Oct 2026 15:07:52 +0000 Subject: [PATCH] Bound the stripe statistics index in startNextStripe and getStripeStatistics Same change as ClickHouse/orc#33 on ClickHouse/2.3.1 (21955610). A Metadata section with fewer entries than the footer has stripes (for example after a corrupt PostScript metadataLength) made RowReaderImpl::startNextStripe read stripe_stats(currentStripe_) out of bounds when a search argument was set. Entries are matched to stripes only by position, so startNextStripe now evaluates stripe statistics only when there is one entry per stripe; otherwise it reads the stripe without stripe-level filtering, as for a file with no Metadata. getStripeStatistics throws ParseError on a count mismatch and InvalidArgument on an out-of-range stripe index. On main it reads stripe_stats(stripeIndex) before the stripe footer, so both checks go right after the metadata check. Co-Authored-By: Claude Opus 5.5 --- c++/src/Reader.cc | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/c++/src/Reader.cc b/c++/src/Reader.cc index 441f738cce..2f4797abc2 100644 --- a/c++/src/Reader.cc +++ b/c++/src/Reader.cc @@ -780,6 +780,15 @@ namespace orc { if (contents_->metadata == nullptr) { throw std::logic_error("No stripe statistics in file"); } + if (contents_->metadata->stripe_stats_size() != footer_->stripes_size()) { + std::stringstream msg; + msg << "Malformed metadata: the file has " << footer_->stripes_size() << " stripes but " + << contents_->metadata->stripe_stats_size() << " stripe statistics"; + throw ParseError(msg.str()); + } + if (stripeIndex >= numberOfStripes_) { + throw InvalidArgument("stripe index out of range"); + } size_t num_cols = static_cast( contents_->metadata->stripe_stats(static_cast(stripeIndex)).col_stats_size()); std::vector> indexStats(num_cols); @@ -1076,8 +1085,9 @@ namespace orc { processingStripe_ = currentStripe_; bool isStripeNeeded = true; - // If PPD enabled and stripe stats existed, evaulate it first - if (sargsApplier_ && contents_->metadata) { + // If PPD enabled and the stripe stats match the stripes one to one, evaluate them first + if (sargsApplier_ && contents_->metadata && + contents_->metadata->stripe_stats_size() == footer_->stripes_size()) { const auto& currentStripeStats = contents_->metadata->stripe_stats(static_cast(currentStripe_)); // skip this stripe after stats fail to satisfy sargs