// Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "puffin/src/puff_reader.h" #include <algorithm> #include <memory> #include <string> #include <vector> #include "puffin/src/set_errors.h" namespace puffin { namespace { // Reads a value from the buffer in big-endian mode. inline uint16_t ReadByteArrayToUint16(const uint8_t* buffer) { return (*buffer << 8) | *(buffer + 1); } } // namespace bool BufferPuffReader::GetNext(PuffData* data, Error* error) { PuffData& pd = *data; size_t length = 0; if (state_ == State::kReadingLenDist) { // Boundary check TEST_AND_RETURN_FALSE_SET_ERROR(index_ < puff_size_, Error::kInsufficientInput); if (puff_buf_in_[index_] & 0x80) { // Reading length/distance. if ((puff_buf_in_[index_] & 0x7F) < 127) { length = puff_buf_in_[index_] & 0x7F; } else { index_++; // Boundary check TEST_AND_RETURN_FALSE_SET_ERROR(index_ < puff_size_, Error::kInsufficientInput); length = puff_buf_in_[index_] + 127; } length += 3; TEST_AND_RETURN_FALSE(length <= 259); index_++; // End of block. End of block is similar to length/distance but without // distance value and length value set to 259. if (length == 259) { pd.type = PuffData::Type::kEndOfBlock; state_ = State::kReadingBlockMetadata; DVLOG(2) << "Read end of block"; return true; } // Boundary check TEST_AND_RETURN_FALSE_SET_ERROR(index_ + 1 < puff_size_, Error::kInsufficientInput); auto distance = ReadByteArrayToUint16(&puff_buf_in_[index_]); // The distance in RFC is in the range [1..32768], but in the puff spec, // we write zero-based distance in the puff stream. TEST_AND_RETURN_FALSE_SET_ERROR(distance < (1 << 15), Error::kInsufficientInput); distance++; index_ += 2; pd.type = PuffData::Type::kLenDist; pd.length = length; pd.distance = distance; DVLOG(2) << "Read length: " << length << " distance: " << distance; return true; } else { // Reading literals. // Boundary check TEST_AND_RETURN_FALSE_SET_ERROR(index_ < puff_size_, Error::kInsufficientInput); if ((puff_buf_in_[index_] & 0x7F) < 127) { length = puff_buf_in_[index_] & 0x7F; index_++; } else { index_++; // Boundary check TEST_AND_RETURN_FALSE_SET_ERROR(index_ + 1 < puff_size_, Error::kInsufficientInput); length = ReadByteArrayToUint16(&puff_buf_in_[index_]) + 127; index_ += 2; } length++; DVLOG(2) << "Read literals length: " << length; // Boundary check TEST_AND_RETURN_FALSE_SET_ERROR(index_ + length <= puff_size_, Error::kInsufficientInput); pd.type = PuffData::Type::kLiterals; pd.length = length; pd.read_fn = [this, length](uint8_t* buffer, size_t count) mutable { TEST_AND_RETURN_FALSE(count <= length); memcpy(buffer, &puff_buf_in_[index_], count); index_ += count; length -= count; return true; }; return true; } } else { // Block metadata pd.type = PuffData::Type::kBlockMetadata; // Boundary check TEST_AND_RETURN_FALSE_SET_ERROR(index_ + 2 < puff_size_, Error::kInsufficientInput); length = ReadByteArrayToUint16(&puff_buf_in_[index_]) + 1; index_ += 2; DVLOG(2) << "Read block metadata length: " << length; // Boundary check TEST_AND_RETURN_FALSE_SET_ERROR(index_ + length <= puff_size_, Error::kInsufficientInput); TEST_AND_RETURN_FALSE(length <= sizeof(pd.block_metadata)); memcpy(pd.block_metadata, &puff_buf_in_[index_], length); index_ += length; pd.length = length; state_ = State::kReadingLenDist; } return true; } size_t BufferPuffReader::BytesLeft() const { return puff_size_ - index_; } } // namespace puffin