You need to agree to share your contact information to access this model

This repository is publicly accessible, but you have to accept the conditions to access its files and content.

Log in or Sign Up to review the conditions and access this model content.

YAML Metadata Warning:empty or missing yaml metadata in repo card

Check out the documentation for more information.

Apache ORC C++ LIST length child-count corruption

Status: reproduced on current main; prior-art gate open.

Apache ORC's C++ ListColumnReader decodes the untrusted LIST LENGTH stream as unsigned integers into int64_t slots, then casts each slot to uint64_t and accumulates it without validating the value or the sum. A 289-byte ORC file can therefore drive either an impossible child allocation or wrapped, negative collection offsets.

Tested at Apache ORC commit af4cbf36b051c176f0d13ae5a7ac436ada8aeccf (2026-07-29).

Root cause

The LIST reader selects unsigned RLE:

rle_ = createRleDecoder(std::move(stream), false, vers, memoryPool, metrics);

The decoded bits are stored in int64_t, cast back to uint64_t, and added without range or overflow checks:

rle_->next(offsets, numValues, notNull);
uint64_t totalChildren = 0;
for (size_t i = 0; i < numValues; ++i) {
  uint64_t tmp = static_cast<uint64_t>(offsets[i]);
  offsets[i] = static_cast<int64_t>(totalChildren);
  totalChildren += tmp;
}
offsets[numValues] = static_cast<int64_t>(totalChildren);
childReader->next(*(listBatch.elements.get()), totalChildren, nullptr);

DataBuffer<T>::reserve() then trusts the impossible capacity, does not check sizeof(T) * newCapacity, and does not check malloc() before copying:

buf_ = reinterpret_cast<T*>(
    memoryPool_.malloc(sizeof(T) * newCapacity));
memcpy(buf_, buf_old, sizeof(T) * currentSize_);

The same unchecked accumulation exists in ListColumnReader::skip() and in the parallel Map reader.

Differential proof

Fixture LIST lengths Result with official orc-contents
control-one-row.orc [1] Prints {"xs": [7]}; exit 0
trigger-huge-child-count.orc [2^63] SIGSEGV in child-batch resize
control-two-rows.orc [1, 1] Prints both rows; exit 0
trigger-wrapped-offsets.orc [2^63, 2^63] Wrapped offsets; downstream OOB read/SIGBUS

The one-row ASan/UBSan build crashes with EXC_BAD_ACCESS at a null write:

_platform_memmove
orc::DataBuffer<char>::reserve(unsigned long long)  MemoryPool.cc:119
orc::DataBuffer<char>::resize(unsigned long long)   MemoryPool.cc:167
orc::ColumnVectorBatch::resize(unsigned long long)  Vector.cc:49
orc::IntegerVectorBatch<long long>::resize(...)     Vector.hh:119
orc::ColumnReader::next(...)                        ColumnReader.cc:83
orc::ListColumnReader::nextInternal<false>(...)     ColumnReader.cc:991

The two-row trigger makes totalChildren wrap to zero and returns offsets [0, INT64_MIN, 0]. The official printer later starts the second row at INT64_MIN, casts the index to uint64_t, and crashes reading the child vector:

orc::LongColumnPrinter::printRow(unsigned long long) ColumnPrinter.cc:329
orc::ListColumnPrinter::printRow(unsigned long long) ColumnPrinter.cc:481

This is not merely an allocation-pressure test: the second fixture demonstrates parser-generated invalid offsets and a native out-of-bounds read in an ordinary consumer.

Reproduce

Build the pinned current source with the project sanitizer options:

cmake -S orc -B orc-asan \
  -DCMAKE_BUILD_TYPE=DEBUG \
  -DBUILD_JAVA=OFF \
  -DBUILD_CPP_TESTS=OFF \
  -DBUILD_TOOLS=ON \
  -DORC_ENABLE_ASAN=ON \
  -DORC_ENABLE_UBSAN=ON
cmake --build orc-asan --target orc-contents --parallel 4

Then:

ORC_CONTENTS=/path/to/orc-asan/tools/src/orc-contents ./reproduce.sh

generate_fixtures.py materializes the controls and applies the exact LIST stream mutations. It asserts all four SHA-256 hashes before testing.

Impact

Applications that deserialize attacker-supplied ORC LIST or MAP columns can crash during ordinary row reads. Depending on the decoded lengths, the C++ reader can:

  • pass an attacker-controlled impossible capacity into native buffer resizing, causing a null-pointer write after allocation failure;
  • wrap totalChildren and return negative/non-monotonic offsets to callers;
  • induce out-of-bounds reads in consumers that trust those offsets.

The demonstrated impact is deterministic denial of service and native out-of-bounds read. This report does not claim code execution.

Suggested remediation

Reject any decoded collection length that is negative when represented as int64_t, and reject every addition that would overflow uint64_t or exceed the reader's practical allocation limit. Keep offsets representable and monotonically nondecreasing in int64_t. Apply the checks to LIST and MAP next, nextEncoded, and skip, then add malformed unsigned RLEv1/RLEv2 regression fixtures.

Separately, harden DataBuffer<T>::reserve() against multiplication overflow and allocation failure.

Novelty

The fresh exact scan found no Hugging Face repository, Apache ORC issue/PR, or local report for LIST/MAP child-count accumulation. Public adjacent findings are different occurrences:

  • ORC-2192: direct STRING byte-length handling in StringDirectColumnReader::computeSize;
  • poc-orc-rlev2-patchedbase-oob-cwe125: PATCHED_BASE decoder OOB;
  • ORC-414: malformed footer protobuf indexing;
  • CVE-2018-8015: recursively nested schemas.

None covers ListColumnReader/MapColumnReader, totalChildren wrap, or collection offsets.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support