From 6ef93dce023058aa951e6828915b7ee92cff232f Mon Sep 17 00:00:00 2001 From: Groene AI <270696204+groeneai@users.noreply.github.com> Date: Tue, 6 Oct 2026 17:48:19 +0000 Subject: [PATCH 1/2] Bound the stripe statistics index in startNextStripe and getStripeStatistics 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 (protobuf bounds assert on debug, SIGSEGV on release). Entries are matched to stripes only by position, so a list of a different length cannot be used at all: a too-small metadataLength keeps only the LAST entries, which would otherwise be applied to the first stripes. startNextStripe now evaluates stripe statistics only when there is one entry per stripe; otherwise it reads the stripe without stripe-level filtering (row group indexes, bloom filters and dictionaries still apply), as for a file with no Metadata. getStripeStatistics throws ParseError on a count mismatch and std::logic_error on an out-of-range stripe index. 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 58b8534dcb..71d7cf521a 100644 --- a/c++/src/Reader.cc +++ b/c++/src/Reader.cc @@ -887,6 +887,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 std::logic_error("stripe index out of range"); + } proto::StripeInformation currentStripeInfo = footer_->stripes(static_cast(stripeIndex)); proto::StripeFooter currentStripeFooter = getStripeFooter(currentStripeInfo, *contents_.get()); @@ -1300,8 +1309,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 From 2c69dbd6d50006629575e233f856c0811e019ba9 Mon Sep 17 00:00:00 2001 From: Groene AI <270696204+groeneai@users.noreply.github.com> Date: Thu, 8 Oct 2026 10:15:11 +0000 Subject: [PATCH 2/2] Throw InvalidArgument instead of std::logic_error for a bad stripe index getStripeStatistics now rejects an out-of-range stripe index with orc::InvalidArgument (a std::runtime_error). ClickHouse treats any std::logic_error as a failed assertion and aborts in debug and sanitizer builds, so a caller-supplied index must not be reported that way. Co-Authored-By: Claude Opus 5.5 --- c++/src/Reader.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c++/src/Reader.cc b/c++/src/Reader.cc index 71d7cf521a..418aa4f839 100644 --- a/c++/src/Reader.cc +++ b/c++/src/Reader.cc @@ -894,7 +894,7 @@ namespace orc { throw ParseError(msg.str()); } if (stripeIndex >= numberOfStripes_) { - throw std::logic_error("stripe index out of range"); + throw InvalidArgument("stripe index out of range"); } proto::StripeInformation currentStripeInfo = footer_->stripes(static_cast(stripeIndex));