1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "perfetto/protozero/proto_decoder.h"
18 
19 #include <string.h>
20 #include <limits>
21 
22 #include "perfetto/base/compiler.h"
23 #include "perfetto/base/logging.h"
24 #include "perfetto/ext/base/utils.h"
25 #include "perfetto/protozero/proto_utils.h"
26 
27 namespace protozero {
28 
29 using namespace proto_utils;
30 
31 #if !PERFETTO_IS_LITTLE_ENDIAN()
32 #error Unimplemented for big endian archs.
33 #endif
34 
35 namespace {
36 
37 struct ParseFieldResult {
38   enum ParseResult { kAbort, kSkip, kOk };
39   ParseResult parse_res;
40   const uint8_t* next;
41   Field field;
42 };
43 
44 // Parses one field and returns the field itself and a pointer to the next
45 // field to parse. If parsing fails, the returned |next| == |buffer|.
46 PERFETTO_ALWAYS_INLINE ParseFieldResult
ParseOneField(const uint8_t * const buffer,const uint8_t * const end)47 ParseOneField(const uint8_t* const buffer, const uint8_t* const end) {
48   ParseFieldResult res{ParseFieldResult::kAbort, buffer, Field{}};
49 
50   // The first byte of a proto field is structured as follows:
51   // The least 3 significant bits determine the field type.
52   // The most 5 significant bits determine the field id. If MSB == 1, the
53   // field id continues on the next bytes following the VarInt encoding.
54   const uint8_t kFieldTypeNumBits = 3;
55   const uint64_t kFieldTypeMask = (1 << kFieldTypeNumBits) - 1;  // 0000 0111;
56   const uint8_t* pos = buffer;
57 
58   // If we've already hit the end, just return an invalid field.
59   if (PERFETTO_UNLIKELY(pos >= end))
60     return res;
61 
62   uint64_t preamble = 0;
63   if (PERFETTO_LIKELY(*pos < 0x80)) {  // Fastpath for fields with ID < 16.
64     preamble = *(pos++);
65   } else {
66     const uint8_t* next = ParseVarInt(pos, end, &preamble);
67     if (PERFETTO_UNLIKELY(pos == next))
68       return res;
69     pos = next;
70   }
71 
72   uint32_t field_id = static_cast<uint32_t>(preamble >> kFieldTypeNumBits);
73   if (field_id == 0 || pos >= end)
74     return res;
75 
76   auto field_type = static_cast<uint8_t>(preamble & kFieldTypeMask);
77   const uint8_t* new_pos = pos;
78   uint64_t int_value = 0;
79   uint64_t size = 0;
80 
81   switch (field_type) {
82     case static_cast<uint8_t>(ProtoWireType::kVarInt): {
83       new_pos = ParseVarInt(pos, end, &int_value);
84 
85       // new_pos not being greater than pos means ParseVarInt could not fully
86       // parse the number. This is because we are out of space in the buffer.
87       // Set the id to zero and return but don't update the offset so a future
88       // read can read this field.
89       if (PERFETTO_UNLIKELY(new_pos == pos))
90         return res;
91 
92       break;
93     }
94 
95     case static_cast<uint8_t>(ProtoWireType::kLengthDelimited): {
96       uint64_t payload_length;
97       new_pos = ParseVarInt(pos, end, &payload_length);
98       if (PERFETTO_UNLIKELY(new_pos == pos))
99         return res;
100 
101       // ParseVarInt guarantees that |new_pos| <= |end| when it succeeds;
102       if (payload_length > static_cast<uint64_t>(end - new_pos))
103         return res;
104 
105       const uintptr_t payload_start = reinterpret_cast<uintptr_t>(new_pos);
106       int_value = payload_start;
107       size = payload_length;
108       new_pos += payload_length;
109       break;
110     }
111 
112     case static_cast<uint8_t>(ProtoWireType::kFixed64): {
113       new_pos = pos + sizeof(uint64_t);
114       if (PERFETTO_UNLIKELY(new_pos > end))
115         return res;
116       memcpy(&int_value, pos, sizeof(uint64_t));
117       break;
118     }
119 
120     case static_cast<uint8_t>(ProtoWireType::kFixed32): {
121       new_pos = pos + sizeof(uint32_t);
122       if (PERFETTO_UNLIKELY(new_pos > end))
123         return res;
124       memcpy(&int_value, pos, sizeof(uint32_t));
125       break;
126     }
127 
128     default:
129       PERFETTO_DLOG("Invalid proto field type: %u", field_type);
130       return res;
131   }
132 
133   res.next = new_pos;
134 
135   if (PERFETTO_UNLIKELY(field_id > std::numeric_limits<uint16_t>::max())) {
136     PERFETTO_DLOG("Skipping field %" PRIu32 " because its id > 0xFFFF",
137                   field_id);
138     res.parse_res = ParseFieldResult::kSkip;
139     return res;
140   }
141 
142   if (PERFETTO_UNLIKELY(size > proto_utils::kMaxMessageLength)) {
143     PERFETTO_DLOG("Skipping field %" PRIu32 " because it's too big (%" PRIu64
144                   " KB)",
145                   field_id, size / 1024);
146     res.parse_res = ParseFieldResult::kSkip;
147     return res;
148   }
149 
150   res.parse_res = ParseFieldResult::kOk;
151   res.field.initialize(static_cast<uint16_t>(field_id), field_type, int_value,
152                        static_cast<uint32_t>(size));
153   return res;
154 }
155 
156 }  // namespace
157 
FindField(uint32_t field_id)158 Field ProtoDecoder::FindField(uint32_t field_id) {
159   Field res{};
160   auto old_position = read_ptr_;
161   read_ptr_ = begin_;
162   for (auto f = ReadField(); f.valid(); f = ReadField()) {
163     if (f.id() == field_id) {
164       res = f;
165       break;
166     }
167   }
168   read_ptr_ = old_position;
169   return res;
170 }
171 
172 PERFETTO_ALWAYS_INLINE
ReadField()173 Field ProtoDecoder::ReadField() {
174   ParseFieldResult res;
175   do {
176     res = ParseOneField(read_ptr_, end_);
177     read_ptr_ = res.next;
178   } while (PERFETTO_UNLIKELY(res.parse_res == ParseFieldResult::kSkip));
179   return res.field;
180 }
181 
ParseAllFields()182 void TypedProtoDecoderBase::ParseAllFields() {
183   const uint8_t* cur = begin_;
184   ParseFieldResult res;
185   for (;;) {
186     res = ParseOneField(cur, end_);
187     PERFETTO_DCHECK(res.parse_res != ParseFieldResult::kOk || res.next != cur);
188     cur = res.next;
189     if (PERFETTO_UNLIKELY(res.parse_res == ParseFieldResult::kSkip)) {
190       continue;
191     } else if (PERFETTO_UNLIKELY(res.parse_res == ParseFieldResult::kAbort)) {
192       break;
193     }
194     PERFETTO_DCHECK(res.parse_res == ParseFieldResult::kOk);
195     PERFETTO_DCHECK(res.field.valid());
196     auto field_id = res.field.id();
197     if (PERFETTO_UNLIKELY(field_id >= num_fields_))
198       continue;
199 
200     Field* fld = &fields_[field_id];
201     if (PERFETTO_LIKELY(!fld->valid())) {
202       // This is the first time we see this field.
203       *fld = std::move(res.field);
204     } else {
205       // Repeated field case.
206       // In this case we need to:
207       // 1. Append the last value of the field to end of the repeated field
208       //    storage.
209       // 2. Replace the default instance at offset |field_id| with the current
210       //    value. This is because in case of repeated field a call to Get(X) is
211       //    supposed to return the last value of X, not the first one.
212       // This is so that the RepeatedFieldIterator will iterate in the right
213       // order, see comments on RepeatedFieldIterator.
214       if (PERFETTO_UNLIKELY(size_ >= capacity_)) {
215         ExpandHeapStorage();
216         // ExpandHeapStorage moves fields_ so we need to update the ptr to fld:
217         fld = &fields_[field_id];
218         PERFETTO_DCHECK(size_ < capacity_);
219       }
220       fields_[size_++] = *fld;
221       *fld = std::move(res.field);
222     }
223   }
224   read_ptr_ = res.next;
225 }
226 
ExpandHeapStorage()227 void TypedProtoDecoderBase::ExpandHeapStorage() {
228   uint32_t new_capacity = capacity_ * 2;
229   PERFETTO_CHECK(new_capacity > size_);
230   std::unique_ptr<Field[]> new_storage(new Field[new_capacity]);
231 
232   static_assert(std::is_trivially_copyable<Field>::value,
233                 "Field must be trivially copyable");
234   memcpy(&new_storage[0], fields_, sizeof(Field) * size_);
235 
236   heap_storage_ = std::move(new_storage);
237   fields_ = &heap_storage_[0];
238   capacity_ = new_capacity;
239 }
240 
241 }  // namespace protozero
242