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
16namespace base {
17
18// static
19const int Pickle::kPayloadUnit = 64;
20
21static const size_t kCapacityReadOnly = static_cast<size_t>(-1);
22
23PickleIterator::PickleIterator(const Pickle& pickle)
24    : payload_(pickle.payload()),
25      read_index_(0),
26      end_index_(pickle.payload_size()) {
27}
28
29template <typename Type>
30inline 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
41inline 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
50template<typename Type>
51inline 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
61const 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
72inline 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
83bool PickleIterator::ReadBool(bool* result) {
84  return ReadBuiltinType(result);
85}
86
87bool PickleIterator::ReadInt(int* result) {
88  return ReadBuiltinType(result);
89}
90
91bool PickleIterator::ReadLong(long* result) {
92  return ReadBuiltinType(result);
93}
94
95bool PickleIterator::ReadUInt16(uint16_t* result) {
96  return ReadBuiltinType(result);
97}
98
99bool PickleIterator::ReadUInt32(uint32_t* result) {
100  return ReadBuiltinType(result);
101}
102
103bool PickleIterator::ReadInt64(int64_t* result) {
104  return ReadBuiltinType(result);
105}
106
107bool PickleIterator::ReadUInt64(uint64_t* result) {
108  return ReadBuiltinType(result);
109}
110
111bool 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
121bool 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
133bool 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
145bool 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
157bool 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
169bool 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
181bool 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
193bool 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
203bool 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
213Pickle::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
224Pickle::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
235Pickle::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
254Pickle::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
263Pickle::~Pickle() {
264  if (capacity_after_header_ != kCapacityReadOnly)
265    free(header_);
266}
267
268Pickle& 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
289bool 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
296bool 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
304bool Pickle::WriteData(const char* data, int length) {
305  return length >= 0 && WriteInt(length) && WriteBytes(data, length);
306}
307
308bool Pickle::WriteBytes(const void* data, int length) {
309  WriteBytesCommon(data, length);
310  return true;
311}
312
313void 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
325void 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
333void* 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
340size_t Pickle::GetTotalAllocatedSize() const {
341  if (capacity_after_header_ == kCapacityReadOnly)
342    return 0;
343  return header_size_ + capacity_after_header_;
344}
345
346// static
347const 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
361bool 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
387template <size_t length> void Pickle::WriteBytesStatic(const void* data) {
388  WriteBytesCommon(data, length);
389}
390
391template void Pickle::WriteBytesStatic<2>(const void* data);
392template void Pickle::WriteBytesStatic<4>(const void* data);
393template void Pickle::WriteBytesStatic<8>(const void* data);
394
395inline 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
420inline 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