summaryrefslogtreecommitdiffstats
path: root/ipc/chromium/src/base/pickle.cc
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/chromium/src/base/pickle.cc')
-rw-r--r--ipc/chromium/src/base/pickle.cc619
1 files changed, 619 insertions, 0 deletions
diff --git a/ipc/chromium/src/base/pickle.cc b/ipc/chromium/src/base/pickle.cc
new file mode 100644
index 0000000000..e2a1be77b5
--- /dev/null
+++ b/ipc/chromium/src/base/pickle.cc
@@ -0,0 +1,619 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/pickle.h"
+
+#include "mozilla/Alignment.h"
+#include "mozilla/CheckedInt.h"
+#include "mozilla/EndianUtils.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+
+#include <stdlib.h>
+
+#include <limits>
+#include <string>
+#include <algorithm>
+#include <type_traits>
+
+#include "nsDebug.h"
+
+//------------------------------------------------------------------------------
+
+static_assert(MOZ_ALIGNOF(Pickle::memberAlignmentType) >= MOZ_ALIGNOF(uint32_t),
+ "Insufficient alignment");
+
+static const uint32_t kHeaderSegmentCapacity = 64;
+
+static const uint32_t kDefaultSegmentCapacity = 4096;
+
+static const char kBytePaddingMarker = char(0xbf);
+
+namespace {
+
+// We want to copy data to our payload as efficiently as possible.
+// memcpy fits the bill for copying, but not all compilers or
+// architectures support inlining memcpy from void*, which has unknown
+// static alignment. However, we know that all the members of our
+// payload will be aligned on memberAlignmentType boundaries. We
+// therefore use that knowledge to construct a copier that will copy
+// efficiently (via standard C++ assignment mechanisms) if the datatype
+// needs that alignment or less, and memcpy otherwise. (The compiler
+// may still inline memcpy, of course.)
+
+template <typename T, size_t size, bool hasSufficientAlignment>
+struct Copier {
+ static void Copy(T* dest, const char* iter) { memcpy(dest, iter, sizeof(T)); }
+};
+
+// Copying 64-bit quantities happens often enough and can easily be made
+// worthwhile on 32-bit platforms, so handle it specially. Only do it
+// if 64-bit types aren't sufficiently aligned; the alignment
+// requirements for them vary between 32-bit platforms.
+#ifndef HAVE_64BIT_BUILD
+template <typename T>
+struct Copier<T, sizeof(uint64_t), false> {
+ static void Copy(T* dest, const char* iter) {
+# if MOZ_LITTLE_ENDIAN
+ static const int loIndex = 0, hiIndex = 1;
+# else
+ static const int loIndex = 1, hiIndex = 0;
+# endif
+ static_assert(MOZ_ALIGNOF(uint32_t*) == MOZ_ALIGNOF(void*),
+ "Pointers have different alignments");
+ const uint32_t* src = reinterpret_cast<const uint32_t*>(iter);
+ uint32_t* uint32dest = reinterpret_cast<uint32_t*>(dest);
+ uint32dest[loIndex] = src[loIndex];
+ uint32dest[hiIndex] = src[hiIndex];
+ }
+};
+#endif
+
+template <typename T, size_t size>
+struct Copier<T, size, true> {
+ static void Copy(T* dest, const char* iter) {
+ // The pointer ought to be properly aligned.
+ DCHECK_EQ((((uintptr_t)iter) & (MOZ_ALIGNOF(T) - 1)), 0);
+ *dest = *reinterpret_cast<const T*>(iter);
+ }
+};
+
+} // anonymous namespace
+
+PickleIterator::PickleIterator(const Pickle& pickle)
+ : iter_(pickle.buffers_.Iter()) {
+ iter_.Advance(pickle.buffers_, pickle.header_size_);
+}
+
+template <typename T>
+void PickleIterator::CopyInto(T* dest) {
+ static_assert(std::is_trivially_copyable<T>::value,
+ "Copied type must be a POD type");
+ Copier<T, sizeof(T),
+ (MOZ_ALIGNOF(T) <=
+ sizeof(Pickle::memberAlignmentType))>::Copy(dest, iter_.Data());
+}
+
+bool Pickle::IteratorHasRoomFor(const PickleIterator& iter,
+ uint32_t len) const {
+ // Make sure we don't get into trouble where AlignInt(len) == 0.
+ MOZ_RELEASE_ASSERT(len < 64);
+
+ return iter.iter_.HasRoomFor(AlignInt(len));
+}
+
+bool Pickle::HasBytesAvailable(const PickleIterator* iter, uint32_t len) const {
+ return iter->iter_.HasBytesAvailable(buffers_, len);
+}
+
+void Pickle::UpdateIter(PickleIterator* iter, uint32_t bytes) const {
+ // Make sure we don't get into trouble where AlignInt(bytes) == 0.
+ MOZ_RELEASE_ASSERT(bytes < 64);
+
+ iter->iter_.Advance(buffers_, AlignInt(bytes));
+}
+
+// Payload is sizeof(Pickle::memberAlignmentType) aligned.
+
+Pickle::Pickle(uint32_t header_size, size_t segment_capacity)
+ : buffers_(AlignInt(header_size),
+ segment_capacity ? segment_capacity : kHeaderSegmentCapacity,
+ segment_capacity ? segment_capacity : kDefaultSegmentCapacity),
+ header_(nullptr),
+ header_size_(AlignInt(header_size)) {
+ DCHECK(static_cast<memberAlignmentType>(header_size) >= sizeof(Header));
+ DCHECK(header_size_ <= kHeaderSegmentCapacity);
+ header_ = reinterpret_cast<Header*>(buffers_.Start());
+ header_->payload_size = 0;
+}
+
+Pickle::Pickle(uint32_t header_size, const char* data, uint32_t length)
+ : buffers_(length, AlignCapacity(length), kDefaultSegmentCapacity),
+ header_(nullptr),
+ header_size_(AlignInt(header_size)) {
+ DCHECK(static_cast<memberAlignmentType>(header_size) >= sizeof(Header));
+ DCHECK(header_size <= kHeaderSegmentCapacity);
+ MOZ_RELEASE_ASSERT(header_size <= length);
+
+ header_ = reinterpret_cast<Header*>(buffers_.Start());
+ memcpy(header_, data, length);
+}
+
+Pickle::Pickle(Pickle&& other)
+ : buffers_(std::move(other.buffers_)),
+ header_(other.header_),
+ header_size_(other.header_size_) {
+ other.header_ = nullptr;
+}
+
+Pickle::~Pickle() {}
+
+Pickle& Pickle::operator=(Pickle&& other) {
+ BufferList tmp = std::move(other.buffers_);
+ other.buffers_ = std::move(buffers_);
+ buffers_ = std::move(tmp);
+
+ // std::swap(buffers_, other.buffers_);
+ std::swap(header_, other.header_);
+ std::swap(header_size_, other.header_size_);
+ return *this;
+}
+
+void Pickle::CopyFrom(const Pickle& other) {
+ MOZ_ALWAYS_TRUE(buffers_.CopyFrom(other.buffers_));
+ MOZ_ASSERT(other.header_ ==
+ reinterpret_cast<const Header*>(other.buffers_.Start()));
+
+ header_ = reinterpret_cast<Header*>(buffers_.Start());
+ header_size_ = other.header_size_;
+}
+
+bool Pickle::ReadBool(PickleIterator* iter, bool* result) const {
+ DCHECK(iter);
+
+ int tmp;
+ if (!ReadInt(iter, &tmp)) return false;
+ DCHECK(0 == tmp || 1 == tmp);
+ *result = tmp ? true : false;
+ return true;
+}
+
+bool Pickle::ReadInt16(PickleIterator* iter, int16_t* result) const {
+ DCHECK(iter);
+
+ if (!IteratorHasRoomFor(*iter, sizeof(*result)))
+ return ReadBytesInto(iter, result, sizeof(*result));
+
+ iter->CopyInto(result);
+
+ UpdateIter(iter, sizeof(*result));
+ return true;
+}
+
+bool Pickle::ReadUInt16(PickleIterator* iter, uint16_t* result) const {
+ DCHECK(iter);
+
+ if (!IteratorHasRoomFor(*iter, sizeof(*result)))
+ return ReadBytesInto(iter, result, sizeof(*result));
+
+ iter->CopyInto(result);
+
+ UpdateIter(iter, sizeof(*result));
+ return true;
+}
+
+bool Pickle::ReadInt(PickleIterator* iter, int* result) const {
+ DCHECK(iter);
+
+ if (!IteratorHasRoomFor(*iter, sizeof(*result)))
+ return ReadBytesInto(iter, result, sizeof(*result));
+
+ iter->CopyInto(result);
+
+ UpdateIter(iter, sizeof(*result));
+ return true;
+}
+
+// Always written as a 64-bit value since the size for this type can
+// differ between architectures.
+bool Pickle::ReadLong(PickleIterator* iter, long* result) const {
+ DCHECK(iter);
+
+ int64_t big_result = 0;
+ if (IteratorHasRoomFor(*iter, sizeof(big_result))) {
+ iter->CopyInto(&big_result);
+ UpdateIter(iter, sizeof(big_result));
+ } else {
+ if (!ReadBytesInto(iter, &big_result, sizeof(big_result))) {
+ return false;
+ }
+ }
+ DCHECK(big_result <= LONG_MAX && big_result >= LONG_MIN);
+ *result = static_cast<long>(big_result);
+
+ return true;
+}
+
+// Always written as a 64-bit value since the size for this type can
+// differ between architectures.
+bool Pickle::ReadULong(PickleIterator* iter, unsigned long* result) const {
+ DCHECK(iter);
+
+ uint64_t big_result = 0;
+ if (IteratorHasRoomFor(*iter, sizeof(big_result))) {
+ iter->CopyInto(&big_result);
+ UpdateIter(iter, sizeof(big_result));
+ } else {
+ if (!ReadBytesInto(iter, &big_result, sizeof(big_result))) {
+ return false;
+ }
+ }
+ DCHECK(big_result <= ULONG_MAX);
+ *result = static_cast<unsigned long>(big_result);
+
+ return true;
+}
+
+bool Pickle::ReadLength(PickleIterator* iter, int* result) const {
+ if (!ReadInt(iter, result)) return false;
+ return ((*result) >= 0);
+}
+
+bool Pickle::ReadInt32(PickleIterator* iter, int32_t* result) const {
+ DCHECK(iter);
+
+ if (!IteratorHasRoomFor(*iter, sizeof(*result)))
+ return ReadBytesInto(iter, result, sizeof(*result));
+
+ iter->CopyInto(result);
+
+ UpdateIter(iter, sizeof(*result));
+ return true;
+}
+
+bool Pickle::ReadUInt32(PickleIterator* iter, uint32_t* result) const {
+ DCHECK(iter);
+
+ if (!IteratorHasRoomFor(*iter, sizeof(*result)))
+ return ReadBytesInto(iter, result, sizeof(*result));
+
+ iter->CopyInto(result);
+
+ UpdateIter(iter, sizeof(*result));
+ return true;
+}
+
+bool Pickle::ReadInt64(PickleIterator* iter, int64_t* result) const {
+ DCHECK(iter);
+
+ if (!IteratorHasRoomFor(*iter, sizeof(*result)))
+ return ReadBytesInto(iter, result, sizeof(*result));
+
+ iter->CopyInto(result);
+
+ UpdateIter(iter, sizeof(*result));
+ return true;
+}
+
+bool Pickle::ReadUInt64(PickleIterator* iter, uint64_t* result) const {
+ DCHECK(iter);
+
+ if (!IteratorHasRoomFor(*iter, sizeof(*result)))
+ return ReadBytesInto(iter, result, sizeof(*result));
+
+ iter->CopyInto(result);
+
+ UpdateIter(iter, sizeof(*result));
+ return true;
+}
+
+bool Pickle::ReadDouble(PickleIterator* iter, double* result) const {
+ DCHECK(iter);
+
+ if (!IteratorHasRoomFor(*iter, sizeof(*result)))
+ return ReadBytesInto(iter, result, sizeof(*result));
+
+ iter->CopyInto(result);
+
+ UpdateIter(iter, sizeof(*result));
+ return true;
+}
+
+// Always written as a 64-bit value since the size for this type can
+// differ between architectures.
+bool Pickle::ReadIntPtr(PickleIterator* iter, intptr_t* result) const {
+ DCHECK(iter);
+
+ int64_t big_result = 0;
+ if (IteratorHasRoomFor(*iter, sizeof(big_result))) {
+ iter->CopyInto(&big_result);
+ UpdateIter(iter, sizeof(big_result));
+ } else {
+ if (!ReadBytesInto(iter, &big_result, sizeof(big_result))) {
+ return false;
+ }
+ }
+
+ DCHECK(big_result <= std::numeric_limits<intptr_t>::max() &&
+ big_result >= std::numeric_limits<intptr_t>::min());
+ *result = static_cast<intptr_t>(big_result);
+
+ return true;
+}
+
+bool Pickle::ReadUnsignedChar(PickleIterator* iter,
+ unsigned char* result) const {
+ DCHECK(iter);
+
+ if (!IteratorHasRoomFor(*iter, sizeof(*result)))
+ return ReadBytesInto(iter, result, sizeof(*result));
+
+ iter->CopyInto(result);
+
+ UpdateIter(iter, sizeof(*result));
+ return true;
+}
+
+bool Pickle::ReadString(PickleIterator* iter, std::string* result) const {
+ DCHECK(iter);
+
+ int len;
+ if (!ReadLength(iter, &len)) return false;
+
+ auto chars = mozilla::MakeUnique<char[]>(len);
+ if (!ReadBytesInto(iter, chars.get(), len)) {
+ return false;
+ }
+ result->assign(chars.get(), len);
+
+ return true;
+}
+
+bool Pickle::ReadWString(PickleIterator* iter, std::wstring* result) const {
+ DCHECK(iter);
+
+ int len;
+ if (!ReadLength(iter, &len)) return false;
+ // Avoid integer multiplication overflow.
+ if (len > INT_MAX / static_cast<int>(sizeof(wchar_t))) return false;
+
+ auto chars = mozilla::MakeUnique<wchar_t[]>(len);
+ if (!ReadBytesInto(iter, chars.get(), len * sizeof(wchar_t))) {
+ return false;
+ }
+ result->assign(chars.get(), len);
+
+ return true;
+}
+
+bool Pickle::ReadBytesInto(PickleIterator* iter, void* data,
+ uint32_t length) const {
+ if (AlignInt(length) < length) {
+ return false;
+ }
+
+ if (!buffers_.ReadBytes(iter->iter_, reinterpret_cast<char*>(data), length)) {
+ return false;
+ }
+
+ return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length) - length);
+}
+
+bool Pickle::IgnoreBytes(PickleIterator* iter, uint32_t length) const {
+ if (AlignInt(length) < length) {
+ return false;
+ }
+
+ return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length));
+}
+
+#ifdef MOZ_PICKLE_SENTINEL_CHECKING
+MOZ_NEVER_INLINE
+bool Pickle::ReadSentinel(PickleIterator* iter, uint32_t sentinel) const {
+ uint32_t found;
+ if (!ReadUInt32(iter, &found)) {
+ return false;
+ }
+ return found == sentinel;
+}
+
+bool Pickle::IgnoreSentinel(PickleIterator* iter) const {
+ uint32_t found;
+ return ReadUInt32(iter, &found);
+}
+
+bool Pickle::WriteSentinel(uint32_t sentinel) { return WriteUInt32(sentinel); }
+#endif
+
+void Pickle::EndRead(PickleIterator& iter, uint32_t ipcMsgType) const {
+ // FIXME: Deal with the footer somehow...
+ // DCHECK(iter.iter_.Done());
+}
+
+void Pickle::Truncate(PickleIterator* iter) {
+ size_t dropped = buffers_.Truncate(iter->iter_);
+ header_->payload_size -= dropped;
+}
+
+static const char kBytePaddingData[4] = {
+ kBytePaddingMarker,
+ kBytePaddingMarker,
+ kBytePaddingMarker,
+ kBytePaddingMarker,
+};
+
+static void WritePadding(Pickle::BufferList& buffers, uint32_t padding) {
+ MOZ_RELEASE_ASSERT(padding <= 4);
+ if (padding) {
+ MOZ_ALWAYS_TRUE(buffers.WriteBytes(kBytePaddingData, padding));
+ }
+}
+
+void Pickle::BeginWrite(uint32_t length) {
+ // write at an alignment-aligned offset from the beginning of the header
+ uint32_t offset = AlignInt(header_->payload_size);
+ uint32_t padding = (header_size_ + offset) % sizeof(memberAlignmentType);
+ uint32_t new_size = offset + padding + AlignInt(length);
+ MOZ_RELEASE_ASSERT(new_size >= header_->payload_size);
+
+ DCHECK(intptr_t(header_) % sizeof(memberAlignmentType) == 0);
+
+#ifdef ARCH_CPU_64_BITS
+ DCHECK_LE(length, std::numeric_limits<uint32_t>::max());
+#endif
+
+ WritePadding(buffers_, padding);
+
+ DCHECK((header_size_ + header_->payload_size + padding) %
+ sizeof(memberAlignmentType) ==
+ 0);
+
+ header_->payload_size = new_size;
+}
+
+void Pickle::EndWrite(uint32_t length) {
+ uint32_t padding = AlignInt(length) - length;
+ WritePadding(buffers_, padding);
+}
+
+bool Pickle::WriteBool(bool value) { return WriteInt(value ? 1 : 0); }
+
+bool Pickle::WriteInt16(int16_t value) {
+ return WriteBytes(&value, sizeof(value));
+}
+
+bool Pickle::WriteUInt16(uint16_t value) {
+ return WriteBytes(&value, sizeof(value));
+}
+
+bool Pickle::WriteInt(int value) { return WriteBytes(&value, sizeof(value)); }
+
+bool Pickle::WriteLong(long value) {
+ // Always written as a 64-bit value since the size for this type can
+ // differ between architectures.
+ return WriteInt64(int64_t(value));
+}
+
+bool Pickle::WriteULong(unsigned long value) {
+ // Always written as a 64-bit value since the size for this type can
+ // differ between architectures.
+ return WriteUInt64(uint64_t(value));
+}
+
+bool Pickle::WriteInt32(int32_t value) {
+ return WriteBytes(&value, sizeof(value));
+}
+
+bool Pickle::WriteUInt32(uint32_t value) {
+ return WriteBytes(&value, sizeof(value));
+}
+
+bool Pickle::WriteInt64(int64_t value) {
+ return WriteBytes(&value, sizeof(value));
+}
+
+bool Pickle::WriteUInt64(uint64_t value) {
+ return WriteBytes(&value, sizeof(value));
+}
+
+bool Pickle::WriteDouble(double value) {
+ return WriteBytes(&value, sizeof(value));
+}
+
+bool Pickle::WriteIntPtr(intptr_t value) {
+ // Always written as a 64-bit value since the size for this type can
+ // differ between architectures.
+ return WriteInt64(int64_t(value));
+}
+
+bool Pickle::WriteUnsignedChar(unsigned char value) {
+ return WriteBytes(&value, sizeof(value));
+}
+
+bool Pickle::WriteBytesZeroCopy(void* data, uint32_t data_len,
+ uint32_t capacity) {
+ BeginWrite(data_len);
+
+ uint32_t new_capacity = AlignInt(capacity);
+#ifndef MOZ_MEMORY
+ if (new_capacity > capacity) {
+ // If the buffer we were given is not large enough to contain padding
+ // after the data, reallocate it to make it so. When using jemalloc,
+ // we're guaranteed the buffer size is going to be at least 4-bytes
+ // aligned, so we skip realloc altogether. Even with other allocators,
+ // the realloc is likely not necessary, but we don't take chances.
+ // At least with ASan, it does matter to realloc to inform ASan we're
+ // going to use more data from the buffer (and let it actually realloc
+ // if it needs to).
+ data = realloc(data, new_capacity);
+ }
+#endif
+ buffers_.WriteBytesZeroCopy(reinterpret_cast<char*>(data), data_len,
+ new_capacity);
+
+ EndWrite(data_len);
+ return true;
+}
+
+bool Pickle::WriteBytes(const void* data, uint32_t data_len) {
+ BeginWrite(data_len);
+
+ MOZ_ALWAYS_TRUE(
+ buffers_.WriteBytes(reinterpret_cast<const char*>(data), data_len));
+
+ EndWrite(data_len);
+ return true;
+}
+
+bool Pickle::WriteString(const std::string& value) {
+ if (!WriteInt(static_cast<int>(value.size()))) return false;
+
+ return WriteBytes(value.data(), static_cast<int>(value.size()));
+}
+
+bool Pickle::WriteWString(const std::wstring& value) {
+ if (!WriteInt(static_cast<int>(value.size()))) return false;
+
+ return WriteBytes(value.data(),
+ static_cast<int>(value.size() * sizeof(wchar_t)));
+}
+
+bool Pickle::WriteData(const char* data, uint32_t length) {
+ return WriteInt(length) && WriteBytes(data, length);
+}
+
+void Pickle::InputBytes(const char* data, uint32_t length) {
+ MOZ_ALWAYS_TRUE(buffers_.WriteBytes(data, length));
+}
+
+int32_t* Pickle::GetInt32PtrForTest(uint32_t offset) {
+ size_t pos = buffers_.Size() - offset;
+ BufferList::IterImpl iter(buffers_);
+ MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(buffers_, pos));
+ return reinterpret_cast<int32_t*>(iter.Data());
+}
+
+// static
+uint32_t Pickle::MessageSize(uint32_t header_size, const char* start,
+ const char* end) {
+ DCHECK(header_size == AlignInt(header_size));
+ DCHECK(header_size <=
+ static_cast<memberAlignmentType>(kHeaderSegmentCapacity));
+
+ if (end < start) return 0;
+ size_t length = static_cast<size_t>(end - start);
+ if (length < sizeof(Header)) return 0;
+
+ const Header* hdr = reinterpret_cast<const Header*>(start);
+ if (length < header_size) return 0;
+
+ mozilla::CheckedInt<uint32_t> sum(header_size);
+ sum += hdr->payload_size;
+
+ if (!sum.isValid()) return 0;
+
+ return sum.value();
+}