tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

pickle.cc (15980B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
      4 // Use of this source code is governed by a BSD-style license that can be
      5 // found in the LICENSE file.
      6 
      7 #include "base/pickle.h"
      8 
      9 #include "mozilla/CheckedInt.h"
     10 #include "mozilla/EndianUtils.h"
     11 #include "mozilla/Telemetry.h"
     12 #include "mozilla/ipc/ProtocolUtils.h"
     13 
     14 #include <stdlib.h>
     15 
     16 #include <limits>
     17 #include <string>
     18 #include <algorithm>
     19 #include <type_traits>
     20 
     21 #include "nsDebug.h"
     22 
     23 //------------------------------------------------------------------------------
     24 
     25 static_assert(alignof(Pickle::memberAlignmentType) >= alignof(uint32_t),
     26              "Insufficient alignment");
     27 
     28 static const uint32_t kHeaderSegmentCapacity = 64;
     29 
     30 static const uint32_t kDefaultSegmentCapacity = 4096;
     31 
     32 static const char kBytePaddingMarker = char(0xbf);
     33 
     34 namespace {
     35 
     36 // We want to copy data to our payload as efficiently as possible.
     37 // memcpy fits the bill for copying, but not all compilers or
     38 // architectures support inlining memcpy from void*, which has unknown
     39 // static alignment.  However, we know that all the members of our
     40 // payload will be aligned on memberAlignmentType boundaries.  We
     41 // therefore use that knowledge to construct a copier that will copy
     42 // efficiently (via standard C++ assignment mechanisms) if the datatype
     43 // needs that alignment or less, and memcpy otherwise.  (The compiler
     44 // may still inline memcpy, of course.)
     45 
     46 template <typename T, size_t size, bool hasSufficientAlignment>
     47 struct Copier {
     48  static void Copy(T* dest, const char* iter) { memcpy(dest, iter, sizeof(T)); }
     49 };
     50 
     51 // Copying 64-bit quantities happens often enough and can easily be made
     52 // worthwhile on 32-bit platforms, so handle it specially.  Only do it
     53 // if 64-bit types aren't sufficiently aligned; the alignment
     54 // requirements for them vary between 32-bit platforms.
     55 #ifndef HAVE_64BIT_BUILD
     56 template <typename T>
     57 struct Copier<T, sizeof(uint64_t), false> {
     58  static void Copy(T* dest, const char* iter) {
     59 #  if MOZ_LITTLE_ENDIAN
     60    static const int loIndex = 0, hiIndex = 1;
     61 #  else
     62    static const int loIndex = 1, hiIndex = 0;
     63 #  endif
     64    static_assert(alignof(uint32_t*) == alignof(void*),
     65                  "Pointers have different alignments");
     66    const uint32_t* src = reinterpret_cast<const uint32_t*>(iter);
     67    uint32_t* uint32dest = reinterpret_cast<uint32_t*>(dest);
     68    uint32dest[loIndex] = src[loIndex];
     69    uint32dest[hiIndex] = src[hiIndex];
     70  }
     71 };
     72 #endif
     73 
     74 template <typename T, size_t size>
     75 struct Copier<T, size, true> {
     76  static void Copy(T* dest, const char* iter) {
     77    // The pointer ought to be properly aligned.
     78    DCHECK_EQ((((uintptr_t)iter) & (alignof(T) - 1)), 0);
     79    *dest = *reinterpret_cast<const T*>(iter);
     80  }
     81 };
     82 
     83 }  // anonymous namespace
     84 
     85 PickleIterator::PickleIterator(const Pickle& pickle)
     86    : iter_(pickle.buffers_.Iter()) {
     87  iter_.Advance(pickle.buffers_, pickle.header_size_);
     88 }
     89 
     90 template <typename T>
     91 void PickleIterator::CopyInto(T* dest) {
     92  static_assert(std::is_trivially_copyable<T>::value,
     93                "Copied type must be a POD type");
     94  Copier<T, sizeof(T), (alignof(T) <= sizeof(Pickle::memberAlignmentType))>::
     95      Copy(dest, iter_.Data());
     96 }
     97 template void PickleIterator::CopyInto<char>(char*);
     98 
     99 bool Pickle::IteratorHasRoomFor(const PickleIterator& iter,
    100                                uint32_t len) const {
    101  // Make sure we don't get into trouble where AlignInt(len) == 0.
    102  MOZ_RELEASE_ASSERT(len < 64);
    103 
    104  return iter.iter_.HasRoomFor(AlignInt(len));
    105 }
    106 
    107 bool Pickle::HasBytesAvailable(const PickleIterator* iter, uint32_t len) const {
    108  return iter->iter_.HasBytesAvailable(buffers_, len);
    109 }
    110 
    111 void Pickle::UpdateIter(PickleIterator* iter, uint32_t bytes) const {
    112  // Make sure we don't get into trouble where AlignInt(bytes) == 0.
    113  MOZ_RELEASE_ASSERT(bytes < 64);
    114 
    115  iter->iter_.Advance(buffers_, AlignInt(bytes));
    116 }
    117 
    118 // Payload is sizeof(Pickle::memberAlignmentType) aligned.
    119 
    120 Pickle::Pickle(uint32_t header_size, size_t segment_capacity)
    121    : buffers_(AlignInt(header_size),
    122               segment_capacity ? segment_capacity : kHeaderSegmentCapacity,
    123               segment_capacity ? segment_capacity : kDefaultSegmentCapacity),
    124      header_(nullptr),
    125      header_size_(AlignInt(header_size)) {
    126  DCHECK(static_cast<memberAlignmentType>(header_size) >= sizeof(Header));
    127  DCHECK(header_size_ <= kHeaderSegmentCapacity);
    128  header_ = reinterpret_cast<Header*>(buffers_.Start());
    129  memset(header_, 0, header_size_);
    130  header_->payload_size = 0;
    131 }
    132 
    133 Pickle::Pickle(uint32_t header_size, const char* data, uint32_t length)
    134    : buffers_(length, AlignCapacity(length), kDefaultSegmentCapacity),
    135      header_(nullptr),
    136      header_size_(AlignInt(header_size)) {
    137  DCHECK(static_cast<memberAlignmentType>(header_size) >= sizeof(Header));
    138  DCHECK(header_size <= kHeaderSegmentCapacity);
    139  MOZ_RELEASE_ASSERT(header_size <= length);
    140 
    141  header_ = reinterpret_cast<Header*>(buffers_.Start());
    142  memcpy(header_, data, length);
    143 }
    144 
    145 Pickle::Pickle(Pickle&& other)
    146    : buffers_(std::move(other.buffers_)),
    147      header_(other.header_),
    148      header_size_(other.header_size_) {
    149  other.header_ = nullptr;
    150 }
    151 
    152 Pickle::~Pickle() {}
    153 
    154 Pickle& Pickle::operator=(Pickle&& other) {
    155  BufferList tmp = std::move(other.buffers_);
    156  other.buffers_ = std::move(buffers_);
    157  buffers_ = std::move(tmp);
    158 
    159  // std::swap(buffers_, other.buffers_);
    160  std::swap(header_, other.header_);
    161  std::swap(header_size_, other.header_size_);
    162  return *this;
    163 }
    164 
    165 void Pickle::CopyFrom(const Pickle& other) {
    166  MOZ_ALWAYS_TRUE(buffers_.CopyFrom(other.buffers_));
    167  MOZ_ASSERT(other.header_ ==
    168             reinterpret_cast<const Header*>(other.buffers_.Start()));
    169 
    170  header_ = reinterpret_cast<Header*>(buffers_.Start());
    171  header_size_ = other.header_size_;
    172 }
    173 
    174 bool Pickle::ReadBool(PickleIterator* iter, bool* result) const {
    175  int tmp;
    176  if (!ReadScalar(iter, &tmp)) return false;
    177 
    178  DCHECK(0 == tmp || 1 == tmp);
    179  *result = tmp ? true : false;
    180 
    181  return true;
    182 }
    183 
    184 bool Pickle::ReadInt16(PickleIterator* iter, int16_t* result) const {
    185  return ReadScalar(iter, result);
    186 }
    187 
    188 bool Pickle::ReadUInt16(PickleIterator* iter, uint16_t* result) const {
    189  return ReadScalar(iter, result);
    190 }
    191 
    192 bool Pickle::ReadInt(PickleIterator* iter, int* result) const {
    193  return ReadScalar(iter, result);
    194 }
    195 
    196 // Always written as a 64-bit value since the size for this type can
    197 // differ between architectures.
    198 bool Pickle::ReadLong(PickleIterator* iter, long* result) const {
    199  int64_t big_result;
    200  if (!ReadScalar(iter, &big_result)) return false;
    201 
    202  DCHECK(big_result <= LONG_MAX && big_result >= LONG_MIN);
    203  *result = static_cast<long>(big_result);
    204 
    205  return true;
    206 }
    207 
    208 // Always written as a 64-bit value since the size for this type can
    209 // differ between architectures.
    210 bool Pickle::ReadULong(PickleIterator* iter, unsigned long* result) const {
    211  uint64_t big_result;
    212  if (!ReadScalar(iter, &big_result)) return false;
    213  DCHECK(big_result <= ULONG_MAX);
    214  *result = static_cast<unsigned long>(big_result);
    215 
    216  return true;
    217 }
    218 
    219 bool Pickle::ReadLength(PickleIterator* iter, int* result) const {
    220  if (!ReadScalar(iter, result)) return false;
    221  return ((*result) >= 0);
    222 }
    223 
    224 bool Pickle::ReadInt32(PickleIterator* iter, int32_t* result) const {
    225  return ReadScalar(iter, result);
    226 }
    227 
    228 bool Pickle::ReadUInt32(PickleIterator* iter, uint32_t* result) const {
    229  return ReadScalar(iter, result);
    230 }
    231 
    232 bool Pickle::ReadInt64(PickleIterator* iter, int64_t* result) const {
    233  return ReadScalar(iter, result);
    234 }
    235 
    236 bool Pickle::ReadUInt64(PickleIterator* iter, uint64_t* result) const {
    237  return ReadScalar(iter, result);
    238 }
    239 
    240 bool Pickle::ReadDouble(PickleIterator* iter, double* result) const {
    241  return ReadScalar(iter, result);
    242 }
    243 
    244 // Always written as a 64-bit value since the size for this type can
    245 // differ between architectures.
    246 bool Pickle::ReadIntPtr(PickleIterator* iter, intptr_t* result) const {
    247  DCHECK(iter);
    248 
    249  int64_t big_result;
    250  if (!ReadScalar(iter, &big_result)) return false;
    251 
    252  DCHECK(big_result <= std::numeric_limits<intptr_t>::max() &&
    253         big_result >= std::numeric_limits<intptr_t>::min());
    254  *result = static_cast<intptr_t>(big_result);
    255 
    256  return true;
    257 }
    258 
    259 bool Pickle::ReadUnsignedChar(PickleIterator* iter,
    260                              unsigned char* result) const {
    261  return ReadScalar(iter, result);
    262 }
    263 
    264 bool Pickle::ReadString(PickleIterator* iter, std::string* result) const {
    265  DCHECK(iter);
    266 
    267  int len;
    268  if (!ReadLength(iter, &len)) return false;
    269 
    270  auto chars = mozilla::MakeUnique<char[]>(len);
    271  if (!ReadBytesInto(iter, chars.get(), len)) {
    272    return false;
    273  }
    274  result->assign(chars.get(), len);
    275 
    276  return true;
    277 }
    278 
    279 bool Pickle::ReadWString(PickleIterator* iter, std::wstring* result) const {
    280  DCHECK(iter);
    281 
    282  int len;
    283  if (!ReadLength(iter, &len)) return false;
    284  // Avoid integer multiplication overflow.
    285  if (len > INT_MAX / static_cast<int>(sizeof(wchar_t))) return false;
    286 
    287  auto chars = mozilla::MakeUnique<wchar_t[]>(len);
    288  if (!ReadBytesInto(iter, chars.get(), len * sizeof(wchar_t))) {
    289    return false;
    290  }
    291  result->assign(chars.get(), len);
    292 
    293  return true;
    294 }
    295 
    296 bool Pickle::ReadBytesInto(PickleIterator* iter, void* data,
    297                           uint32_t length) const {
    298  if (AlignInt(length) < length) {
    299    return false;
    300  }
    301 
    302  if (!buffers_.ReadBytes(iter->iter_, reinterpret_cast<char*>(data), length)) {
    303    return false;
    304  }
    305 
    306  return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length) - length);
    307 }
    308 
    309 bool Pickle::IgnoreBytes(PickleIterator* iter, uint32_t length) const {
    310  if (AlignInt(length) < length) {
    311    return false;
    312  }
    313 
    314  return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length));
    315 }
    316 
    317 #ifdef MOZ_PICKLE_SENTINEL_CHECKING
    318 MOZ_NEVER_INLINE
    319 bool Pickle::ReadSentinel(PickleIterator* iter, uint32_t sentinel) const {
    320  uint32_t found;
    321  if (!ReadScalar(iter, &found)) {
    322    return false;
    323  }
    324  return found == sentinel;
    325 }
    326 
    327 bool Pickle::IgnoreSentinel(PickleIterator* iter) const {
    328  uint32_t found;
    329  return ReadUInt32(iter, &found);
    330 }
    331 
    332 bool Pickle::WriteSentinel(uint32_t sentinel) { return WriteUInt32(sentinel); }
    333 #endif
    334 
    335 void Pickle::EndRead(PickleIterator& iter, uint32_t ipcMsgType) const {
    336  // FIXME: Deal with the footer somehow...
    337  // DCHECK(iter.iter_.Done());
    338 }
    339 
    340 void Pickle::Truncate(PickleIterator* iter) {
    341  size_t dropped = buffers_.Truncate(iter->iter_);
    342  header_->payload_size -= dropped;
    343 }
    344 
    345 static const char kBytePaddingData[4] = {
    346    kBytePaddingMarker,
    347    kBytePaddingMarker,
    348    kBytePaddingMarker,
    349    kBytePaddingMarker,
    350 };
    351 
    352 static void WritePadding(Pickle::BufferList& buffers, uint32_t padding) {
    353  MOZ_RELEASE_ASSERT(padding <= 4);
    354  if (padding) {
    355    MOZ_ALWAYS_TRUE(buffers.WriteBytes(kBytePaddingData, padding));
    356  }
    357 }
    358 
    359 void Pickle::BeginWrite(uint32_t length) {
    360  // write at an alignment-aligned offset from the beginning of the header
    361  uint32_t offset = AlignInt(header_->payload_size);
    362  uint32_t padding = (header_size_ + offset) % sizeof(memberAlignmentType);
    363  uint32_t new_size = offset + padding + AlignInt(length);
    364  MOZ_RELEASE_ASSERT(new_size >= header_->payload_size);
    365 
    366  DCHECK(intptr_t(header_) % sizeof(memberAlignmentType) == 0);
    367 
    368 #ifdef HAVE_64BIT_BUILD
    369  DCHECK_LE(length, std::numeric_limits<uint32_t>::max());
    370 #endif
    371 
    372  WritePadding(buffers_, padding);
    373 
    374  DCHECK((header_size_ + header_->payload_size + padding) %
    375             sizeof(memberAlignmentType) ==
    376         0);
    377 
    378  header_->payload_size = new_size;
    379 }
    380 
    381 void Pickle::EndWrite(uint32_t length) {
    382  uint32_t padding = AlignInt(length) - length;
    383  WritePadding(buffers_, padding);
    384 }
    385 
    386 bool Pickle::WriteBool(bool value) { return WriteInt(value ? 1 : 0); }
    387 
    388 bool Pickle::WriteInt16(int16_t value) {
    389  return WriteBytes(&value, sizeof(value));
    390 }
    391 
    392 bool Pickle::WriteUInt16(uint16_t value) {
    393  return WriteBytes(&value, sizeof(value));
    394 }
    395 
    396 bool Pickle::WriteInt(int value) { return WriteBytes(&value, sizeof(value)); }
    397 
    398 bool Pickle::WriteLong(long value) {
    399  // Always written as a 64-bit value since the size for this type can
    400  // differ between architectures.
    401  return WriteInt64(int64_t(value));
    402 }
    403 
    404 bool Pickle::WriteULong(unsigned long value) {
    405  // Always written as a 64-bit value since the size for this type can
    406  // differ between architectures.
    407  return WriteUInt64(uint64_t(value));
    408 }
    409 
    410 bool Pickle::WriteInt32(int32_t value) {
    411  return WriteBytes(&value, sizeof(value));
    412 }
    413 
    414 bool Pickle::WriteUInt32(uint32_t value) {
    415  return WriteBytes(&value, sizeof(value));
    416 }
    417 
    418 bool Pickle::WriteInt64(int64_t value) {
    419  return WriteBytes(&value, sizeof(value));
    420 }
    421 
    422 bool Pickle::WriteUInt64(uint64_t value) {
    423  return WriteBytes(&value, sizeof(value));
    424 }
    425 
    426 bool Pickle::WriteDouble(double value) {
    427  return WriteBytes(&value, sizeof(value));
    428 }
    429 
    430 bool Pickle::WriteIntPtr(intptr_t value) {
    431  // Always written as a 64-bit value since the size for this type can
    432  // differ between architectures.
    433  return WriteInt64(int64_t(value));
    434 }
    435 
    436 bool Pickle::WriteUnsignedChar(unsigned char value) {
    437  return WriteBytes(&value, sizeof(value));
    438 }
    439 
    440 bool Pickle::WriteBytesZeroCopy(void* data, uint32_t data_len,
    441                                uint32_t capacity) {
    442  BeginWrite(data_len);
    443 
    444  uint32_t new_capacity = AlignInt(capacity);
    445 #ifndef MOZ_MEMORY
    446  if (new_capacity > capacity) {
    447    // If the buffer we were given is not large enough to contain padding
    448    // after the data, reallocate it to make it so. When using jemalloc,
    449    // we're guaranteed the buffer size is going to be at least 4-bytes
    450    // aligned, so we skip realloc altogether. Even with other allocators,
    451    // the realloc is likely not necessary, but we don't take chances.
    452    // At least with ASan, it does matter to realloc to inform ASan we're
    453    // going to use more data from the buffer (and let it actually realloc
    454    // if it needs to).
    455    data = realloc(data, new_capacity);
    456  }
    457 #endif
    458 
    459  // Shouldn't fail, because we're using InfallibleAllocPolicy.
    460  MOZ_ALWAYS_TRUE(buffers_.WriteBytesZeroCopy(reinterpret_cast<char*>(data),
    461                                              data_len, new_capacity));
    462 
    463  EndWrite(data_len);
    464  return true;
    465 }
    466 
    467 bool Pickle::WriteBytes(const void* data, uint32_t data_len) {
    468  BeginWrite(data_len);
    469 
    470  MOZ_ALWAYS_TRUE(
    471      buffers_.WriteBytes(reinterpret_cast<const char*>(data), data_len));
    472 
    473  EndWrite(data_len);
    474  return true;
    475 }
    476 
    477 bool Pickle::WriteString(const std::string& value) {
    478  if (!WriteInt(static_cast<int>(value.size()))) return false;
    479 
    480  return WriteBytes(value.data(), static_cast<int>(value.size()));
    481 }
    482 
    483 bool Pickle::WriteWString(const std::wstring& value) {
    484  if (!WriteInt(static_cast<int>(value.size()))) return false;
    485 
    486  return WriteBytes(value.data(),
    487                    static_cast<int>(value.size() * sizeof(wchar_t)));
    488 }
    489 
    490 bool Pickle::WriteData(const char* data, uint32_t length) {
    491  return WriteInt(length) && WriteBytes(data, length);
    492 }
    493 
    494 void Pickle::InputBytes(const char* data, uint32_t length) {
    495  MOZ_ALWAYS_TRUE(buffers_.WriteBytes(data, length));
    496 }
    497 
    498 int32_t* Pickle::GetInt32PtrForTest(uint32_t offset) {
    499  size_t pos = buffers_.Size() - offset;
    500  BufferList::IterImpl iter(buffers_);
    501  MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(buffers_, pos));
    502  return reinterpret_cast<int32_t*>(iter.Data());
    503 }
    504 
    505 // static
    506 uint32_t Pickle::MessageSize(uint32_t header_size, const char* start,
    507                             const char* end) {
    508  DCHECK(header_size == AlignInt(header_size));
    509  DCHECK(header_size <=
    510         static_cast<memberAlignmentType>(kHeaderSegmentCapacity));
    511 
    512  if (end < start) return 0;
    513  size_t length = static_cast<size_t>(end - start);
    514  if (length < sizeof(Header)) return 0;
    515 
    516  const Header* hdr = reinterpret_cast<const Header*>(start);
    517  if (length < header_size) return 0;
    518 
    519  mozilla::CheckedInt<uint32_t> sum(header_size);
    520  sum += hdr->payload_size;
    521 
    522  if (!sum.isValid()) return 0;
    523 
    524  return sum.value();
    525 }