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