1 // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "puffin/src/puff_reader.h" 6 7 #include <algorithm> 8 #include <memory> 9 #include <string> 10 #include <vector> 11 12 #include "puffin/src/set_errors.h" 13 14 namespace puffin { 15 16 namespace { 17 // Reads a value from the buffer in big-endian mode. 18 inline uint16_t ReadByteArrayToUint16(const uint8_t* buffer) { 19 return (*buffer << 8) | *(buffer + 1); 20 } 21 } // namespace 22 23 bool BufferPuffReader::GetNext(PuffData* data, Error* error) { 24 PuffData& pd = *data; 25 size_t length = 0; 26 if (state_ == State::kReadingLenDist) { 27 // Boundary check 28 TEST_AND_RETURN_FALSE_SET_ERROR(index_ < puff_size_, 29 Error::kInsufficientInput); 30 if (puff_buf_in_[index_] & 0x80) { // Reading length/distance. 31 if ((puff_buf_in_[index_] & 0x7F) < 127) { 32 length = puff_buf_in_[index_] & 0x7F; 33 } else { 34 index_++; 35 // Boundary check 36 TEST_AND_RETURN_FALSE_SET_ERROR(index_ < puff_size_, 37 Error::kInsufficientInput); 38 length = puff_buf_in_[index_] + 127; 39 } 40 length += 3; 41 TEST_AND_RETURN_FALSE(length <= 259); 42 43 index_++; 44 45 // End of block. End of block is similar to length/distance but without 46 // distance value and length value set to 259. 47 if (length == 259) { 48 pd.type = PuffData::Type::kEndOfBlock; 49 state_ = State::kReadingBlockMetadata; 50 DVLOG(2) << "Read end of block"; 51 return true; 52 } 53 54 // Boundary check 55 TEST_AND_RETURN_FALSE_SET_ERROR(index_ + 1 < puff_size_, 56 Error::kInsufficientInput); 57 auto distance = ReadByteArrayToUint16(&puff_buf_in_[index_]); 58 // The distance in RFC is in the range [1..32768], but in the puff spec, 59 // we write zero-based distance in the puff stream. 60 TEST_AND_RETURN_FALSE_SET_ERROR(distance < (1 << 15), 61 Error::kInsufficientInput); 62 distance++; 63 index_ += 2; 64 65 pd.type = PuffData::Type::kLenDist; 66 pd.length = length; 67 pd.distance = distance; 68 DVLOG(2) << "Read length: " << length << " distance: " << distance; 69 return true; 70 } else { // Reading literals. 71 // Boundary check 72 TEST_AND_RETURN_FALSE_SET_ERROR(index_ < puff_size_, 73 Error::kInsufficientInput); 74 if ((puff_buf_in_[index_] & 0x7F) < 127) { 75 length = puff_buf_in_[index_] & 0x7F; 76 index_++; 77 } else { 78 index_++; 79 // Boundary check 80 TEST_AND_RETURN_FALSE_SET_ERROR(index_ + 1 < puff_size_, 81 Error::kInsufficientInput); 82 length = ReadByteArrayToUint16(&puff_buf_in_[index_]) + 127; 83 index_ += 2; 84 } 85 length++; 86 DVLOG(2) << "Read literals length: " << length; 87 // Boundary check 88 TEST_AND_RETURN_FALSE_SET_ERROR(index_ + length <= puff_size_, 89 Error::kInsufficientInput); 90 pd.type = PuffData::Type::kLiterals; 91 pd.length = length; 92 pd.read_fn = [this, length](uint8_t* buffer, size_t count) mutable { 93 TEST_AND_RETURN_FALSE(count <= length); 94 memcpy(buffer, &puff_buf_in_[index_], count); 95 index_ += count; 96 length -= count; 97 return true; 98 }; 99 return true; 100 } 101 } else { // Block metadata 102 pd.type = PuffData::Type::kBlockMetadata; 103 // Boundary check 104 TEST_AND_RETURN_FALSE_SET_ERROR(index_ + 2 < puff_size_, 105 Error::kInsufficientInput); 106 length = ReadByteArrayToUint16(&puff_buf_in_[index_]) + 1; 107 index_ += 2; 108 DVLOG(2) << "Read block metadata length: " << length; 109 // Boundary check 110 TEST_AND_RETURN_FALSE_SET_ERROR(index_ + length <= puff_size_, 111 Error::kInsufficientInput); 112 TEST_AND_RETURN_FALSE(length <= sizeof(pd.block_metadata)); 113 memcpy(pd.block_metadata, &puff_buf_in_[index_], length); 114 index_ += length; 115 pd.length = length; 116 state_ = State::kReadingLenDist; 117 } 118 return true; 119 } 120 121 size_t BufferPuffReader::BytesLeft() const { 122 return puff_size_ - index_; 123 } 124 125 } // namespace puffin 126