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.
ReadByteArrayToUint16(const uint8_t * buffer)18 inline uint16_t ReadByteArrayToUint16(const uint8_t* buffer) {
19   return (*buffer << 8) | *(buffer + 1);
20 }
21 }  // namespace
22 
GetNext(PuffData * data,Error * error)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 
BytesLeft() const121 size_t BufferPuffReader::BytesLeft() const {
122   return puff_size_ - index_;
123 }
124 
125 }  // namespace puffin
126