diff --git a/c++/src/RLEv1.cc b/c++/src/RLEv1.cc index 72c555e610..4a7ab6e278 100644 --- a/c++/src/RLEv1.cc +++ b/c++/src/RLEv1.cc @@ -140,7 +140,10 @@ namespace orc { signed char RleDecoderV1::readByte() { SCOPED_MINUS_STOPWATCH(metrics, DecodingLatencyUs); - if (bufferStart_ == bufferEnd_) { + // Use >= rather than == so that a cursor which has already overshot the + // buffer end (bufferStart_ > bufferEnd_) still triggers a refill instead of + // dereferencing out of bounds. + if (bufferStart_ >= bufferEnd_) { int bufferLength; const void* bufferPointer; if (!inputStream_->Next(&bufferPointer, &bufferLength)) { diff --git a/c++/src/RLEv2.hh b/c++/src/RLEv2.hh index c2ce5aa851..b7279ee39b 100644 --- a/c++/src/RLEv2.hh +++ b/c++/src/RLEv2.hh @@ -188,7 +188,11 @@ namespace orc { } uint64_t bufLength() { - return bufferEnd_ - bufferStart_; + // Guard against a cursor that has overshot the buffer end. If bufferStart_ + // has advanced past bufferEnd_, the raw pointer difference is negative and + // would underflow to a huge value when returned as unsigned, defeating the + // std::min clamps in the bit-unpacking fast loops (BpackingDefault.cc). + return bufferEnd_ > bufferStart_ ? static_cast(bufferEnd_ - bufferStart_) : 0; } void setBitsLeft(const uint32_t bits) { diff --git a/c++/src/RleDecoderV2.cc b/c++/src/RleDecoderV2.cc index f35dd4fe5d..d3192869a0 100644 --- a/c++/src/RleDecoderV2.cc +++ b/c++/src/RleDecoderV2.cc @@ -31,7 +31,10 @@ namespace orc { unsigned char RleDecoderV2::readByte() { SCOPED_MINUS_STOPWATCH(metrics, DecodingLatencyUs); - if (bufferStart_ == bufferEnd_) { + // Use >= rather than == so that a cursor which has already overshot the + // buffer end (bufferStart_ > bufferEnd_) still triggers a refill instead of + // dereferencing out of bounds. + if (bufferStart_ >= bufferEnd_) { int bufferLength; const void* bufferPointer; if (!inputStream_->Next(&bufferPointer, &bufferLength)) {