1 // Copyright (c) 2012 The Chromium 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 "base/pickle.h"
6 
7 #include <stdlib.h>
8 
9 #include <algorithm>  // for max()
10 #include <limits>
11 
12 #include "base/bits.h"
13 #include "base/macros.h"
14 #include "build/build_config.h"
15 
16 namespace base {
17 
18 // static
19 const int Pickle::kPayloadUnit = 64;
20 
21 static const size_t kCapacityReadOnly = static_cast<size_t>(-1);
22 
PickleIterator(const Pickle & pickle)23 PickleIterator::PickleIterator(const Pickle& pickle)
24     : payload_(pickle.payload()),
25       read_index_(0),
26       end_index_(pickle.payload_size()) {
27 }
28 
29 template <typename Type>
ReadBuiltinType(Type * result)30 inline bool PickleIterator::ReadBuiltinType(Type* result) {
31   const char* read_from = GetReadPointerAndAdvance<Type>();
32   if (!read_from)
33     return false;
34   if (sizeof(Type) > sizeof(uint32_t))
35     memcpy(result, read_from, sizeof(*result));
36   else
37     *result = *reinterpret_cast<const Type*>(read_from);
38   return true;
39 }
40 
Advance(size_t size)41 inline void PickleIterator::Advance(size_t size) {
42   size_t aligned_size = bits::Align(size, sizeof(uint32_t));
43   if (end_index_ - read_index_ < aligned_size) {
44     read_index_ = end_index_;
45   } else {
46     read_index_ += aligned_size;
47   }
48 }
49 
50 template<typename Type>
GetReadPointerAndAdvance()51 inline const char* PickleIterator::GetReadPointerAndAdvance() {
52   if (sizeof(Type) > end_index_ - read_index_) {
53     read_index_ = end_index_;
54     return NULL;
55   }
56   const char* current_read_ptr = payload_ + read_index_;
57   Advance(sizeof(Type));
58   return current_read_ptr;
59 }
60 
GetReadPointerAndAdvance(int num_bytes)61 const char* PickleIterator::GetReadPointerAndAdvance(int num_bytes) {
62   if (num_bytes < 0 ||
63       end_index_ - read_index_ < static_cast<size_t>(num_bytes)) {
64     read_index_ = end_index_;
65     return NULL;
66   }
67   const char* current_read_ptr = payload_ + read_index_;
68   Advance(num_bytes);
69   return current_read_ptr;
70 }
71 
GetReadPointerAndAdvance(int num_elements,size_t size_element)72 inline const char* PickleIterator::GetReadPointerAndAdvance(
73     int num_elements,
74     size_t size_element) {
75   // Check for int32_t overflow.
76   int64_t num_bytes = static_cast<int64_t>(num_elements) * size_element;
77   int num_bytes32 = static_cast<int>(num_bytes);
78   if (num_bytes != static_cast<int64_t>(num_bytes32))
79     return NULL;
80   return GetReadPointerAndAdvance(num_bytes32);
81 }
82 
ReadBool(bool * result)83 bool PickleIterator::ReadBool(bool* result) {
84   return ReadBuiltinType(result);
85 }
86 
ReadInt(int * result)87 bool PickleIterator::ReadInt(int* result) {
88   return ReadBuiltinType(result);
89 }
90 
ReadLong(long * result)91 bool PickleIterator::ReadLong(long* result) {
92   return ReadBuiltinType(result);
93 }
94 
ReadUInt16(uint16_t * result)95 bool PickleIterator::ReadUInt16(uint16_t* result) {
96   return ReadBuiltinType(result);
97 }
98 
ReadUInt32(uint32_t * result)99 bool PickleIterator::ReadUInt32(uint32_t* result) {
100   return ReadBuiltinType(result);
101 }
102 
ReadInt64(int64_t * result)103 bool PickleIterator::ReadInt64(int64_t* result) {
104   return ReadBuiltinType(result);
105 }
106 
ReadUInt64(uint64_t * result)107 bool PickleIterator::ReadUInt64(uint64_t* result) {
108   return ReadBuiltinType(result);
109 }
110 
ReadSizeT(size_t * result)111 bool PickleIterator::ReadSizeT(size_t* result) {
112   // Always read size_t as a 64-bit value to ensure compatibility between 32-bit
113   // and 64-bit processes.
114   uint64_t result_uint64 = 0;
115   bool success = ReadBuiltinType(&result_uint64);
116   *result = static_cast<size_t>(result_uint64);
117   // Fail if the cast above truncates the value.
118   return success && (*result == result_uint64);
119 }
120 
ReadFloat(float * result)121 bool PickleIterator::ReadFloat(float* result) {
122   // crbug.com/315213
123   // The source data may not be properly aligned, and unaligned float reads
124   // cause SIGBUS on some ARM platforms, so force using memcpy to copy the data
125   // into the result.
126   const char* read_from = GetReadPointerAndAdvance<float>();
127   if (!read_from)
128     return false;
129   memcpy(result, read_from, sizeof(*result));
130   return true;
131 }
132 
ReadDouble(double * result)133 bool PickleIterator::ReadDouble(double* result) {
134   // crbug.com/315213
135   // The source data may not be properly aligned, and unaligned double reads
136   // cause SIGBUS on some ARM platforms, so force using memcpy to copy the data
137   // into the result.
138   const char* read_from = GetReadPointerAndAdvance<double>();
139   if (!read_from)
140     return false;
141   memcpy(result, read_from, sizeof(*result));
142   return true;
143 }
144 
ReadString(std::string * result)145 bool PickleIterator::ReadString(std::string* result) {
146   int len;
147   if (!ReadInt(&len))
148     return false;
149   const char* read_from = GetReadPointerAndAdvance(len);
150   if (!read_from)
151     return false;
152 
153   result->assign(read_from, len);
154   return true;
155 }
156 
ReadStringPiece(StringPiece * result)157 bool PickleIterator::ReadStringPiece(StringPiece* result) {
158   int len;
159   if (!ReadInt(&len))
160     return false;
161   const char* read_from = GetReadPointerAndAdvance(len);
162   if (!read_from)
163     return false;
164 
165   *result = StringPiece(read_from, len);
166   return true;
167 }
168 
ReadString16(string16 * result)169 bool PickleIterator::ReadString16(string16* result) {
170   int len;
171   if (!ReadInt(&len))
172     return false;
173   const char* read_from = GetReadPointerAndAdvance(len, sizeof(char16));
174   if (!read_from)
175     return false;
176 
177   result->assign(reinterpret_cast<const char16*>(read_from), len);
178   return true;
179 }
180 
ReadStringPiece16(StringPiece16 * result)181 bool PickleIterator::ReadStringPiece16(StringPiece16* result) {
182   int len;
183   if (!ReadInt(&len))
184     return false;
185   const char* read_from = GetReadPointerAndAdvance(len, sizeof(char16));
186   if (!read_from)
187     return false;
188 
189   *result = StringPiece16(reinterpret_cast<const char16*>(read_from), len);
190   return true;
191 }
192 
ReadData(const char ** data,int * length)193 bool PickleIterator::ReadData(const char** data, int* length) {
194   *length = 0;
195   *data = 0;
196 
197   if (!ReadInt(length))
198     return false;
199 
200   return ReadBytes(data, *length);
201 }
202 
ReadBytes(const char ** data,int length)203 bool PickleIterator::ReadBytes(const char** data, int length) {
204   const char* read_from = GetReadPointerAndAdvance(length);
205   if (!read_from)
206     return false;
207   *data = read_from;
208   return true;
209 }
210 
211 // Payload is uint32_t aligned.
212 
Pickle()213 Pickle::Pickle()
214     : header_(NULL),
215       header_size_(sizeof(Header)),
216       capacity_after_header_(0),
217       write_offset_(0) {
218   static_assert((Pickle::kPayloadUnit & (Pickle::kPayloadUnit - 1)) == 0,
219                 "Pickle::kPayloadUnit must be a power of two");
220   Resize(kPayloadUnit);
221   header_->payload_size = 0;
222 }
223 
Pickle(int header_size)224 Pickle::Pickle(int header_size)
225     : header_(NULL),
226       header_size_(bits::Align(header_size, sizeof(uint32_t))),
227       capacity_after_header_(0),
228       write_offset_(0) {
229   DCHECK_GE(static_cast<size_t>(header_size), sizeof(Header));
230   DCHECK_LE(header_size, kPayloadUnit);
231   Resize(kPayloadUnit);
232   header_->payload_size = 0;
233 }
234 
Pickle(const char * data,int data_len)235 Pickle::Pickle(const char* data, int data_len)
236     : header_(reinterpret_cast<Header*>(const_cast<char*>(data))),
237       header_size_(0),
238       capacity_after_header_(kCapacityReadOnly),
239       write_offset_(0) {
240   if (data_len >= static_cast<int>(sizeof(Header)))
241     header_size_ = data_len - header_->payload_size;
242 
243   if (header_size_ > static_cast<unsigned int>(data_len))
244     header_size_ = 0;
245 
246   if (header_size_ != bits::Align(header_size_, sizeof(uint32_t)))
247     header_size_ = 0;
248 
249   // If there is anything wrong with the data, we're not going to use it.
250   if (!header_size_)
251     header_ = NULL;
252 }
253 
Pickle(const Pickle & other)254 Pickle::Pickle(const Pickle& other)
255     : header_(NULL),
256       header_size_(other.header_size_),
257       capacity_after_header_(0),
258       write_offset_(other.write_offset_) {
259   Resize(other.header_->payload_size);
260   memcpy(header_, other.header_, header_size_ + other.header_->payload_size);
261 }
262 
~Pickle()263 Pickle::~Pickle() {
264   if (capacity_after_header_ != kCapacityReadOnly)
265     free(header_);
266 }
267 
operator =(const Pickle & other)268 Pickle& Pickle::operator=(const Pickle& other) {
269   if (this == &other) {
270     NOTREACHED();
271     return *this;
272   }
273   if (capacity_after_header_ == kCapacityReadOnly) {
274     header_ = NULL;
275     capacity_after_header_ = 0;
276   }
277   if (header_size_ != other.header_size_) {
278     free(header_);
279     header_ = NULL;
280     header_size_ = other.header_size_;
281   }
282   Resize(other.header_->payload_size);
283   memcpy(header_, other.header_,
284          other.header_size_ + other.header_->payload_size);
285   write_offset_ = other.write_offset_;
286   return *this;
287 }
288 
WriteString(const StringPiece & value)289 bool Pickle::WriteString(const StringPiece& value) {
290   if (!WriteInt(static_cast<int>(value.size())))
291     return false;
292 
293   return WriteBytes(value.data(), static_cast<int>(value.size()));
294 }
295 
WriteString16(const StringPiece16 & value)296 bool Pickle::WriteString16(const StringPiece16& value) {
297   if (!WriteInt(static_cast<int>(value.size())))
298     return false;
299 
300   return WriteBytes(value.data(),
301                     static_cast<int>(value.size()) * sizeof(char16));
302 }
303 
WriteData(const char * data,int length)304 bool Pickle::WriteData(const char* data, int length) {
305   return length >= 0 && WriteInt(length) && WriteBytes(data, length);
306 }
307 
WriteBytes(const void * data,int length)308 bool Pickle::WriteBytes(const void* data, int length) {
309   WriteBytesCommon(data, length);
310   return true;
311 }
312 
Reserve(size_t length)313 void Pickle::Reserve(size_t length) {
314   size_t data_len = bits::Align(length, sizeof(uint32_t));
315   DCHECK_GE(data_len, length);
316 #ifdef ARCH_CPU_64_BITS
317   DCHECK_LE(data_len, std::numeric_limits<uint32_t>::max());
318 #endif
319   DCHECK_LE(write_offset_, std::numeric_limits<uint32_t>::max() - data_len);
320   size_t new_size = write_offset_ + data_len;
321   if (new_size > capacity_after_header_)
322     Resize(capacity_after_header_ * 2 + new_size);
323 }
324 
Resize(size_t new_capacity)325 void Pickle::Resize(size_t new_capacity) {
326   CHECK_NE(capacity_after_header_, kCapacityReadOnly);
327   capacity_after_header_ = bits::Align(new_capacity, kPayloadUnit);
328   void* p = realloc(header_, GetTotalAllocatedSize());
329   CHECK(p);
330   header_ = reinterpret_cast<Header*>(p);
331 }
332 
ClaimBytes(size_t num_bytes)333 void* Pickle::ClaimBytes(size_t num_bytes) {
334   void* p = ClaimUninitializedBytesInternal(num_bytes);
335   CHECK(p);
336   memset(p, 0, num_bytes);
337   return p;
338 }
339 
GetTotalAllocatedSize() const340 size_t Pickle::GetTotalAllocatedSize() const {
341   if (capacity_after_header_ == kCapacityReadOnly)
342     return 0;
343   return header_size_ + capacity_after_header_;
344 }
345 
346 // static
FindNext(size_t header_size,const char * start,const char * end)347 const char* Pickle::FindNext(size_t header_size,
348                              const char* start,
349                              const char* end) {
350   size_t pickle_size = 0;
351   if (!PeekNext(header_size, start, end, &pickle_size))
352     return NULL;
353 
354   if (pickle_size > static_cast<size_t>(end - start))
355     return NULL;
356 
357   return start + pickle_size;
358 }
359 
360 // static
PeekNext(size_t header_size,const char * start,const char * end,size_t * pickle_size)361 bool Pickle::PeekNext(size_t header_size,
362                       const char* start,
363                       const char* end,
364                       size_t* pickle_size) {
365   DCHECK_EQ(header_size, bits::Align(header_size, sizeof(uint32_t)));
366   DCHECK_GE(header_size, sizeof(Header));
367   DCHECK_LE(header_size, static_cast<size_t>(kPayloadUnit));
368 
369   size_t length = static_cast<size_t>(end - start);
370   if (length < sizeof(Header))
371     return false;
372 
373   const Header* hdr = reinterpret_cast<const Header*>(start);
374   if (length < header_size)
375     return false;
376 
377   if (hdr->payload_size > std::numeric_limits<size_t>::max() - header_size) {
378     // If payload_size causes an overflow, we return maximum possible
379     // pickle size to indicate that.
380     *pickle_size = std::numeric_limits<size_t>::max();
381   } else {
382     *pickle_size = header_size + hdr->payload_size;
383   }
384   return true;
385 }
386 
WriteBytesStatic(const void * data)387 template <size_t length> void Pickle::WriteBytesStatic(const void* data) {
388   WriteBytesCommon(data, length);
389 }
390 
391 template void Pickle::WriteBytesStatic<2>(const void* data);
392 template void Pickle::WriteBytesStatic<4>(const void* data);
393 template void Pickle::WriteBytesStatic<8>(const void* data);
394 
ClaimUninitializedBytesInternal(size_t length)395 inline void* Pickle::ClaimUninitializedBytesInternal(size_t length) {
396   DCHECK_NE(kCapacityReadOnly, capacity_after_header_)
397       << "oops: pickle is readonly";
398   size_t data_len = bits::Align(length, sizeof(uint32_t));
399   DCHECK_GE(data_len, length);
400 #ifdef ARCH_CPU_64_BITS
401   DCHECK_LE(data_len, std::numeric_limits<uint32_t>::max());
402 #endif
403   DCHECK_LE(write_offset_, std::numeric_limits<uint32_t>::max() - data_len);
404   size_t new_size = write_offset_ + data_len;
405   if (new_size > capacity_after_header_) {
406     size_t new_capacity = capacity_after_header_ * 2;
407     const size_t kPickleHeapAlign = 4096;
408     if (new_capacity > kPickleHeapAlign)
409       new_capacity = bits::Align(new_capacity, kPickleHeapAlign) - kPayloadUnit;
410     Resize(std::max(new_capacity, new_size));
411   }
412 
413   char* write = mutable_payload() + write_offset_;
414   memset(write + length, 0, data_len - length);  // Always initialize padding
415   header_->payload_size = static_cast<uint32_t>(new_size);
416   write_offset_ = new_size;
417   return write;
418 }
419 
WriteBytesCommon(const void * data,size_t length)420 inline void Pickle::WriteBytesCommon(const void* data, size_t length) {
421   DCHECK_NE(kCapacityReadOnly, capacity_after_header_)
422       << "oops: pickle is readonly";
423   MSAN_CHECK_MEM_IS_INITIALIZED(data, length);
424   void* write = ClaimUninitializedBytesInternal(length);
425   memcpy(write, data, length);
426 }
427 
428 }  // namespace base
429