diff --git a/snappy.cc b/snappy.cc index d6d709a..dc9d2ee 100644 --- a/snappy.cc +++ b/snappy.cc @@ -1935,7 +1935,24 @@ class SnappyIOVecReader : public Source { private: // Advances to the next nonempty `iovec` and updates related variables. void Advance() { + // Best-effort cap on consecutive zero-length iovec walks. Without an + // explicit `iov_cnt` parameter (see API-level fix recommendation in the + // accompanying PR description), this is the safest interim guard against + // a run of zero-length trailing entries paired with a nonzero claimed + // `total_size_remaining_`. + int empty_walks = 0; + constexpr int kMaxEmptyWalks = 1024; do { + // Defensive check 1 (NDEBUG-safe): caller passed inconsistent + // `total_size_remaining_` (greater than the sum of actual `iov_len` + // values). Saturate to a no-data state and bail out instead of + // underflowing into a large unsigned value below. + if (total_size_remaining_ < curr_size_remaining_) { + curr_pos_ = nullptr; + curr_size_remaining_ = 0; + total_size_remaining_ = 0; + return; + } assert(total_size_remaining_ >= curr_size_remaining_); total_size_remaining_ -= curr_size_remaining_; if (total_size_remaining_ == 0) { @@ -1943,6 +1960,20 @@ class SnappyIOVecReader : public Source { curr_size_remaining_ = 0; return; } + // Defensive check 2: zero-length entries cap. If the caller claims + // `total_size_remaining_ > 0` but only zero-length iovecs are + // available, the do-while loop would otherwise spin past the array + // end. Bail out after `kMaxEmptyWalks` consecutive zero-length steps. + if (curr_size_remaining_ == 0) { + if (++empty_walks > kMaxEmptyWalks) { + curr_pos_ = nullptr; + curr_size_remaining_ = 0; + total_size_remaining_ = 0; + return; + } + } else { + empty_walks = 0; + } ++curr_iov_; curr_pos_ = reinterpret_cast(curr_iov_->iov_base); curr_size_remaining_ = curr_iov_->iov_len;