import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1262671 - IPC sentinel checking (r=froydnj) (2bd39988fe)
- Bug 1262671 - IPC ReadData/ReadBytes elimination (r=froydnj) (85b47baffd)
- Bug 1272415: Don't include task.h everywhere. r=froydnj (6198aedfed)
- Bug 1273312 - Add task.h to ipdl unit tests (r=khuey) (42b8d3bb90)
- Bug 1268616 - Part 1: Check max message size before resizing. r=billm (6889a43e16)
- Bug 1268616 - Part 2: Stop sending messages that are too large. r=billm (a68e6624be)
- Bug 1268616 - Part 3: Reduce the maxmimum IPC message size. r=billm (581076632e)
- Bug 1262671 - Use BufferList for Pickle (r=froydnj) (f61d8b233d)
- Bug 1262671 - Introduce MFBT BufferList class (r=froydnj) (cb1aca1708)
- mfbt: BufferList: VC2013 fix (93da0d98f7)
This commit is contained in:
2024-10-08 22:16:10 +08:00
parent f632bc6ab3
commit 1beb91ad2b
58 changed files with 1449 additions and 1146 deletions
+1
View File
@@ -11,6 +11,7 @@
#include "mozilla/dom/ipc/BlobChild.h"
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "base/task.h"
using namespace mozilla::ipc;
using namespace mozilla::jsipc;
+1
View File
@@ -9,6 +9,7 @@
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "nsXULAppAPI.h"
#include "nsIObserverService.h"
#include "base/task.h"
using namespace mozilla::ipc;
using namespace mozilla::jsipc;
+6 -11
View File
@@ -98,8 +98,7 @@ StructuredCloneData::WriteIPCParams(IPC::Message* aMsg) const
WriteParam(aMsg, DataLength());
if (DataLength()) {
// Structured clone data must be 64-bit aligned.
aMsg->WriteBytes(Data(), DataLength(), sizeof(uint64_t));
aMsg->WriteBytes(Data(), DataLength());
}
}
@@ -118,18 +117,14 @@ StructuredCloneData::ReadIPCParams(const IPC::Message* aMsg,
return true;
}
uint64_t* dataBuffer = nullptr;
const char** buffer =
const_cast<const char**>(reinterpret_cast<char**>(&dataBuffer));
// Structured clone data must be 64-bit aligned.
if (!aMsg->ReadBytes(aIter, buffer, dataLength, sizeof(uint64_t))) {
mSharedData = SharedJSAllocatedData::AllocateForExternalData(dataLength);
NS_ENSURE_TRUE(mSharedData, false);
if (!aMsg->ReadBytesInto(aIter, mSharedData->Data(), dataLength)) {
mSharedData = nullptr;
return false;
}
mSharedData = SharedJSAllocatedData::CreateFromExternalData(dataBuffer,
dataLength);
NS_ENSURE_TRUE(mSharedData, false);
return true;
}
+10 -2
View File
@@ -31,19 +31,27 @@ public:
}
static already_AddRefed<SharedJSAllocatedData>
CreateFromExternalData(const void* aData, size_t aDataLength)
AllocateForExternalData(size_t aDataLength)
{
uint64_t* data = Allocate64bitSafely(aDataLength);
if (!data) {
return nullptr;
}
memcpy(data, aData, aDataLength);
RefPtr<SharedJSAllocatedData> sharedData =
new SharedJSAllocatedData(data, aDataLength);
return sharedData.forget();
}
static already_AddRefed<SharedJSAllocatedData>
CreateFromExternalData(const void* aData, size_t aDataLength)
{
RefPtr<SharedJSAllocatedData> sharedData =
AllocateForExternalData(aDataLength);
memcpy(sharedData->Data(), aData, aDataLength);
return sharedData.forget();
}
NS_INLINE_DECL_REFCOUNTING(SharedJSAllocatedData)
uint64_t* Data() const { return mData; }
+1
View File
@@ -26,6 +26,7 @@
#include <fstream>
#include "GMPUtils.h"
#include "prio.h"
#include "base/task.h"
#ifdef MOZ_WIDEVINE_EME
#include "widevine-adapter/WidevineAdapter.h"
#endif
+1
View File
@@ -9,6 +9,7 @@
#include "GMPDecryptorChild.h"
#include "GMPVideoDecoderChild.h"
#include "GMPVideoEncoderChild.h"
#include "base/task.h"
namespace mozilla {
namespace gmp {
+1
View File
@@ -13,6 +13,7 @@
#include "mozIGeckoMediaPluginService.h"
#include "mozilla/Logging.h"
#include "mozilla/unused.h"
#include "base/task.h"
namespace mozilla {
+1
View File
@@ -6,6 +6,7 @@
#include "GMPDecryptorChild.h"
#include "GMPContentChild.h"
#include "GMPChild.h"
#include "base/task.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/unused.h"
#include "runnable_utils.h"
+1
View File
@@ -13,6 +13,7 @@
#include "nsXPCOMPrivate.h"
#include "mozilla/SyncRunnable.h"
#include "runnable_utils.h"
#include "base/task.h"
namespace mozilla {
+1
View File
@@ -6,6 +6,7 @@
#include "GMPServiceParent.h"
#include "GMPService.h"
#include "prio.h"
#include "base/task.h"
#include "mozilla/Logging.h"
#include "GMPParent.h"
#include "GMPVideoDecoderParent.h"
+1
View File
@@ -6,6 +6,7 @@
#include "GMPStorageChild.h"
#include "GMPChild.h"
#include "gmp-storage.h"
#include "base/task.h"
#define ON_GMP_THREAD() (mPlugin->GMPMessageLoop() == MessageLoop::current())
+2
View File
@@ -8,6 +8,8 @@
#include "mozilla/plugins/PBrowserStreamChild.h"
#include "mozilla/plugins/AStream.h"
#include "base/task.h"
#include "base/timer.h"
namespace mozilla {
namespace plugins {
+1 -8
View File
@@ -39,14 +39,7 @@ struct ParamTraits<mozilla::plugins::NPRemoteEvent>
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
{
const char* bytes = 0;
if (!aMsg->ReadBytes(aIter, &bytes, sizeof(paramType))) {
return false;
}
memcpy(aResult, bytes, sizeof(paramType));
return true;
return aMsg->ReadBytesInto(aIter, aResult, sizeof(paramType));
}
static void Log(const paramType& aParam, std::wstring* aLog)
+1 -5
View File
@@ -57,14 +57,10 @@ struct ParamTraits<mozilla::plugins::NPRemoteEvent> // synonym for XEvent
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
{
const char* bytes = 0;
if (!aMsg->ReadBytes(aIter, &bytes, sizeof(paramType))) {
if (!aMsg->ReadBytesInto(aIter, aResult, sizeof(paramType))) {
return false;
}
memcpy(aResult, bytes, sizeof(paramType));
#ifdef MOZ_X11
SetXDisplay(aResult->event);
#endif
+1 -4
View File
@@ -132,12 +132,9 @@ struct ParamTraits<mozilla::plugins::NPRemoteEvent>
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
{
const char* bytes = 0;
if (!aMsg->ReadBytes(aIter, &bytes, sizeof(paramType))) {
if (!aMsg->ReadBytesInto(aIter, aResult, sizeof(paramType))) {
return false;
}
memcpy(aResult, bytes, sizeof(paramType));
if (aResult->event.event == WM_PAINT) {
// restore the lParam to point at the RECT
+11 -7
View File
@@ -459,15 +459,19 @@ struct ParamTraits<NPNSString*>
return false;
}
UniChar* buffer = nullptr;
// Avoid integer multiplication overflow.
if (length > INT_MAX / static_cast<long>(sizeof(UniChar))) {
return false;
}
auto chars = mozilla::MakeUnique<UniChar[]>(length);
if (length != 0) {
if (!aMsg->ReadBytes(aIter, (const char**)&buffer, length * sizeof(UniChar)) ||
!buffer) {
if (!aMsg->ReadBytesInto(aIter, chars.get(), length * sizeof(UniChar))) {
return false;
}
}
*aResult = (NPNSString*)::CFStringCreateWithBytes(kCFAllocatorDefault, (UInt8*)buffer,
*aResult = (NPNSString*)::CFStringCreateWithBytes(kCFAllocatorDefault, (UInt8*)chars.get(),
length * sizeof(UniChar),
kCFStringEncodingUTF16, false);
if (!*aResult) {
@@ -525,16 +529,16 @@ struct ParamTraits<NSCursorInfo>
return false;
}
uint8_t* data = nullptr;
auto data = mozilla::MakeUnique<uint8_t[]>(dataLength);
if (dataLength != 0) {
if (!aMsg->ReadBytes(aIter, (const char**)&data, dataLength) || !data) {
if (!aMsg->ReadBytesInto(aIter, data.get(), dataLength)) {
return false;
}
}
aResult->SetType(type);
aResult->SetHotSpot(nsPoint(hotSpotX, hotSpotY));
aResult->SetCustomImageData(data, dataLength);
aResult->SetCustomImageData(data.get(), dataLength);
return true;
}
+1 -3
View File
@@ -56,11 +56,9 @@ bool
ParamTraits<DxgiAdapterDesc>::Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
{
#if defined(XP_WIN)
const char* description = nullptr;
if (!aMsg->ReadBytes(aIter, &description, sizeof(aResult->Description))) {
if (!aMsg->ReadBytesInto(aIter, aResult->Description, sizeof(aResult->Description))) {
return false;
}
memcpy(aResult->Description, description, sizeof(aResult->Description));
if (ReadParam(aMsg, aIter, &aResult->VendorId) &&
ReadParam(aMsg, aIter, &aResult->DeviceId) &&
-1
View File
@@ -9,7 +9,6 @@ include(libevent_path_prefix + '/libeventcommon.mozbuild')
UNIFIED_SOURCES += [
'src/base/at_exit.cc',
'src/base/buffer.cc',
'src/base/command_line.cc',
'src/base/file_path.cc',
'src/base/file_util.cc',
-130
View File
@@ -1,130 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "buffer.h"
#include "nsDebug.h"
Buffer::Buffer()
: mBuffer(nullptr),
mSize(0),
mReserved(0)
{
}
Buffer::~Buffer()
{
if (mBuffer) {
free(mBuffer);
}
}
bool
Buffer::empty() const
{
return mSize == 0;
}
size_t
Buffer::size() const
{
return mSize;
}
const char*
Buffer::data() const
{
return mBuffer;
}
void
Buffer::clear()
{
free(mBuffer);
mBuffer = nullptr;
mSize = 0;
mReserved = 0;
}
void
Buffer::try_realloc(size_t newlength)
{
char* buffer = (char*)realloc(mBuffer, newlength);
if (buffer || !newlength) {
mBuffer = buffer;
mReserved = newlength;
return;
}
// If we're growing the buffer, crash. If we're shrinking, then we continue to
// use the old (larger) buffer.
if (newlength > mReserved) {
NS_ABORT_OOM(newlength);
}
}
void
Buffer::append(const char* bytes, size_t length)
{
if (mSize + length > mReserved) {
try_realloc(mSize + length);
}
memcpy(mBuffer + mSize, bytes, length);
mSize += length;
}
void
Buffer::assign(const char* bytes, size_t length)
{
if (bytes >= mBuffer && bytes < mBuffer + mReserved) {
MOZ_RELEASE_ASSERT(bytes + length <= mBuffer + mSize);
memmove(mBuffer, bytes, length);
mSize = length;
try_realloc(length);
} else {
try_realloc(length);
mSize = length;
memcpy(mBuffer, bytes, length);
}
}
void
Buffer::erase(size_t start, size_t count)
{
mSize -= count;
memmove(mBuffer + start, mBuffer + start + count, mSize - start);
try_realloc(mSize);
}
void
Buffer::reserve(size_t size)
{
if (mReserved < size) {
try_realloc(size);
}
}
char*
Buffer::trade_bytes(size_t count)
{
MOZ_RELEASE_ASSERT(count);
char* result = mBuffer;
mSize = mReserved = mSize - count;
mBuffer = mReserved ? (char*)malloc(mReserved) : nullptr;
MOZ_RELEASE_ASSERT(!mReserved || mBuffer);
if (mSize) {
memcpy(mBuffer, result + count, mSize);
}
// Try to resize the buffer down, but ignore failure. This can cause extra
// copies, but so be it.
char* resized = (char*)realloc(result, count);
if (resized) {
return resized;
}
return result;
}
-44
View File
@@ -1,44 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef CHROME_BASE_BUFFER_H_
#define CHROME_BASE_BUFFER_H_
// Buffer is a simple std::string-like class for buffering up IPC messages. Its
// main distinguishing characteristic is the trade_bytes function.
class Buffer {
public:
Buffer();
~Buffer();
bool empty() const;
const char* data() const;
size_t size() const;
void clear();
void append(const char* bytes, size_t length);
void assign(const char* bytes, size_t length);
void erase(size_t start, size_t count);
void reserve(size_t size);
// This function should be used by a caller who wants to extract the first
// |count| bytes from the buffer. Rather than copying the bytes out, this
// function returns the entire buffer. The bytes in range [count, size()) are
// copied out to a new buffer which becomes the current buffer. The
// presumption is that |count| is very large and approximately equal to size()
// so not much needs to be copied.
char* trade_bytes(size_t count);
private:
void try_realloc(size_t newlength);
char* mBuffer;
size_t mSize;
size_t mReserved;
};
#endif // CHROME_BASE_BUFFER_H_
-135
View File
@@ -240,99 +240,6 @@ void Histogram::WriteAscii(bool graph_it, const std::string& newline,
DCHECK_EQ(sample_count, past);
}
// static
std::string Histogram::SerializeHistogramInfo(const Histogram& histogram,
const SampleSet& snapshot) {
DCHECK_NE(NOT_VALID_IN_RENDERER, histogram.histogram_type());
Pickle pickle;
pickle.WriteString(histogram.histogram_name());
pickle.WriteInt(histogram.declared_min());
pickle.WriteInt(histogram.declared_max());
pickle.WriteSize(histogram.bucket_count());
pickle.WriteUInt32(histogram.range_checksum());
pickle.WriteInt(histogram.histogram_type());
pickle.WriteInt(histogram.flags());
snapshot.Serialize(&pickle);
return std::string(static_cast<const char*>(pickle.data()), pickle.size());
}
// static
bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) {
if (histogram_info.empty()) {
return false;
}
Pickle pickle(histogram_info.data(),
static_cast<int>(histogram_info.size()));
std::string histogram_name;
int declared_min;
int declared_max;
size_t bucket_count;
uint32_t range_checksum;
int histogram_type;
int pickle_flags;
SampleSet sample;
PickleIterator iter(pickle);
if (!pickle.ReadString(&iter, &histogram_name) ||
!pickle.ReadInt(&iter, &declared_min) ||
!pickle.ReadInt(&iter, &declared_max) ||
!pickle.ReadSize(&iter, &bucket_count) ||
!pickle.ReadUInt32(&iter, &range_checksum) ||
!pickle.ReadInt(&iter, &histogram_type) ||
!pickle.ReadInt(&iter, &pickle_flags) ||
!sample.Histogram::SampleSet::Deserialize(&iter, pickle)) {
CHROMIUM_LOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name;
return false;
}
DCHECK(pickle_flags & kIPCSerializationSourceFlag);
// Since these fields may have come from an untrusted renderer, do additional
// checks above and beyond those in Histogram::Initialize()
if (declared_max <= 0 || declared_min <= 0 || declared_max < declared_min ||
INT_MAX / sizeof(Count) <= bucket_count || bucket_count < 2) {
CHROMIUM_LOG(ERROR) << "Values error decoding Histogram: " << histogram_name;
return false;
}
Flags flags = static_cast<Flags>(pickle_flags & ~kIPCSerializationSourceFlag);
DCHECK_NE(NOT_VALID_IN_RENDERER, histogram_type);
Histogram* render_histogram(NULL);
if (histogram_type == HISTOGRAM) {
render_histogram = Histogram::FactoryGet(
histogram_name, declared_min, declared_max, bucket_count, flags);
} else if (histogram_type == LINEAR_HISTOGRAM) {
render_histogram = LinearHistogram::FactoryGet(
histogram_name, declared_min, declared_max, bucket_count, flags);
} else if (histogram_type == BOOLEAN_HISTOGRAM) {
render_histogram = BooleanHistogram::FactoryGet(histogram_name, flags);
} else {
CHROMIUM_LOG(ERROR) << "Error Deserializing Histogram Unknown histogram_type: "
<< histogram_type;
return false;
}
DCHECK_EQ(render_histogram->declared_min(), declared_min);
DCHECK_EQ(render_histogram->declared_max(), declared_max);
DCHECK_EQ(render_histogram->bucket_count(), bucket_count);
DCHECK_EQ(render_histogram->range_checksum(), range_checksum);
DCHECK_EQ(render_histogram->histogram_type(), histogram_type);
if (render_histogram->flags() & kIPCSerializationSourceFlag) {
DVLOG(1) << "Single process mode, histogram observed and not copied: "
<< histogram_name;
} else {
DCHECK_EQ(flags & render_histogram->flags(), flags);
render_histogram->AddSampleSet(sample);
}
return true;
}
//------------------------------------------------------------------------------
// Methods for the validating a sample and a related histogram.
//------------------------------------------------------------------------------
@@ -773,48 +680,6 @@ void Histogram::SampleSet::Add(const SampleSet& other) {
counts_[index] += other.counts_[index];
}
bool Histogram::SampleSet::Serialize(Pickle* pickle) const {
OffTheBooksMutexAutoLock locker(mutex_);
pickle->WriteInt64(sum_);
pickle->WriteInt64(redundant_count_);
pickle->WriteSize(counts_.size());
for (size_t index = 0; index < counts_.size(); ++index) {
pickle->WriteInt(counts_[index]);
}
return true;
}
bool Histogram::SampleSet::Deserialize(PickleIterator* iter, const Pickle& pickle) {
OffTheBooksMutexAutoLock locker(mutex_);
DCHECK_EQ(counts_.size(), 0u);
DCHECK_EQ(sum_, 0);
DCHECK_EQ(redundant_count_, 0);
size_t counts_size;
if (!pickle.ReadInt64(iter, &sum_) ||
!pickle.ReadInt64(iter, &redundant_count_) ||
!pickle.ReadSize(iter, &counts_size)) {
return false;
}
if (counts_size == 0)
return false;
int count = 0;
for (size_t index = 0; index < counts_size; ++index) {
int i;
if (!pickle.ReadInt(iter, &i))
return false;
counts_.push_back(i);
count += i;
}
return true;
}
//------------------------------------------------------------------------------
// LinearHistogram: This histogram uses a traditional set of evenly spaced
// buckets.
-28
View File
@@ -54,9 +54,6 @@
#include "base/time.h"
#include "base/lock.h"
class Pickle;
class PickleIterator;
namespace base {
using mozilla::OffTheBooksMutex;
@@ -302,13 +299,6 @@ class Histogram {
kNoFlags = 0,
kUmaTargetedHistogramFlag = 0x1, // Histogram should be UMA uploaded.
// Indicate that the histogram was pickled to be sent across an IPC Channel.
// If we observe this flag on a histogram being aggregated into after IPC,
// then we are running in a single process mode, and the aggregation should
// not take place (as we would be aggregating back into the source
// histogram!).
kIPCSerializationSourceFlag = 0x10,
kHexRangePrintingFlag = 0x8000 // Fancy bucket-naming supported.
};
@@ -361,9 +351,6 @@ class Histogram {
// Arithmetic manipulation of corresponding elements of the set.
void Add(const SampleSet& other);
bool Serialize(Pickle* pickle) const;
bool Deserialize(PickleIterator* iter, const Pickle& pickle);
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
//---------------- THREAD UNSAFE METHODS ----------------//
@@ -474,21 +461,6 @@ class Histogram {
void ClearFlags(Flags flags) { flags_ = static_cast<Flags>(flags_ & ~flags); }
int flags() const { return flags_; }
// Convenience methods for serializing/deserializing the histograms.
// Histograms from Renderer process are serialized and sent to the browser.
// Browser process reconstructs the histogram from the pickled version
// accumulates the browser-side shadow copy of histograms (that mirror
// histograms created in the renderer).
// Serialize the given snapshot of a Histogram into a String. Uses
// Pickle class to flatten the object.
static std::string SerializeHistogramInfo(const Histogram& histogram,
const SampleSet& snapshot);
// The following method accepts a list of pickled histograms and
// builds a histogram and updates shadow copy of histogram data in the
// browser process.
static bool DeserializeHistogramInfo(const std::string& histogram_info);
// Check to see if bucket ranges, counts and tallies in the snapshot are
// consistent with the bucket ranges and checksums in our histogram. This can
// produce a false-alarm if a race occurred in the reading of the data during
+1 -2
View File
@@ -16,8 +16,6 @@
#include "base/lock.h"
#include "base/message_pump.h"
#include "base/observer_list.h"
#include "base/task.h"
#include "base/timer.h"
#if defined(OS_WIN)
// We need this to declare base::MessagePumpWin::Dispatcher, which we should
@@ -28,6 +26,7 @@
#endif
#include "nsAutoPtr.h"
#include "nsThreadUtils.h"
class nsIThread;
+217 -242
View File
@@ -7,6 +7,7 @@
#include "base/pickle.h"
#include "mozilla/Alignment.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/Endian.h"
#include "mozilla/TypeTraits.h"
@@ -18,18 +19,18 @@
#include "nsDebug.h"
#if !defined(RELEASE_BUILD) || defined(DEBUG)
#define SENTINEL_CHECKING
#endif
//------------------------------------------------------------------------------
static_assert(MOZ_ALIGNOF(Pickle::memberAlignmentType) >= MOZ_ALIGNOF(uint32_t),
"Insufficient alignment");
// static
const int Pickle::kPayloadUnit = 64;
static const uint32_t kHeaderSegmentCapacity = 64;
const uint32_t kFastGrowthCap = 128 * 1024;
// We mark a read only pickle with a special capacity_.
static const uint32_t kCapacityReadOnly = (uint32_t) -1;
static const uint32_t kDefaultSegmentCapacity = 4096;
static const char kBytePaddingMarker = char(0xbf);
@@ -48,8 +49,8 @@ namespace {
template<typename T, size_t size, bool hasSufficientAlignment>
struct Copier
{
static void Copy(T* dest, void** iter) {
memcpy(dest, *iter, sizeof(T));
static void Copy(T* dest, const char* iter) {
memcpy(dest, iter, sizeof(T));
}
};
@@ -61,7 +62,7 @@ struct Copier
template<typename T>
struct Copier<T, sizeof(uint64_t), false>
{
static void Copy(T* dest, void** iter) {
static void Copy(T* dest, const char* iter) {
#if MOZ_LITTLE_ENDIAN
static const int loIndex = 0, hiIndex = 1;
#else
@@ -69,7 +70,7 @@ struct Copier<T, sizeof(uint64_t), false>
#endif
static_assert(MOZ_ALIGNOF(uint32_t*) == MOZ_ALIGNOF(void*),
"Pointers have different alignments");
uint32_t* src = *reinterpret_cast<uint32_t**>(iter);
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];
@@ -80,96 +81,81 @@ struct Copier<T, sizeof(uint64_t), false>
template<typename T, size_t size>
struct Copier<T, size, true>
{
static void Copy(T* dest, void** iter) {
// The reinterpret_cast is only safe if two conditions hold:
// (1) If the alignment of T* is the same as void*;
// (2) The alignment of the data in *iter is at least as
// big as MOZ_ALIGNOF(T).
// Check the first condition, as the second condition is already
// known to be true, or we wouldn't be here.
static_assert(MOZ_ALIGNOF(T*) == MOZ_ALIGNOF(void*),
"Pointers have different alignments");
*dest = *(*reinterpret_cast<T**>(iter));
static void Copy(T* dest, const char* iter) {
*dest = *reinterpret_cast<const T*>(iter);
}
};
} // anonymous namespace
template<typename T>
void
PickleIterator::CopyFrom(T* dest) {
static_assert(mozilla::IsPod<T>::value, "Copied type must be a POD type");
Copier<T, sizeof(T), (MOZ_ALIGNOF(T) <= sizeof(Pickle::memberAlignmentType))>::Copy(dest, &iter_);
PickleIterator::PickleIterator(const Pickle& pickle)
: iter_(pickle.buffers_.Iter()) {
iter_.Advance(pickle.buffers_, pickle.header_size_);
}
PickleIterator::PickleIterator(const Pickle& pickle)
: iter_(const_cast<char*>(pickle.payload())) {
template<typename T>
void
PickleIterator::CopyInto(T* dest) {
static_assert(mozilla::IsPod<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));
}
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()
: header_(NULL),
header_size_(sizeof(Header)),
capacity_(0),
variable_buffer_offset_(0) {
Resize(kPayloadUnit);
header_->payload_size = 0;
}
Pickle::Pickle(int header_size)
: header_(NULL),
header_size_(AlignInt(header_size)),
capacity_(0),
variable_buffer_offset_(0) {
Pickle::Pickle(uint32_t header_size)
: buffers_(AlignInt(header_size), kHeaderSegmentCapacity, kDefaultSegmentCapacity),
header_(nullptr),
header_size_(AlignInt(header_size)) {
DCHECK(static_cast<memberAlignmentType>(header_size) >= sizeof(Header));
DCHECK(header_size <= kPayloadUnit);
Resize(kPayloadUnit);
if (!header_) {
NS_ABORT_OOM(kPayloadUnit);
}
DCHECK(header_size_ <= kHeaderSegmentCapacity);
header_ = reinterpret_cast<Header*>(buffers_.Start());
header_->payload_size = 0;
}
Pickle::Pickle(const char* data, int data_len, Ownership ownership)
: header_(reinterpret_cast<Header*>(const_cast<char*>(data))),
header_size_(0),
capacity_(ownership == BORROWS ? kCapacityReadOnly : data_len),
variable_buffer_offset_(0) {
if (data_len >= static_cast<int>(sizeof(Header)))
header_size_ = data_len - header_->payload_size;
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);
if (header_size_ > static_cast<unsigned int>(data_len))
header_size_ = 0;
if (header_size_ != AlignInt(header_size_))
header_size_ = 0;
// If there is anything wrong with the data, we're not going to use it.
if (!header_size_)
header_ = nullptr;
header_ = reinterpret_cast<Header*>(buffers_.Start());
memcpy(header_, data, length);
}
Pickle::Pickle(Pickle&& other)
: header_(other.header_),
header_size_(other.header_size_),
capacity_(other.capacity_),
variable_buffer_offset_(other.variable_buffer_offset_) {
other.header_ = NULL;
other.capacity_ = 0;
other.variable_buffer_offset_ = 0;
: buffers_(mozilla::Move(other.buffers_)),
header_(other.header_),
header_size_(other.header_size_) {
other.header_ = nullptr;
}
Pickle::~Pickle() {
if (capacity_ != kCapacityReadOnly)
free(header_);
}
Pickle& Pickle::operator=(Pickle&& other) {
BufferList tmp = mozilla::Move(other.buffers_);
other.buffers_ = mozilla::Move(buffers_);
buffers_ = mozilla::Move(tmp);
//std::swap(buffers_, other.buffers_);
std::swap(header_, other.header_);
std::swap(header_size_, other.header_size_);
std::swap(capacity_, other.capacity_);
std::swap(variable_buffer_offset_, other.variable_buffer_offset_);
return *this;
}
@@ -188,9 +174,9 @@ bool Pickle::ReadInt16(PickleIterator* iter, int16_t* result) const {
DCHECK(iter);
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
return false;
return ReadBytesInto(iter, result, sizeof(*result));
iter->CopyFrom(result);
iter->CopyInto(result);
UpdateIter(iter, sizeof(*result));
return true;
@@ -200,9 +186,9 @@ bool Pickle::ReadUInt16(PickleIterator* iter, uint16_t* result) const {
DCHECK(iter);
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
return false;
return ReadBytesInto(iter, result, sizeof(*result));
iter->CopyFrom(result);
iter->CopyInto(result);
UpdateIter(iter, sizeof(*result));
return true;
@@ -212,9 +198,9 @@ bool Pickle::ReadInt(PickleIterator* iter, int* result) const {
DCHECK(iter);
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
return false;
return ReadBytesInto(iter, result, sizeof(*result));
iter->CopyFrom(result);
iter->CopyInto(result);
UpdateIter(iter, sizeof(*result));
return true;
@@ -225,15 +211,18 @@ bool Pickle::ReadInt(PickleIterator* iter, int* result) const {
bool Pickle::ReadLong(PickleIterator* iter, long* result) const {
DCHECK(iter);
int64_t bigResult = 0;
if (!IteratorHasRoomFor(*iter, sizeof(bigResult)))
return false;
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);
iter->CopyFrom(&bigResult);
DCHECK(bigResult <= LONG_MAX && bigResult >= LONG_MIN);
*result = static_cast<long>(bigResult);
UpdateIter(iter, sizeof(bigResult));
return true;
}
@@ -242,15 +231,18 @@ bool Pickle::ReadLong(PickleIterator* iter, long* result) const {
bool Pickle::ReadULong(PickleIterator* iter, unsigned long* result) const {
DCHECK(iter);
uint64_t bigResult = 0;
if (!IteratorHasRoomFor(*iter, sizeof(bigResult)))
return false;
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);
iter->CopyFrom(&bigResult);
DCHECK(bigResult <= ULONG_MAX);
*result = static_cast<unsigned long>(bigResult);
UpdateIter(iter, sizeof(bigResult));
return true;
}
@@ -265,15 +257,18 @@ bool Pickle::ReadLength(PickleIterator* iter, int* result) const {
bool Pickle::ReadSize(PickleIterator* iter, size_t* result) const {
DCHECK(iter);
uint64_t bigResult = 0;
if (!IteratorHasRoomFor(*iter, sizeof(bigResult)))
return false;
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 <= std::numeric_limits<size_t>::max());
*result = static_cast<size_t>(big_result);
iter->CopyFrom(&bigResult);
DCHECK(bigResult <= std::numeric_limits<size_t>::max());
*result = static_cast<size_t>(bigResult);
UpdateIter(iter, sizeof(bigResult));
return true;
}
@@ -281,9 +276,9 @@ bool Pickle::ReadInt32(PickleIterator* iter, int32_t* result) const {
DCHECK(iter);
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
return false;
return ReadBytesInto(iter, result, sizeof(*result));
iter->CopyFrom(result);
iter->CopyInto(result);
UpdateIter(iter, sizeof(*result));
return true;
@@ -293,9 +288,9 @@ bool Pickle::ReadUInt32(PickleIterator* iter, uint32_t* result) const {
DCHECK(iter);
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
return false;
return ReadBytesInto(iter, result, sizeof(*result));
iter->CopyFrom(result);
iter->CopyInto(result);
UpdateIter(iter, sizeof(*result));
return true;
@@ -305,9 +300,9 @@ bool Pickle::ReadInt64(PickleIterator* iter, int64_t* result) const {
DCHECK(iter);
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
return false;
return ReadBytesInto(iter, result, sizeof(*result));
iter->CopyFrom(result);
iter->CopyInto(result);
UpdateIter(iter, sizeof(*result));
return true;
@@ -317,9 +312,9 @@ bool Pickle::ReadUInt64(PickleIterator* iter, uint64_t* result) const {
DCHECK(iter);
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
return false;
return ReadBytesInto(iter, result, sizeof(*result));
iter->CopyFrom(result);
iter->CopyInto(result);
UpdateIter(iter, sizeof(*result));
return true;
@@ -329,9 +324,9 @@ bool Pickle::ReadDouble(PickleIterator* iter, double* result) const {
DCHECK(iter);
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
return false;
return ReadBytesInto(iter, result, sizeof(*result));
iter->CopyFrom(result);
iter->CopyInto(result);
UpdateIter(iter, sizeof(*result));
return true;
@@ -342,15 +337,19 @@ bool Pickle::ReadDouble(PickleIterator* iter, double* result) const {
bool Pickle::ReadIntPtr(PickleIterator* iter, intptr_t* result) const {
DCHECK(iter);
int64_t bigResult = 0;
if (!IteratorHasRoomFor(*iter, sizeof(bigResult)))
return false;
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;
}
}
iter->CopyFrom(&bigResult);
DCHECK(bigResult <= std::numeric_limits<intptr_t>::max() && bigResult >= std::numeric_limits<intptr_t>::min());
*result = static_cast<intptr_t>(bigResult);
DCHECK(big_result <= std::numeric_limits<intptr_t>::max() && big_result >= std::numeric_limits<intptr_t>::min());
*result = static_cast<intptr_t>(big_result);
UpdateIter(iter, sizeof(bigResult));
return true;
}
@@ -358,9 +357,9 @@ bool Pickle::ReadUnsignedChar(PickleIterator* iter, unsigned char* result) const
DCHECK(iter);
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
return false;
return ReadBytesInto(iter, result, sizeof(*result));
iter->CopyFrom(result);
iter->CopyInto(result);
UpdateIter(iter, sizeof(*result));
return true;
@@ -372,13 +371,13 @@ bool Pickle::ReadString(PickleIterator* iter, std::string* result) const {
int len;
if (!ReadLength(iter, &len))
return false;
if (!IteratorHasRoomFor(*iter, len))
auto chars = mozilla::MakeUnique<char[]>(len);
if (!ReadBytesInto(iter, chars.get(), len)) {
return false;
}
result->assign(chars.get(), len);
char* chars = reinterpret_cast<char*>(iter->iter_);
result->assign(chars, len);
UpdateIter(iter, len);
return true;
}
@@ -391,70 +390,85 @@ bool Pickle::ReadWString(PickleIterator* iter, std::wstring* result) const {
// Avoid integer multiplication overflow.
if (len > INT_MAX / static_cast<int>(sizeof(wchar_t)))
return false;
if (!IteratorHasRoomFor(*iter, len * sizeof(wchar_t)))
auto chars = mozilla::MakeUnique<wchar_t[]>(len);
if (!ReadBytesInto(iter, chars.get(), len * sizeof(wchar_t))) {
return false;
}
result->assign(chars.get(), len);
wchar_t* chars = reinterpret_cast<wchar_t*>(iter->iter_);
result->assign(chars, len);
UpdateIter(iter, len * sizeof(wchar_t));
return true;
}
bool Pickle::ReadBytes(PickleIterator* iter, const char** data, int length,
uint32_t alignment) const {
bool Pickle::FlattenBytes(PickleIterator* iter, const char** data, uint32_t length,
uint32_t alignment) {
DCHECK(iter);
DCHECK(data);
DCHECK(alignment == 4 || alignment == 8);
DCHECK(intptr_t(header_) % alignment == 0);
uint32_t paddingLen = intptr_t(iter->iter_) % alignment;
if (paddingLen) {
#ifdef DEBUG
{
const char* padding = static_cast<const char*>(iter->iter_);
for (uint32_t i = 0; i < paddingLen; i++) {
DCHECK(*(padding + i) == kBytePaddingMarker);
}
}
#endif
length += paddingLen;
if (AlignInt(length) < length) {
return false;
}
if (!IteratorHasRoomFor(*iter, length))
uint32_t padding_len = intptr_t(iter->iter_.Data()) % alignment;
if (!iter->iter_.AdvanceAcrossSegments(buffers_, padding_len)) {
return false;
}
*data = static_cast<const char*>(iter->iter_) + paddingLen;
DCHECK(intptr_t(*data) % alignment == 0);
if (!buffers_.FlattenBytes(iter->iter_, data, length)) {
return false;
}
UpdateIter(iter, length);
header_ = reinterpret_cast<Header*>(buffers_.Start());
return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length) - length);
}
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::ReadSentinel(PickleIterator* iter, uint32_t sentinel) const {
#ifdef SENTINEL_CHECKING
uint32_t found;
if (!ReadUInt32(iter, &found)) {
return false;
}
return found == sentinel;
#else
return true;
#endif
}
bool Pickle::ReadData(PickleIterator* iter, const char** data, int* length) const {
DCHECK(iter);
DCHECK(data);
DCHECK(length);
if (!ReadLength(iter, length))
return false;
return ReadBytes(iter, data, *length);
bool Pickle::WriteSentinel(uint32_t sentinel) {
#ifdef SENTINEL_CHECKING
return WriteUInt32(sentinel);
#else
return true;
#endif
}
char* Pickle::BeginWrite(uint32_t length, uint32_t alignment) {
void Pickle::EndRead(PickleIterator& iter) const {
DCHECK(iter.iter_.Done());
}
void Pickle::BeginWrite(uint32_t length, uint32_t alignment) {
DCHECK(alignment % 4 == 0) << "Must be at least 32-bit aligned!";
// 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) % alignment;
uint32_t padding = (header_size_ + offset) % alignment;
uint32_t new_size = offset + padding + AlignInt(length);
uint32_t needed_size = header_size_ + new_size;
if (needed_size > capacity_) {
double growth_rate = capacity_ < kFastGrowthCap ? 2.0 : 1.4;
Resize(std::max(static_cast<uint32_t>(capacity_ * growth_rate), needed_size));
}
MOZ_RELEASE_ASSERT(new_size >= header_->payload_size);
DCHECK(intptr_t(header_) % alignment == 0);
@@ -462,49 +476,42 @@ char* Pickle::BeginWrite(uint32_t length, uint32_t alignment) {
DCHECK_LE(length, std::numeric_limits<uint32_t>::max());
#endif
char* buffer = payload() + offset;
if (padding) {
memset(buffer, kBytePaddingMarker, padding);
buffer += padding;
MOZ_RELEASE_ASSERT(padding <= 8);
static const char padding_data[8] = {
kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker,
kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker,
};
buffers_.WriteBytes(padding_data, padding);
}
DCHECK(intptr_t(buffer) % alignment == 0);
DCHECK((header_size_ + header_->payload_size + padding) % alignment == 0);
header_->payload_size = new_size;
#ifdef MOZ_VALGRIND
// pad the trailing end as well, so that valgrind
// doesn't complain when we write the buffer
padding = AlignInt(length) - length;
if (padding) {
memset(buffer + length, kBytePaddingMarker, padding);
}
#endif
return buffer;
}
void Pickle::EndWrite(char* dest, int length) {
void Pickle::EndWrite(uint32_t length) {
// Zero-pad to keep tools like purify from complaining about uninitialized
// memory.
if (length % sizeof(memberAlignmentType))
memset(dest + length, 0,
sizeof(memberAlignmentType) - (length % sizeof(memberAlignmentType)));
uint32_t padding = AlignInt(length) - length;
if (padding) {
MOZ_RELEASE_ASSERT(padding <= 4);
static const char padding_data[4] = {
kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker,
};
buffers_.WriteBytes(padding_data, padding);
}
}
bool Pickle::WriteBytes(const void* data, int data_len, uint32_t alignment) {
DCHECK(capacity_ != kCapacityReadOnly) << "oops: pickle is readonly";
bool Pickle::WriteBytes(const void* data, uint32_t data_len, uint32_t alignment) {
DCHECK(alignment == 4 || alignment == 8);
DCHECK(intptr_t(header_) % alignment == 0);
char* dest = BeginWrite(data_len, alignment);
if (!dest)
return false;
BeginWrite(data_len, alignment);
memcpy(dest, data, data_len);
buffers_.WriteBytes(reinterpret_cast<const char*>(data), data_len);
EndWrite(dest, data_len);
EndWrite(data_len);
return true;
}
@@ -523,65 +530,27 @@ bool Pickle::WriteWString(const std::wstring& value) {
static_cast<int>(value.size() * sizeof(wchar_t)));
}
bool Pickle::WriteData(const char* data, int length) {
bool Pickle::WriteData(const char* data, uint32_t length) {
return WriteInt(length) && WriteBytes(data, length);
}
char* Pickle::BeginWriteData(int length) {
DCHECK_EQ(variable_buffer_offset_, 0U) <<
"There can only be one variable buffer in a Pickle";
if (!WriteInt(length))
return NULL;
char *data_ptr = BeginWrite(length, sizeof(memberAlignmentType));
if (!data_ptr)
return NULL;
variable_buffer_offset_ =
data_ptr - reinterpret_cast<char*>(header_) - sizeof(int);
// EndWrite doesn't necessarily have to be called after the write operation,
// so we call it here to pad out what the caller will eventually write.
EndWrite(data_ptr, length);
return data_ptr;
void Pickle::InputBytes(const char* data, uint32_t length) {
buffers_.WriteBytes(data, length);
}
void Pickle::Resize(uint32_t new_capacity) {
new_capacity = ConstantAligner<kPayloadUnit>::align(new_capacity);
void* p = moz_xrealloc(header_, new_capacity);
header_ = reinterpret_cast<Header*>(p);
capacity_ = new_capacity;
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
const char* Pickle::FindNext(uint32_t header_size,
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>(kPayloadUnit));
if (end < start)
return nullptr;
size_t length = static_cast<size_t>(end - start);
if (length < sizeof(Header))
return nullptr;
const Header* hdr = reinterpret_cast<const Header*>(start);
if (length < header_size || length - header_size < hdr->payload_size)
return nullptr;
return start + header_size + hdr->payload_size;
}
// static
uint32_t Pickle::GetLength(uint32_t header_size,
const char* start,
const char* end) {
DCHECK(header_size == AlignInt(header_size));
DCHECK(header_size <= static_cast<memberAlignmentType>(kPayloadUnit));
DCHECK(header_size <= static_cast<memberAlignmentType>(kHeaderSegmentCapacity));
if (end < start)
return 0;
@@ -593,5 +562,11 @@ uint32_t Pickle::GetLength(uint32_t header_size,
if (length < header_size)
return 0;
return header_size + hdr->payload_size;
mozilla::CheckedInt<uint32_t> sum(header_size);
sum += hdr->payload_size;
if (!sum.isValid())
return 0;
return sum.value();
}
+46 -96
View File
@@ -14,6 +14,8 @@
#include "base/string16.h"
#include "mozilla/Attributes.h"
#include "mozilla/BufferList.h"
#include "mozilla/mozalloc.h"
class Pickle;
@@ -24,10 +26,10 @@ public:
private:
friend class Pickle;
template<typename T>
void CopyFrom(T* dest);
mozilla::BufferList<InfallibleAllocPolicy>::IterImpl iter_;
void* iter_;
template<typename T>
void CopyInto(T* dest);
};
// This class provides facilities for basic binary value packing and unpacking.
@@ -49,28 +51,16 @@ private:
//
class Pickle {
public:
enum Ownership {
BORROWS,
OWNS,
};
~Pickle();
// Initialize a Pickle object using the default header size.
Pickle();
Pickle() = delete;
// Initialize a Pickle object with the specified header size in bytes, which
// must be greater-than-or-equal-to sizeof(Pickle::Header). The header size
// will be rounded up to ensure that the header size is 32bit-aligned.
explicit Pickle(int header_size);
explicit Pickle(uint32_t header_size);
// Initializes a Pickle from a const block of data. If ownership == BORROWS,
// the data is not copied; instead the data is merely referenced by this
// Pickle. Only const methods should be used on the Pickle when initialized
// this way. The header padding size is deduced from the data length. If
// ownership == OWNS, then again no copying takes place. However, the buffer
// is writable and will be freed when this Pickle is destroyed.
Pickle(const char* data, int data_len, Ownership ownership = BORROWS);
Pickle(uint32_t header_size, const char* data, uint32_t length);
Pickle(const Pickle& other) = delete;
@@ -82,16 +72,13 @@ class Pickle {
Pickle& operator=(Pickle&& other);
// Returns the size of the Pickle's data.
int size() const { return static_cast<int>(header_size_ +
header_->payload_size); }
uint32_t size() const { return header_size_ + header_->payload_size; }
// Return the full size of the memory allocated for this Pickle's data.
uint32_t capacity() const {
return capacity_;
}
typedef mozilla::BufferList<InfallibleAllocPolicy> BufferList;
// Returns the data for this Pickle.
const void* data() const { return header_; }
const BufferList& Buffers() const { return buffers_; }
uint32_t CurrentSize() const { return buffers_.Size(); }
// Methods for reading the payload of the Pickle. To read from the start of
// the Pickle, initialize *iter to NULL. If successful, these methods return
@@ -114,17 +101,17 @@ class Pickle {
MOZ_MUST_USE bool ReadUnsignedChar(PickleIterator* iter, unsigned char* result) const;
MOZ_MUST_USE bool ReadString(PickleIterator* iter, std::string* result) const;
MOZ_MUST_USE bool ReadWString(PickleIterator* iter, std::wstring* result) const;
MOZ_MUST_USE bool ReadData(PickleIterator* iter, const char** data, int* length) const;
MOZ_MUST_USE bool ReadBytes(PickleIterator* iter, const char** data, int length,
uint32_t alignment = sizeof(memberAlignmentType)) const;
MOZ_MUST_USE bool ReadBytesInto(PickleIterator* iter, void* data, uint32_t length) const;
MOZ_MUST_USE bool FlattenBytes(PickleIterator* iter, const char** data, uint32_t length,
uint32_t alignment = sizeof(memberAlignmentType));
// Safer version of ReadInt() checks for the result not being negative.
// Use it for reading the object sizes.
MOZ_MUST_USE bool ReadLength(PickleIterator* iter, int* result) const;
void EndRead(PickleIterator& iter) const {
DCHECK(iter.iter_ == end_of_payload());
}
MOZ_MUST_USE bool ReadSentinel(PickleIterator* iter, uint32_t sentinel) const;
void EndRead(PickleIterator& iter) const;
// Methods for adding to the payload of the Pickle. These values are
// appended to the end of the Pickle's payload. When reading values from a
@@ -182,19 +169,15 @@ class Pickle {
}
bool WriteString(const std::string& value);
bool WriteWString(const std::wstring& value);
bool WriteData(const char* data, int length);
bool WriteBytes(const void* data, int data_len,
bool WriteData(const char* data, uint32_t length);
bool WriteBytes(const void* data, uint32_t data_len,
uint32_t alignment = sizeof(memberAlignmentType));
// Same as WriteData, but allows the caller to write directly into the
// Pickle. This saves a copy in cases where the data is not already
// available in a buffer. The caller should take care to not write more
// than the length it declares it will. Use ReadData to get the data.
// Returns NULL on failure.
//
// The returned pointer will only be valid until the next write operation
// on this Pickle.
char* BeginWriteData(int length);
bool WriteSentinel(uint32_t sentinel);
int32_t* GetInt32PtrForTest(uint32_t offset);
void InputBytes(const char* data, uint32_t length);
// Payload follows after allocation of Header (header size is customizable).
struct Header {
@@ -215,56 +198,21 @@ class Pickle {
return static_cast<const T*>(header_);
}
// Returns true if the given iterator could point to data with the given
// length. If there is no room for the given data before the end of the
// payload, returns false.
bool IteratorHasRoomFor(const PickleIterator& iter, int len) const {
if ((len < 0) || (iter.iter_ < header_) || iter.iter_ > end_of_payload())
return false;
const char* end_of_region = reinterpret_cast<const char*>(iter.iter_) + len;
// Watch out for overflow in pointer calculation, which wraps.
return (iter.iter_ <= end_of_region) && (end_of_region <= end_of_payload());
}
typedef uint32_t memberAlignmentType;
protected:
uint32_t payload_size() const { return header_->payload_size; }
char* payload() {
return reinterpret_cast<char*>(header_) + header_size_;
}
const char* payload() const {
return reinterpret_cast<const char*>(header_) + header_size_;
}
// Returns the address of the byte immediately following the currently valid
// header + payload.
char* end_of_payload() {
// We must have a valid header_.
return payload() + payload_size();
}
const char* end_of_payload() const {
// This object may be invalid.
return header_ ? payload() + payload_size() : nullptr;
}
// Resizes the buffer for use when writing the specified amount of data. The
// location that the data should be written at is returned, or NULL if there
// was an error. Call EndWrite with the returned offset and the given length
// to pad out for the next write.
char* BeginWrite(uint32_t length, uint32_t alignment);
void BeginWrite(uint32_t length, uint32_t alignment);
// Completes the write operation by padding the data with NULL bytes until it
// is padded. Should be paired with BeginWrite, but it does not necessarily
// have to be called after the data is written.
void EndWrite(char* dest, int length);
// Resize the capacity, note that the input value should include the size of
// the header: new_capacity = sizeof(Header) + desired_payload_capacity.
// A realloc() failure will cause a Resize failure... and caller should check
// the return result for true (i.e., successful resizing).
void Resize(uint32_t new_capacity);
void EndWrite(uint32_t length);
// Round 'bytes' up to the next multiple of 'alignment'. 'alignment' must be
// a power of 2.
@@ -280,35 +228,37 @@ class Pickle {
return ConstantAligner<sizeof(memberAlignmentType)>::align(bytes);
}
static uint32_t AlignCapacity(int bytes) {
return ConstantAligner<kSegmentAlignment>::align(bytes);
}
// Returns true if the given iterator could point to data with the given
// length. If there is no room for the given data before the end of the
// payload, returns false.
bool IteratorHasRoomFor(const PickleIterator& iter, uint32_t len) const;
// Moves the iterator by the given number of bytes, making sure it is aligned.
// Pointer (iterator) is NOT aligned, but the change in the pointer
// is guaranteed to be a multiple of sizeof(memberAlignmentType).
static void UpdateIter(PickleIterator* iter, int bytes) {
iter->iter_ = static_cast<char*>(iter->iter_) + AlignInt(bytes);
}
void UpdateIter(PickleIterator* iter, uint32_t bytes) const;
// Find the end of the pickled data that starts at range_start. Returns NULL
// if the entire Pickle is not found in the given data range.
static const char* FindNext(uint32_t header_size,
// Figure out how big the message starting at range_start is. Returns 0 if
// there's no enough data to determine (i.e., if [range_start, range_end) does
// not contain enough of the message header to know the size).
static uint32_t MessageSize(uint32_t header_size,
const char* range_start,
const char* range_end);
// If the given range contains at least header_size bytes, return the length
// of the pickled data including the header.
static uint32_t GetLength(uint32_t header_size,
const char* range_start,
const char* range_end);
// The allocation granularity of the payload.
static const int kPayloadUnit;
// Segments capacities are aligned to 8 bytes to ensure that all reads/writes
// at 8-byte aligned offsets will be on 8-byte aligned pointers.
static const uint32_t kSegmentAlignment = 8;
private:
friend class PickleIterator;
BufferList buffers_;
Header* header_;
uint32_t header_size_;
uint32_t capacity_;
uint32_t variable_buffer_offset_;
};
#endif // BASE_PICKLE_H__
+1 -1
View File
@@ -50,7 +50,7 @@ class Channel : public Message::Sender {
enum {
// The maximum message size in bytes. Attempting to receive a
// message of this size or bigger results in a channel error.
kMaximumMessageSize = 256 * 1024 * 1024,
kMaximumMessageSize = 128 * 1024 * 1024,
// Ammount of data to read at once from the pipe.
kReadBufferSize = 4 * 1024,
@@ -35,6 +35,9 @@
#include "mozilla/ipc/ProtocolUtils.h"
#include "mozilla/UniquePtr.h"
// Work around possible OS limitations.
static const size_t kMaxIOVecSize = 256;
#ifdef MOZ_TASK_TRACER
#include "GeckoTaskTracerImpl.h"
using namespace mozilla::tasktracer;
@@ -185,7 +188,8 @@ void Channel::ChannelImpl::Init(Mode mode, Listener* listener) {
mode_ = mode;
is_blocked_on_write_ = false;
message_send_bytes_written_ = 0;
partial_write_iter_.reset();
input_buf_offset_ = 0;
server_listen_pipe_ = -1;
pipe_ = -1;
client_pipe_ = -1;
@@ -263,11 +267,6 @@ bool Channel::ChannelImpl::EnqueueHelloMessage() {
return true;
}
void Channel::ChannelImpl::ClearAndShrinkInputOverflowBuf()
{
input_overflow_buf_.clear();
}
bool Channel::ChannelImpl::Connect() {
if (pipe_ == -1) {
return false;
@@ -287,10 +286,8 @@ bool Channel::ChannelImpl::Connect() {
}
bool Channel::ChannelImpl::ProcessIncomingMessages() {
ssize_t bytes_read = 0;
struct msghdr msg = {0};
struct iovec iov = {input_buf_, Channel::kReadBufferSize};
struct iovec iov;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
@@ -299,27 +296,30 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() {
for (;;) {
msg.msg_controllen = sizeof(input_cmsg_buf_);
if (bytes_read == 0) {
if (pipe_ == -1)
return false;
if (pipe_ == -1)
return false;
// Read from pipe.
// recvmsg() returns 0 if the connection has closed or EAGAIN if no data
// is waiting on the pipe.
bytes_read = HANDLE_EINTR(recvmsg(pipe_, &msg, MSG_DONTWAIT));
// In some cases the beginning of a message will be stored in input_buf_. We
// don't want to overwrite that, so we store the new data after it.
iov.iov_base = input_buf_ + input_buf_offset_;
iov.iov_len = Channel::kReadBufferSize - input_buf_offset_;
if (bytes_read < 0) {
if (errno == EAGAIN) {
return true;
} else {
CHROMIUM_LOG(ERROR) << "pipe error (" << pipe_ << "): " << strerror(errno);
return false;
}
} else if (bytes_read == 0) {
// The pipe has closed...
Close();
// Read from pipe.
// recvmsg() returns 0 if the connection has closed or EAGAIN if no data
// is waiting on the pipe.
ssize_t bytes_read = HANDLE_EINTR(recvmsg(pipe_, &msg, MSG_DONTWAIT));
if (bytes_read < 0) {
if (errno == EAGAIN) {
return true;
} else {
CHROMIUM_LOG(ERROR) << "pipe error (" << pipe_ << "): " << strerror(errno);
return false;
}
} else if (bytes_read == 0) {
// The pipe has closed...
Close();
return false;
}
DCHECK(bytes_read);
@@ -373,38 +373,8 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() {
}
// Process messages from input buffer.
const char *p;
const char *overflowp;
const char *end;
if (input_overflow_buf_.empty()) {
overflowp = NULL;
p = input_buf_;
end = p + bytes_read;
} else {
if (input_overflow_buf_.size() >
static_cast<size_t>(kMaximumMessageSize - bytes_read)) {
ClearAndShrinkInputOverflowBuf();
CHROMIUM_LOG(ERROR) << "IPC message is too big";
return false;
}
input_overflow_buf_.append(input_buf_, bytes_read);
overflowp = p = input_overflow_buf_.data();
end = p + input_overflow_buf_.size();
// If we've received the entire header, then we know the message
// length. In that case, reserve enough space to hold the entire
// message. This is more efficient than repeatedly enlarging the buffer as
// more data comes in.
uint32_t length = Message::GetLength(p, end);
if (length) {
input_overflow_buf_.reserve(length + kReadBufferSize);
// Recompute these pointers in case the buffer moved.
overflowp = p = input_overflow_buf_.data();
end = p + input_overflow_buf_.size();
}
}
const char *p = input_buf_;
const char *end = input_buf_ + input_buf_offset_ + bytes_read;
// A pointer to an array of |num_fds| file descriptors which includes any
// fds that have spilled over from a previous read.
@@ -424,131 +394,154 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() {
num_fds = input_overflow_fds_.size();
}
// The data for the message we're currently reading consists of any data
// stored in incoming_message_ followed by data in input_buf_ (followed by
// other messages).
while (p < end) {
const char* message_tail = Message::FindNext(p, end);
if (message_tail) {
int len = static_cast<int>(message_tail - p);
char* buf;
// Try to figure out how big the message is. Size is 0 if we haven't read
// enough of the header to know the size.
uint32_t message_length = 0;
if (incoming_message_.isSome()) {
message_length = incoming_message_.ref().size();
} else {
message_length = Message::MessageSize(p, end);
}
// The Message |m| allocated below needs to own its data. We can either
// copy the data out of the buffer or else steal the buffer and move the
// remaining data elsewhere. If len is large enough, we steal. Otherwise
// we copy.
if (len > kMaxCopySize) {
// Since len > kMaxCopySize > kReadBufferSize, we know that we must be
// using the overflow buffer. And since we always shift everything to
// the left at the end of a read, we must be at the start of the
// overflow buffer.
MOZ_RELEASE_ASSERT(p == overflowp);
buf = input_overflow_buf_.trade_bytes(len);
if (!message_length) {
// We haven't seen the full message header.
MOZ_ASSERT(incoming_message_.isNothing());
// At this point the remaining data is at the front of
// input_overflow_buf_. p will get fixed up at the end of the
// loop. Set it to null here to make sure no one uses it.
p = nullptr;
overflowp = message_tail = input_overflow_buf_.data();
end = overflowp + input_overflow_buf_.size();
} else {
buf = (char*)moz_xmalloc(len);
memcpy(buf, p, len);
// Move everything we have to the start of the buffer. We'll finish
// reading this message when we get more data. For now we leave it in
// input_buf_.
memmove(input_buf_, p, end - p);
input_buf_offset_ = end - p;
break;
}
input_buf_offset_ = 0;
bool partial;
if (incoming_message_.isSome()) {
// We already have some data for this message stored in
// incoming_message_. We want to append the new data there.
Message& m = incoming_message_.ref();
// How much data from this message remains to be added to
// incoming_message_?
MOZ_ASSERT(message_length > m.CurrentSize());
uint32_t remaining = message_length - m.CurrentSize();
// How much data from this message is stored in input_buf_?
uint32_t in_buf = std::min(remaining, uint32_t(end - p));
m.InputBytes(p, in_buf);
p += in_buf;
// Are we done reading this message?
partial = in_buf != remaining;
} else {
// How much data from this message is stored in input_buf_?
uint32_t in_buf = std::min(message_length, uint32_t(end - p));
incoming_message_.emplace(p, in_buf);
p += in_buf;
// Are we done reading this message?
partial = in_buf != message_length;
}
if (partial) {
break;
}
Message& m = incoming_message_.ref();
if (m.header()->num_fds) {
// the message has file descriptors
const char* error = NULL;
if (m.header()->num_fds > num_fds - fds_i) {
// the message has been completely received, but we didn't get
// enough file descriptors.
error = "Message needs unreceived descriptors";
}
Message m(buf, len, Message::OWNS);
if (m.header()->num_fds) {
// the message has file descriptors
const char* error = NULL;
if (m.header()->num_fds > num_fds - fds_i) {
// the message has been completely received, but we didn't get
// enough file descriptors.
error = "Message needs unreceived descriptors";
}
if (m.header()->num_fds >
FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE) {
// There are too many descriptors in this message
error = "Message requires an excessive number of descriptors";
}
if (m.header()->num_fds >
FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE) {
// There are too many descriptors in this message
error = "Message requires an excessive number of descriptors";
}
if (error) {
CHROMIUM_LOG(WARNING) << error
<< " channel:" << this
<< " message-type:" << m.type()
<< " header()->num_fds:" << m.header()->num_fds
<< " num_fds:" << num_fds
<< " fds_i:" << fds_i;
// close the existing file descriptors so that we don't leak them
for (unsigned i = fds_i; i < num_fds; ++i)
HANDLE_EINTR(close(fds[i]));
input_overflow_fds_.clear();
// abort the connection
return false;
}
if (error) {
CHROMIUM_LOG(WARNING) << error
<< " channel:" << this
<< " message-type:" << m.type()
<< " header()->num_fds:" << m.header()->num_fds
<< " num_fds:" << num_fds
<< " fds_i:" << fds_i;
// close the existing file descriptors so that we don't leak them
for (unsigned i = fds_i; i < num_fds; ++i)
HANDLE_EINTR(close(fds[i]));
input_overflow_fds_.clear();
// abort the connection
return false;
}
#if defined(OS_MACOSX)
// Send a message to the other side, indicating that we are now
// responsible for closing the descriptor.
Message *fdAck = new Message(MSG_ROUTING_NONE,
RECEIVED_FDS_MESSAGE_TYPE,
IPC::Message::PRIORITY_NORMAL);
DCHECK(m.fd_cookie() != 0);
fdAck->set_fd_cookie(m.fd_cookie());
OutputQueuePush(fdAck);
// Send a message to the other side, indicating that we are now
// responsible for closing the descriptor.
Message *fdAck = new Message(MSG_ROUTING_NONE,
RECEIVED_FDS_MESSAGE_TYPE,
IPC::Message::PRIORITY_NORMAL);
DCHECK(m.fd_cookie() != 0);
fdAck->set_fd_cookie(m.fd_cookie());
OutputQueuePush(fdAck);
#endif
m.file_descriptor_set()->SetDescriptors(
&fds[fds_i], m.header()->num_fds);
fds_i += m.header()->num_fds;
}
m.file_descriptor_set()->SetDescriptors(
&fds[fds_i], m.header()->num_fds);
fds_i += m.header()->num_fds;
}
#ifdef IPC_MESSAGE_DEBUG_EXTRA
DLOG(INFO) << "received message on channel @" << this <<
" with type " << m.type();
DLOG(INFO) << "received message on channel @" << this <<
" with type " << m.type();
#endif
#ifdef MOZ_TASK_TRACER
AutoSaveCurTraceInfo saveCurTraceInfo;
SetCurTraceInfo(m.header()->source_event_id,
m.header()->parent_task_id,
m.header()->source_event_type);
AutoSaveCurTraceInfo saveCurTraceInfo;
SetCurTraceInfo(m.header()->source_event_id,
m.header()->parent_task_id,
m.header()->source_event_type);
#endif
if (m.routing_id() == MSG_ROUTING_NONE &&
m.type() == HELLO_MESSAGE_TYPE) {
// The Hello message contains only the process id.
listener_->OnChannelConnected(MessageIterator(m).NextInt());
if (m.routing_id() == MSG_ROUTING_NONE &&
m.type() == HELLO_MESSAGE_TYPE) {
// The Hello message contains only the process id.
listener_->OnChannelConnected(MessageIterator(m).NextInt());
#if defined(OS_MACOSX)
} else if (m.routing_id() == MSG_ROUTING_NONE &&
m.type() == RECEIVED_FDS_MESSAGE_TYPE) {
DCHECK(m.fd_cookie() != 0);
CloseDescriptors(m.fd_cookie());
} else if (m.routing_id() == MSG_ROUTING_NONE &&
m.type() == RECEIVED_FDS_MESSAGE_TYPE) {
DCHECK(m.fd_cookie() != 0);
CloseDescriptors(m.fd_cookie());
#endif
} else {
listener_->OnMessageReceived(mozilla::Move(m));
}
p = message_tail;
} else {
// Last message is partial.
break;
listener_->OnMessageReceived(mozilla::Move(m));
}
incoming_message_.reset();
}
if (end == p) {
ClearAndShrinkInputOverflowBuf();
} else if (!overflowp) {
// p is from input_buf_
input_overflow_buf_.assign(p, end - p);
} else if (p > overflowp) {
// p is from input_overflow_buf_
input_overflow_buf_.erase(0, p - overflowp);
}
input_overflow_fds_ = std::vector<int>(&fds[fds_i], &fds[num_fds]);
// When the input data buffer is empty, the overflow fds should be too. If
// this is not the case, we probably have a rogue renderer which is trying
// to fill our descriptor table.
if (input_overflow_buf_.empty() && !input_overflow_fds_.empty()) {
if (incoming_message_.isNothing() && input_buf_offset_ == 0 && !input_overflow_fds_.empty()) {
// We close these descriptors in Close()
return false;
}
bytes_read = 0; // Get more data.
}
return true;
@@ -576,7 +569,7 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() {
int[FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE]));
char buf[tmp];
if (message_send_bytes_written_ == 0 &&
if (partial_write_iter_.isNothing() &&
!msg->file_descriptor_set()->empty()) {
// This is the first chunk of a message which has descriptors to send
struct cmsghdr *cmsg;
@@ -604,16 +597,46 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() {
#endif
}
size_t amt_to_write = msg->size() - message_send_bytes_written_;
DCHECK(amt_to_write != 0);
const char *out_bytes = reinterpret_cast<const char*>(msg->data()) +
message_send_bytes_written_;
struct iovec iov[kMaxIOVecSize];
size_t iov_count = 0;
size_t amt_to_write = 0;
struct iovec iov = {const_cast<char*>(out_bytes), amt_to_write};
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
if (partial_write_iter_.isNothing()) {
Pickle::BufferList::IterImpl iter(msg->Buffers());
partial_write_iter_.emplace(iter);
}
// How much of this message have we written so far?
Pickle::BufferList::IterImpl iter = partial_write_iter_.value();
// Store the unwritten part of the first segment to write into the iovec.
iov[0].iov_base = const_cast<char*>(iter.Data());
iov[0].iov_len = iter.RemainingInSegment();
amt_to_write += iov[0].iov_len;
iter.Advance(msg->Buffers(), iov[0].iov_len);
iov_count++;
// Store remaining segments to write into iovec.
while (!iter.Done()) {
char* data = iter.Data();
size_t size = iter.RemainingInSegment();
// Don't add more than kMaxIOVecSize to the iovec so that we avoid
// OS-dependent limits.
if (iov_count < kMaxIOVecSize) {
iov[iov_count].iov_base = data;
iov[iov_count].iov_len = size;
iov_count++;
}
amt_to_write += size;
iter.Advance(msg->Buffers(), size);
}
msgh.msg_iov = iov;
msgh.msg_iovlen = iov_count;
ssize_t bytes_written = HANDLE_EINTR(sendmsg(pipe_, &msgh, MSG_DONTWAIT));
#if !defined(OS_MACOSX)
// On OSX CommitAll gets called later, once we get the RECEIVED_FDS_MESSAGE_TYPE
// message.
@@ -657,9 +680,9 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() {
}
if (static_cast<size_t>(bytes_written) != amt_to_write) {
// If write() fails with EAGAIN then bytes_written will be -1.
if (bytes_written > 0) {
// If write() fails with EAGAIN then bytes_written will be -1.
message_send_bytes_written_ += bytes_written;
partial_write_iter_.ref().AdvanceAcrossSegments(msg->Buffers(), bytes_written);
}
// Tell libevent to call us back once things are unblocked.
@@ -672,7 +695,7 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() {
this);
return true;
} else {
message_send_bytes_written_ = 0;
partial_write_iter_.reset();
#if defined(OS_MACOSX)
if (!msg->file_descriptor_set()->empty())
@@ -16,12 +16,14 @@
#include <vector>
#include <list>
#include "base/buffer.h"
#include "base/message_loop.h"
#include "base/task.h"
#include "chrome/common/file_descriptor_set_posix.h"
#include "nsAutoPtr.h"
#include "mozilla/Maybe.h"
namespace IPC {
// An implementation of ChannelImpl for POSIX systems that works via
@@ -62,8 +64,6 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
bool ProcessIncomingMessages();
bool ProcessOutgoingMessages();
void ClearAndShrinkInputOverflowBuf();
// MessageLoopForIO::Watcher implementation.
virtual void OnFileCanReadWithoutBlocking(int fd);
virtual void OnFileCanWriteWithoutBlocking(int fd);
@@ -86,9 +86,10 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
// Indicates whether we're currently blocked waiting for a write to complete.
bool is_blocked_on_write_;
// If sending a message blocks then we use this variable
// to keep track of where we are.
size_t message_send_bytes_written_;
// If sending a message blocks then we use this iterator to keep track of
// where in the message we are. It gets reset when the message is finished
// sending.
mozilla::Maybe<Pickle::BufferList::IterImpl> partial_write_iter_;
int server_listen_pipe_;
int pipe_;
@@ -105,6 +106,7 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
// We read from the pipe into this buffer
char input_buf_[Channel::kReadBufferSize];
size_t input_buf_offset_;
// We want input_cmsg_buf_ to be big enough to hold
// CMSG_SPACE(Channel::kReadBufferSize) bytes (see the comment below for an
@@ -128,9 +130,9 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
// for the control header.
char input_cmsg_buf_[Channel::kReadBufferSize + kControlBufferSlopBytes];
// Large messages that span multiple pipe buffers, get built-up using
// this buffer.
Buffer input_overflow_buf_;
// Large incoming messages that span multiple pipe buffers get built-up in the
// buffers of this message.
mozilla::Maybe<Message> incoming_message_;
std::vector<int> input_overflow_fds_;
// In server-mode, we have to wait for the client to connect before we
+104 -90
View File
@@ -88,6 +88,7 @@ void Channel::ChannelImpl::Init(Mode mode, Listener* listener) {
processing_incoming_ = false;
closed_ = false;
output_queue_length_ = 0;
input_buf_offset_ = 0;
}
void Channel::ChannelImpl::OutputQueuePush(Message* msg)
@@ -332,8 +333,8 @@ bool Channel::ChannelImpl::ProcessIncomingMessages(
// Read from pipe...
BOOL ok = ReadFile(pipe_,
input_buf_,
Channel::kReadBufferSize,
input_buf_ + input_buf_offset_,
Channel::kReadBufferSize - input_buf_offset_,
&bytes_read,
&input_state_.context.overlapped);
if (!ok) {
@@ -352,95 +353,94 @@ bool Channel::ChannelImpl::ProcessIncomingMessages(
// Process messages from input buffer.
const char* p, *end;
if (input_overflow_buf_.empty()) {
p = input_buf_;
end = p + bytes_read;
} else {
if (input_overflow_buf_.size() > (kMaximumMessageSize - bytes_read)) {
input_overflow_buf_.clear();
CHROMIUM_LOG(ERROR) << "IPC message is too big";
return false;
}
input_overflow_buf_.append(input_buf_, bytes_read);
p = input_overflow_buf_.data();
end = p + input_overflow_buf_.size();
// If we've received the entire header, then we know the message
// length. In that case, reserve enough space to hold the entire
// message. This is more efficient than repeatedly enlarging the buffer as
// more data comes in.
uint32_t length = Message::GetLength(p, end);
if (length) {
input_overflow_buf_.reserve(length + kReadBufferSize);
// Recompute these pointers in case the buffer moved.
p = input_overflow_buf_.data();
end = p + input_overflow_buf_.size();
}
}
const char *p = input_buf_;
const char *end = input_buf_ + input_buf_offset_ + bytes_read;
while (p < end) {
const char* message_tail = Message::FindNext(p, end);
if (message_tail) {
int len = static_cast<int>(message_tail - p);
char* buf;
// The Message |m| allocated below needs to own its data. We can either
// copy the data out of the buffer or else steal the buffer and move the
// remaining data elsewhere. If len is large enough, we steal. Otherwise
// we copy.
if (len > kMaxCopySize) {
// Since len > kMaxCopySize > kReadBufferSize, we know that we must be
// using the overflow buffer. And since we always shift everything to
// the left at the end of a read, we must be at the start of the
// overflow buffer.
buf = input_overflow_buf_.trade_bytes(len);
// At this point the remaining data is at the front of
// input_overflow_buf_. p will get fixed up at the end of the
// loop. Set it to null here to make sure no one uses it.
p = nullptr;
message_tail = input_overflow_buf_.data();
end = message_tail + input_overflow_buf_.size();
} else {
buf = (char*)moz_xmalloc(len);
memcpy(buf, p, len);
}
Message m(buf, len, Message::OWNS);
#ifdef IPC_MESSAGE_DEBUG_EXTRA
DLOG(INFO) << "received message on channel @" << this <<
" with type " << m.type();
#endif
if (m.routing_id() == MSG_ROUTING_NONE &&
m.type() == HELLO_MESSAGE_TYPE) {
// The Hello message contains the process id and must include the
// shared secret, if we are waiting for it.
MessageIterator it = MessageIterator(m);
int32_t claimed_pid = it.NextInt();
if (waiting_for_shared_secret_ && (it.NextInt() != shared_secret_)) {
NOTREACHED();
// Something went wrong. Abort connection.
Close();
listener_->OnChannelError();
return false;
}
waiting_for_shared_secret_ = false;
listener_->OnChannelConnected(claimed_pid);
} else {
listener_->OnMessageReceived(mozilla::Move(m));
}
p = message_tail;
// Try to figure out how big the message is. Size is 0 if we haven't read
// enough of the header to know the size.
uint32_t message_length = 0;
if (incoming_message_.isSome()) {
message_length = incoming_message_.ref().size();
} else {
// Last message is partial.
message_length = Message::MessageSize(p, end);
}
if (!message_length) {
// We haven't seen the full message header.
MOZ_ASSERT(incoming_message_.isNothing());
// Move everything we have to the start of the buffer. We'll finish
// reading this message when we get more data. For now we leave it in
// input_buf_.
memmove(input_buf_, p, end - p);
input_buf_offset_ = end - p;
break;
}
}
if (p != input_overflow_buf_.data()) {
// Don't assign unless we have to since this will throw away any memory we
// might have reserved.
input_overflow_buf_.assign(p, end - p);
input_buf_offset_ = 0;
bool partial;
if (incoming_message_.isSome()) {
// We already have some data for this message stored in
// incoming_message_. We want to append the new data there.
Message& m = incoming_message_.ref();
// How much data from this message remains to be added to
// incoming_message_?
MOZ_ASSERT(message_length > m.CurrentSize());
uint32_t remaining = message_length - m.CurrentSize();
// How much data from this message is stored in input_buf_?
uint32_t in_buf = std::min(remaining, uint32_t(end - p));
m.InputBytes(p, in_buf);
p += in_buf;
// Are we done reading this message?
partial = in_buf != remaining;
} else {
// How much data from this message is stored in input_buf_?
uint32_t in_buf = std::min(message_length, uint32_t(end - p));
incoming_message_.emplace(p, in_buf);
p += in_buf;
// Are we done reading this message?
partial = in_buf != message_length;
}
if (partial) {
break;
}
Message& m = incoming_message_.ref();
#ifdef IPC_MESSAGE_DEBUG_EXTRA
DLOG(INFO) << "received message on channel @" << this <<
" with type " << m.type();
#endif
if (m.routing_id() == MSG_ROUTING_NONE &&
m.type() == HELLO_MESSAGE_TYPE) {
// The Hello message contains the process id and must include the
// shared secret, if we are waiting for it.
MessageIterator it = MessageIterator(m);
int32_t claimed_pid = it.NextInt();
if (waiting_for_shared_secret_ && (it.NextInt() != shared_secret_)) {
NOTREACHED();
// Something went wrong. Abort connection.
Close();
listener_->OnChannelError();
return false;
}
waiting_for_shared_secret_ = false;
listener_->OnChannelConnected(claimed_pid);
} else {
listener_->OnMessageReceived(mozilla::Move(m));
}
incoming_message_.reset();
}
bytes_read = 0; // Get more data.
@@ -467,8 +467,15 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages(
// Message was sent.
DCHECK(!output_queue_.empty());
Message* m = output_queue_.front();
OutputQueuePop();
delete m;
MOZ_RELEASE_ASSERT(partial_write_iter_.isSome());
Pickle::BufferList::IterImpl& iter = partial_write_iter_.ref();
iter.Advance(m->Buffers(), bytes_written);
if (iter.Done()) {
partial_write_iter_.reset();
OutputQueuePop();
delete m;
}
}
if (output_queue_.empty())
@@ -479,9 +486,16 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages(
// Write to pipe...
Message* m = output_queue_.front();
if (partial_write_iter_.isNothing()) {
Pickle::BufferList::IterImpl iter(m->Buffers());
partial_write_iter_.emplace(iter);
}
Pickle::BufferList::IterImpl& iter = partial_write_iter_.ref();
BOOL ok = WriteFile(pipe_,
m->data(),
m->size(),
iter.Data(),
iter.RemainingInSegment(),
&bytes_written,
&output_state_.context.overlapped);
if (!ok) {
@@ -12,10 +12,12 @@
#include <queue>
#include <string>
#include "base/buffer.h"
#include "base/message_loop.h"
#include "base/task.h"
#include "nsISupportsImpl.h"
#include "mozilla/Maybe.h"
namespace IPC {
class Channel::ChannelImpl : public MessageLoopForIO::IOHandler {
@@ -82,12 +84,18 @@ class Channel::ChannelImpl : public MessageLoopForIO::IOHandler {
// Messages to be sent are queued here.
std::queue<Message*> output_queue_;
// If sending a message blocks then we use this iterator to keep track of
// where in the message we are. It gets reset when the message is finished
// sending.
mozilla::Maybe<Pickle::BufferList::IterImpl> partial_write_iter_;
// We read from the pipe into this buffer
char input_buf_[Channel::kReadBufferSize];
size_t input_buf_offset_;
// Large messages that span multiple pipe buffers, get built-up using
// this buffer.
Buffer input_overflow_buf_;
// Large incoming messages that span multiple pipe buffers get built-up in the
// buffers of this message.
mozilla::Maybe<Message> incoming_message_;
// In server-mode, we have to wait for the client to connect before we
// can begin reading. We make use of the input_state_ when performing
@@ -73,8 +73,8 @@ Message::Message(int32_t routing_id, msgid_t type, PriorityValue priority,
InitLoggingVariables(aName);
}
Message::Message(const char* data, int data_len, Ownership ownership)
: Pickle(data, data_len, ownership)
Message::Message(const char* data, int data_len)
: Pickle(sizeof(Header), data, data_len)
{
MOZ_COUNT_CTOR(IPC::Message);
InitLoggingVariables();
+6 -16
View File
@@ -72,12 +72,7 @@ class Message : public Pickle {
MessageCompression compression = COMPRESSION_NONE,
const char* const name="???");
// Initializes a message from a const block of data. If ownership == BORROWS,
// the data is not copied; instead the data is merely referenced by this
// message. Only const methods should be used on the message when initialized
// this way. If ownership == OWNS, then again no copying takes place. However,
// the buffer is writable and will be freed when the message is destroyed.
Message(const char* data, int data_len, Ownership ownership = BORROWS);
Message(const char* data, int data_len);
Message(const Message& other) = delete;
Message(Message&& other);
@@ -240,16 +235,11 @@ class Message : public Pickle {
static void Log(const Message* msg, std::wstring* l) {
}
// Find the end of the message data that starts at range_start. Returns NULL
// if the entire message is not found in the given data range.
static const char* FindNext(const char* range_start, const char* range_end) {
return Pickle::FindNext(sizeof(Header), range_start, range_end);
}
// If the given range contains at least header_size bytes, return the length
// of the message including the header.
static uint32_t GetLength(const char* range_start, const char* range_end) {
return Pickle::GetLength(sizeof(Header), range_start, range_end);
// Figure out how big the message starting at range_start is. Returns 0 if
// there's no enough data to determine (i.e., if [range_start, range_end) does
// not contain enough of the message header to know the size).
static uint32_t MessageSize(const char* range_start, const char* range_end) {
return Pickle::MessageSize(sizeof(Header), range_start, range_end);
}
#if defined(OS_POSIX)
@@ -55,11 +55,6 @@ class MessageIterator {
NOTREACHED();
return val;
}
void NextData(const char** data, int* length) const {
if (!msg_.ReadData(&iter_, data, length)) {
NOTREACHED();
}
}
private:
const Message& msg_;
mutable PickleIterator iter_;
@@ -192,19 +187,10 @@ template <>
struct ParamTraitsFundamental<long long> {
typedef long long param_type;
static void Write(Message* m, const param_type& p) {
m->WriteData(reinterpret_cast<const char*>(&p), sizeof(param_type));
}
m->WriteBytes(&p, sizeof(param_type));
}
static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
const char *data;
int data_size = 0;
bool result = m->ReadData(iter, &data, &data_size);
if (result && data_size == sizeof(param_type)) {
memcpy(r, data, sizeof(param_type));
} else {
result = false;
NOTREACHED();
}
return result;
return m->ReadBytesInto(iter, r, sizeof(*r));
}
static void Log(const param_type& p, std::wstring* l) {
l->append(StringPrintf(L"%ll", p));
@@ -215,19 +201,10 @@ template <>
struct ParamTraitsFundamental<unsigned long long> {
typedef unsigned long long param_type;
static void Write(Message* m, const param_type& p) {
m->WriteData(reinterpret_cast<const char*>(&p), sizeof(param_type));
}
m->WriteBytes(&p, sizeof(param_type));
}
static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
const char *data;
int data_size = 0;
bool result = m->ReadData(iter, &data, &data_size);
if (result && data_size == sizeof(param_type)) {
memcpy(r, data, sizeof(param_type));
} else {
result = false;
NOTREACHED();
}
return result;
return m->ReadBytesInto(iter, r, sizeof(*r));
}
static void Log(const param_type& p, std::wstring* l) {
l->append(StringPrintf(L"%ull", p));
@@ -238,20 +215,10 @@ template <>
struct ParamTraitsFundamental<double> {
typedef double param_type;
static void Write(Message* m, const param_type& p) {
m->WriteData(reinterpret_cast<const char*>(&p), sizeof(param_type));
m->WriteDouble(p);
}
static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
const char *data;
int data_size = 0;
bool result = m->ReadData(iter, &data, &data_size);
if (result && data_size == sizeof(param_type)) {
memcpy(r, data, sizeof(param_type));
} else {
result = false;
NOTREACHED();
}
return result;
return m->ReadDouble(iter, r);
}
static void Log(const param_type& p, std::wstring* l) {
l->append(StringPrintf(L"e", p));
+1
View File
@@ -8,6 +8,7 @@
#include "BackgroundChildImpl.h"
#include "BackgroundParentImpl.h"
#include "base/process_util.h"
#include "base/task.h"
#include "FileDescriptor.h"
#include "GeckoProfiler.h"
#include "InputStreamUtils.h"
+1
View File
@@ -8,6 +8,7 @@
#include "base/command_line.h"
#include "base/string_util.h"
#include "base/task.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/process_watcher.h"
#ifdef MOZ_WIDGET_COCOA
+25 -50
View File
@@ -232,12 +232,7 @@ struct ParamTraits<int8_t>
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
{
const char* outp;
if (!aMsg->ReadBytes(aIter, &outp, sizeof(*aResult)))
return false;
*aResult = *reinterpret_cast<const paramType*>(outp);
return true;
return aMsg->ReadBytesInto(aIter, aResult, sizeof(*aResult));
}
};
@@ -253,12 +248,7 @@ struct ParamTraits<uint8_t>
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
{
const char* outp;
if (!aMsg->ReadBytes(aIter, &outp, sizeof(*aResult)))
return false;
*aResult = *reinterpret_cast<const paramType*>(outp);
return true;
return aMsg->ReadBytesInto(aIter, aResult, sizeof(*aResult));
}
};
@@ -309,14 +299,12 @@ struct ParamTraits<nsACString>
}
uint32_t length;
if (ReadParam(aMsg, aIter, &length)) {
const char* buf;
if (aMsg->ReadBytes(aIter, &buf, length)) {
aResult->Assign(buf, length);
return true;
}
if (!ReadParam(aMsg, aIter, &length)) {
return false;
}
return false;
aResult->SetLength(length);
return aMsg->ReadBytesInto(aIter, aResult->BeginWriting(), length);
}
static void Log(const paramType& aParam, std::wstring* aLog)
@@ -359,15 +347,12 @@ struct ParamTraits<nsAString>
}
uint32_t length;
if (ReadParam(aMsg, aIter, &length)) {
const char16_t* buf;
if (aMsg->ReadBytes(aIter, reinterpret_cast<const char**>(&buf),
length * sizeof(char16_t))) {
aResult->Assign(buf, length);
return true;
}
if (!ReadParam(aMsg, aIter, &length)) {
return false;
}
return false;
aResult->SetLength(length);
return aMsg->ReadBytesInto(aIter, aResult->BeginWriting(), length * sizeof(char16_t));
}
static void Log(const paramType& aParam, std::wstring* aLog)
@@ -474,14 +459,8 @@ struct ParamTraits<nsTArray<E>>
return false;
}
const char* outdata;
if (!aMsg->ReadBytes(aIter, &outdata, pickledLength)) {
return false;
}
E* elements = aResult->AppendElements(length);
memcpy(elements, outdata, pickledLength);
return aMsg->ReadBytesInto(aIter, elements, pickledLength);
} else {
aResult->SetCapacity(length);
@@ -491,9 +470,8 @@ struct ParamTraits<nsTArray<E>>
return false;
}
}
return true;
}
return true;
}
static void Log(const paramType& aParam, std::wstring* aLog)
@@ -552,11 +530,7 @@ struct ParamTraits<float>
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
{
const char* outFloat;
if (!aMsg->ReadBytes(aIter, &outFloat, sizeof(float)))
return false;
*aResult = *reinterpret_cast<const float*>(outFloat);
return true;
return aMsg->ReadBytesInto(aIter, aResult, sizeof(*aResult));
}
static void Log(const paramType& aParam, std::wstring* aLog)
@@ -749,16 +723,17 @@ struct ParamTraits<mozilla::SerializedStructuredCloneBuffer>
return false;
}
if (aResult->dataLength) {
const char** buffer =
const_cast<const char**>(reinterpret_cast<char**>(&aResult->data));
// Structured clone data must be 64-bit aligned.
if (!aMsg->ReadBytes(aIter, buffer, aResult->dataLength,
sizeof(uint64_t))) {
return false;
}
} else {
if (!aResult->dataLength) {
aResult->data = nullptr;
return true;
}
const char** buffer =
const_cast<const char**>(reinterpret_cast<char**>(&aResult->data));
// Structured clone data must be 64-bit aligned.
if (!const_cast<Message*>(aMsg)->FlattenBytes(aIter, buffer, aResult->dataLength,
sizeof(uint64_t))) {
return false;
}
return true;
+6 -6
View File
@@ -752,9 +752,9 @@ MessageChannel::Echo(Message* aMsg)
bool
MessageChannel::Send(Message* aMsg)
{
if (aMsg->capacity() >= kMinTelemetryMessageSize) {
if (aMsg->size() >= kMinTelemetryMessageSize) {
Telemetry::Accumulate(Telemetry::IPC_MESSAGE_SIZE,
nsDependentCString(aMsg->name()), aMsg->capacity());
nsDependentCString(aMsg->name()), aMsg->size());
}
CxxStackFrame frame(*this, OUT_MESSAGE, aMsg);
@@ -1053,9 +1053,9 @@ MessageChannel::ProcessPendingRequests(AutoEnterTransaction& aTransaction)
bool
MessageChannel::Send(Message* aMsg, Message* aReply)
{
if (aMsg->capacity() >= kMinTelemetryMessageSize) {
if (aMsg->size() >= kMinTelemetryMessageSize) {
Telemetry::Accumulate(Telemetry::IPC_MESSAGE_SIZE,
nsDependentCString(aMsg->name()), aMsg->capacity());
nsDependentCString(aMsg->name()), aMsg->size());
}
nsAutoPtr<Message> msg(aMsg);
@@ -1243,9 +1243,9 @@ MessageChannel::Send(Message* aMsg, Message* aReply)
MOZ_RELEASE_ASSERT(reply->is_sync());
*aReply = Move(*reply);
if (aReply->capacity() >= kMinTelemetryMessageSize) {
if (aReply->size() >= kMinTelemetryMessageSize) {
Telemetry::Accumulate(Telemetry::IPC_REPLY_SIZE,
nsDependentCString(msgName), aReply->capacity());
nsDependentCString(msgName), aReply->size());
}
return true;
}
+2
View File
@@ -9,6 +9,7 @@
#include "mozilla/ipc/MessageChannel.h"
#include "mozilla/ipc/BrowserProcessSubThread.h"
#include "mozilla/ipc/ProtocolUtils.h"
#include "chrome/common/ipc_channel.h"
#ifdef MOZ_NUWA_PROCESS
#include "ipc/Nuwa.h"
@@ -157,6 +158,7 @@ ProcessLink::EchoMessage(Message *msg)
void
ProcessLink::SendMessage(Message *msg)
{
MOZ_RELEASE_ASSERT(msg->size() < IPC::Channel::kMaximumMessageSize);
mChan->AssertWorkerThread();
mChan->mMonitor->AssertCurrentThreadOwns();
+64 -20
View File
@@ -51,6 +51,11 @@ lowered form of |tu|'''
## Helper code
##
def hashfunc(value):
h = hash(value) % 2**32
if h < 0: h += 2**32
return h
_NULL_ACTOR_ID = ExprLiteral.ZERO
_FREED_ACTOR_ID = ExprLiteral.ONE
@@ -696,6 +701,7 @@ class StructDecl(ipdl.ast.StructDecl, HasFQName):
class _StructField(_CompoundTypeComponent):
def __init__(self, ipdltype, name, sd, side=None):
self.basename = name
fname = name
special = _hasVisibleActor(ipdltype)
if special:
@@ -4403,12 +4409,13 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
ExprLiteral.ZERO),
cond=ExprBinary(ivar, '<', lenvar),
update=ExprPrefixUnop(ivar, '++'))
forwrite.addstmt(StmtExpr(
self.write(eltipdltype, ExprIndex(var, ivar), msgvar)))
forwrite.addstmt(
self.checkedWrite(eltipdltype, ExprIndex(var, ivar), msgvar,
sentinelKey=arraytype.name()))
write.addstmts([
StmtDecl(Decl(Type.UINT32, lenvar.name),
init=_callCxxArrayLength(var)),
StmtExpr(self.write(None, lenvar, msgvar)),
self.checkedWrite(None, lenvar, msgvar, sentinelKey=('length', arraytype.name())),
Whitespace.NL,
forwrite
])
@@ -4422,13 +4429,15 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
forread.addstmt(
self.checkedRead(eltipdltype, ExprAddrOf(ExprIndex(favar, ivar)),
msgvar, itervar, errfnRead,
'\'' + eltipdltype.name() + '[i]\''))
'\'' + eltipdltype.name() + '[i]\'',
sentinelKey=arraytype.name()))
read.addstmts([
StmtDecl(Decl(_cxxArrayType(_cxxBareType(arraytype.basetype, self.side)), favar.name)),
StmtDecl(Decl(Type.UINT32, lenvar.name)),
self.checkedRead(None, ExprAddrOf(lenvar),
msgvar, itervar, errfnArrayLength,
[ arraytype.name() ]),
[ arraytype.name() ],
sentinelKey=('length', arraytype.name())),
Whitespace.NL,
StmtExpr(_callCxxArraySetLength(favar, lenvar)),
forread,
@@ -4560,10 +4569,10 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
for f in sd.fields:
desc = '\'' + f.getMethod().name + '\' (' + f.ipdltype.name() + \
') member of \'' + intype.name + '\''
writefield = StmtExpr(self.write(f.ipdltype, get('.', f), msgvar))
writefield = self.checkedWrite(f.ipdltype, get('.', f), msgvar, sentinelKey=f.basename)
readfield = self.checkedRead(f.ipdltype,
ExprAddrOf(get('->', f)),
msgvar, itervar, errfnRead, desc)
msgvar, itervar, errfnRead, desc, sentinelKey=f.basename)
if f.special and f.side != self.side:
writefield = Whitespace(
"// skipping actor field that's meaningless on this side\n", indent=1)
@@ -4596,13 +4605,14 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
ct = c.ipdltype
isactor = (ct.isIPDL() and ct.isActor())
caselabel = CaseLabel(typename +'::'+ c.enum())
origenum = c.enum()
writecase = StmtBlock()
if c.special and c.side != self.side:
writecase.addstmt(_fatalError('wrong side!'))
else:
wexpr = ExprCall(ExprSelect(var, '.', c.getTypeName()))
writecase.addstmt(StmtExpr(self.write(ct, wexpr, msgvar)))
writecase.addstmt(self.checkedWrite(ct, wexpr, msgvar, sentinelKey=c.enum()))
writecase.addstmt(StmtReturn())
writeswitch.addcase(caselabel, writecase)
@@ -4622,11 +4632,12 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
readcase.addstmts([
StmtDecl(Decl(ct, tmpvar.name), init=c.defaultValue()),
StmtExpr(ExprAssn(ExprDeref(var), tmpvar)),
StmtReturn(self.read(
self.checkedRead(
c.ipdltype,
ExprAddrOf(ExprCall(ExprSelect(var, '->',
c.getTypeName()))),
msgvar, itervar))
msgvar, itervar, errfnRead, 'Union type', sentinelKey=origenum),
StmtReturn(ExprLiteral.TRUE)
])
readswitch.addcase(caselabel, readcase)
@@ -4640,8 +4651,9 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
write = MethodDefn(self.writeMethodDecl(intype, var))
write.addstmts([
uniontdef,
StmtExpr(self.write(
None, ExprCall(Type.INT, args=[ ud.callType(var) ]), msgvar)),
self.checkedWrite(
None, ExprCall(Type.INT, args=[ ud.callType(var) ]), msgvar,
sentinelKey=uniontype.name()),
Whitespace.NL,
writeswitch
])
@@ -4652,7 +4664,8 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
StmtDecl(Decl(Type.INT, typevar.name)),
self.checkedRead(
None, ExprAddrOf(typevar), msgvar, itervar, errfnUnionType,
[ uniontype.name() ]),
[ uniontype.name() ],
sentinelKey=uniontype.name()),
Whitespace.NL,
readswitch,
])
@@ -4698,6 +4711,20 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
return self.maybeAddNullabilityArg(
ipdltype, ExprCall(read, args=[ expr, from_, iterexpr ]))
def checkedWrite(self, ipdltype, expr, msgvar, sentinelKey, this=None):
assert sentinelKey
write = StmtExpr(self.write(ipdltype, expr, msgvar, this))
sentinel = StmtExpr(ExprCall(ExprSelect(msgvar, '->', 'WriteSentinel'),
args=[ ExprLiteral.Int(hashfunc(sentinelKey)) ]))
block = Block()
block.addstmts([
write,
Whitespace('// Sentinel = ' + repr(sentinelKey) + '\n', indent=1),
sentinel ])
return block
def visitMessageDecl(self, md):
isctor = md.decl.type.isCtor()
@@ -5075,7 +5102,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
init=ExprCall(ExprVar(md.pqMsgCtorFunc()),
args=[ routingId ])) ]
+ [ Whitespace.NL ]
+ [ StmtExpr(self.write(p.ipdltype, p.var(), msgvar, this))
+ [ self.checkedWrite(p.ipdltype, p.var(), msgvar, sentinelKey=p.name, this=this)
for p in md.params ]
+ [ Whitespace.NL ]
+ self.setMessageFlags(md, msgvar, reply=0))
@@ -5094,7 +5121,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
[ StmtExpr(ExprAssn(
replyvar, ExprCall(ExprVar(md.pqReplyCtorFunc()), args=[ routingId ]))),
Whitespace.NL ]
+ [ StmtExpr(self.write(r.ipdltype, r.var(), replyvar))
+ [ self.checkedWrite(r.ipdltype, r.var(), replyvar, sentinelKey=r.name)
for r in md.returns ]
+ self.setMessageFlags(md, replyvar, reply=1)
+ [ self.logMessage(md, replyvar, 'Sending reply ') ])
@@ -5150,7 +5177,8 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
decls = [ StmtDecl(Decl(handletype, handlevar.name)) ]
reads = [ self.checkedRead(None, ExprAddrOf(handlevar), msgexpr,
ExprAddrOf(self.itervar),
errfn, "'%s'" % handletype.name) ]
errfn, "'%s'" % handletype.name,
sentinelKey='actor') ]
start = 1
stmts.extend((
@@ -5162,7 +5190,8 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
+ [ Whitespace.NL ]
+ reads + [ self.checkedRead(p.ipdltype, ExprAddrOf(p.var()),
msgexpr, ExprAddrOf(itervar),
errfn, "'%s'" % p.bareType(side).name)
errfn, "'%s'" % p.bareType(side).name,
sentinelKey=p.name)
for p in md.params[start:] ]
+ [ self.endRead(msgvar, itervar) ]))
@@ -5185,7 +5214,8 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
+ [ self.checkedRead(r.ipdltype, r.var(),
ExprAddrOf(self.replyvar),
ExprAddrOf(self.itervar),
errfn, "'%s'" % r.bareType(side).name)
errfn, "'%s'" % r.bareType(side).name,
sentinelKey=r.name)
for r in md.returns ]
+ [ self.endRead(self.replyvar, itervar) ])
@@ -5336,14 +5366,28 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
ifbad.addifstmts(_badTransition())
return [ ifbad ]
def checkedRead(self, ipdltype, expr, msgexpr, iterexpr, errfn, paramtype):
def checkedRead(self, ipdltype, expr, msgexpr, iterexpr, errfn, paramtype, sentinelKey, sentinel=True):
ifbad = StmtIf(ExprNot(self.read(ipdltype, expr, msgexpr, iterexpr)))
if isinstance(paramtype, list):
errorcall = errfn(*paramtype)
else:
errorcall = errfn('Error deserializing ' + paramtype)
ifbad.addifstmts(errorcall)
return ifbad
block = Block()
block.addstmt(ifbad)
if sentinel:
assert sentinelKey
block.addstmt(Whitespace('// Sentinel = ' + repr(sentinelKey) + '\n', indent=1))
read = ExprCall(ExprSelect(msgexpr, '->', 'ReadSentinel'),
args=[ iterexpr, ExprLiteral.Int(hashfunc(sentinelKey)) ])
ifsentinel = StmtIf(ExprNot(read))
ifsentinel.addifstmts(errorcall)
block.addstmt(ifsentinel)
return block
def endRead(self, msgexpr, iterexpr):
return StmtExpr(ExprCall(ExprSelect(msgexpr, '.', 'EndRead'),
@@ -9,6 +9,7 @@
#include "base/command_line.h"
#include "base/string_util.h"
#include "base/task.h"
#include "base/thread.h"
#include "nsRegion.h"
+2 -6
View File
@@ -110,9 +110,8 @@ using namespace mozilla::ipc;
/*static*/ void
ParamTraits<Bad>::Write(Message* aMsg, const paramType& aParam)
{
char* ptr = aMsg->BeginWriteData(4);
ptr -= intptr_t(sizeof(int));
ptr -= intptr_t(sizeof(ActorHandle));
// Skip past the sentinel for the actor as well as the actor.
int32_t* ptr = aMsg->GetInt32PtrForTest(2 * sizeof(int32_t));
ActorHandle* ah = reinterpret_cast<ActorHandle*>(ptr);
if (ah->mId != -3)
fail("guessed wrong offset (value is %d, should be -3)", ah->mId);
@@ -122,9 +121,6 @@ ParamTraits<Bad>::Write(Message* aMsg, const paramType& aParam)
/*static*/ bool
ParamTraits<Bad>::Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
{
const char* ptr;
int len;
mozilla::Unused << aMsg->ReadData(aIter, &ptr, &len);
return true;
}
+1
View File
@@ -1,5 +1,6 @@
#include "TestBridgeMain.h"
#include "base/task.h"
#include "IPDLUnitTests.h" // fail etc.
#include "IPDLUnitTestSubprocess.h"
+1
View File
@@ -1,5 +1,6 @@
#include "TestCrashCleanup.h"
#include "base/task.h"
#include "mozilla/CondVar.h"
#include "mozilla/Mutex.h"
@@ -3,6 +3,7 @@
#include "TestEndpointBridgeMain.h"
#include "base/task.h"
#include "IPDLUnitTests.h" // fail etc.
#include "IPDLUnitTestSubprocess.h"
+1
View File
@@ -1,6 +1,7 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
#include "base/task.h"
#include "base/thread.h"
#include "TestEndpointOpens.h"
@@ -1,5 +1,6 @@
#include "TestInterruptErrorCleanup.h"
#include "base/task.h"
#include "mozilla/CondVar.h"
#include "mozilla/Mutex.h"
@@ -1,5 +1,6 @@
#include "TestInterruptShutdownRace.h"
#include "base/task.h"
#include "IPDLUnitTests.h" // fail etc.
#include "IPDLUnitTestSubprocess.h"
+1
View File
@@ -1,3 +1,4 @@
#include "base/task.h"
#include "base/thread.h"
#include "TestOpens.h"
+1
View File
@@ -1,5 +1,6 @@
#include "TestStackHooks.h"
#include "base/task.h"
#include "IPDLUnitTests.h" // fail etc.
+1
View File
@@ -1,4 +1,5 @@
#include "TestSyncHang.h"
#include "base/task.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "IPDLUnitTests.h" // fail etc.
+5
View File
@@ -329,6 +329,11 @@ public:
void reportAllocOverflow() const
{
}
bool checkSimulatedOOM() const
{
return true;
}
};
#endif /* ifdef __cplusplus */
+1
View File
@@ -12,6 +12,7 @@
#ifndef mozilla_AllocPolicy_h
#define mozilla_AllocPolicy_h
#include "mozilla/Attributes.h"
#include "mozilla/TemplateLib.h"
#include <stddef.h>
+446
View File
@@ -0,0 +1,446 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_BufferList_h
#define mozilla_BufferList_h
#include <algorithm>
#include "mozilla/AllocPolicy.h"
#include "mozilla/Move.h"
#include "mozilla/Types.h"
#include "mozilla/TypeTraits.h"
#include "mozilla/Vector.h"
#include <string.h>
// BufferList represents a sequence of buffers of data. A BufferList can choose
// to own its buffers or not. The class handles writing to the buffers,
// iterating over them, and reading data out. Unlike SegmentedVector, the
// buffers may be of unequal size. Like SegmentedVector, BufferList is a nice
// way to avoid large contiguous allocations (which can trigger OOMs).
namespace mozilla {
template<typename AllocPolicy>
class BufferList : private AllocPolicy
{
// Each buffer in a BufferList has a size and a capacity. The first mSize
// bytes are initialized and the remaining |mCapacity - mSize| bytes are free.
struct Segment
{
char* mData;
size_t mSize;
size_t mCapacity;
Segment(char* aData, size_t aSize, size_t aCapacity)
: mData(aData),
mSize(aSize),
mCapacity(aCapacity)
{
}
Segment(const Segment&) = delete;
Segment& operator=(const Segment&) = delete;
Segment(Segment&& aOther)
: mData(Move(aOther.mData)),
mSize(Move(aOther.mSize)),
mCapacity(Move(aOther.mCapacity))
{
}
Segment& operator=(Segment&& aRhs) {
MOZ_ASSERT(&aRhs != this, "self-move-assignment not allowed");
this->~Segment();
new(this) Segment(Move(aRhs));
return *this;
}
char* Start() const { return mData; }
char* End() const { return mData + mSize; }
};
public:
// For the convenience of callers, all segments are required to be a multiple
// of 8 bytes in capacity. Also, every buffer except the last one is required
// to be full (i.e., size == capacity). Therefore, a byte at offset N within
// the BufferList and stored in memory at an address A will satisfy
// (N % Align == A % Align) if Align == 2, 4, or 8.
//
// NB: FlattenBytes can create non-full segments in the middle of the
// list. However, it ensures that these buffers are 8-byte aligned, so the
// offset invariant is not violated.
static const size_t kSegmentAlignment = 8;
// Allocate a BufferList. The BufferList will free all its buffers when it is
// destroyed. An initial buffer of size aInitialSize and capacity
// aInitialCapacity is allocated automatically. This data will be contiguous
// an can be accessed via |Start()|. Subsequent buffers will be allocated with
// capacity aStandardCapacity.
BufferList(size_t aInitialSize,
size_t aInitialCapacity,
size_t aStandardCapacity,
AllocPolicy aAP = AllocPolicy())
: AllocPolicy(aAP),
mOwning(true),
mSize(0),
mStandardCapacity(aStandardCapacity)
{
MOZ_ASSERT(aInitialCapacity % kSegmentAlignment == 0);
MOZ_ASSERT(aStandardCapacity % kSegmentAlignment == 0);
if (aInitialCapacity) {
AllocateSegment(aInitialSize, aInitialCapacity);
}
}
BufferList(const BufferList& aOther) = delete;
BufferList(BufferList&& aOther)
: mOwning(aOther.mOwning),
mSegments(Move(aOther.mSegments)),
mSize(aOther.mSize),
mStandardCapacity(aOther.mStandardCapacity)
{
aOther.mSegments.clear();
aOther.mSize = 0;
}
BufferList& operator=(const BufferList& aOther) = delete;
BufferList& operator=(BufferList&& aOther)
{
Clear();
mOwning = aOther.mOwning;
mSegments = Move(aOther.mSegments);
mSize = aOther.mSize;
aOther.mSegments.clear();
aOther.mSize = 0;
return *this;
}
~BufferList() { Clear(); }
// Returns the sum of the sizes of all the buffers.
size_t Size() const { return mSize; }
void Clear()
{
if (mOwning) {
for (Segment& segment : mSegments) {
this->free_(segment.mData);
}
}
mSegments.clear();
mSize = 0;
}
// Iterates over bytes in the segments. You can advance it by as many bytes as
// you choose.
class IterImpl
{
// Invariants:
// (0) mSegment <= bufferList.mSegments.size()
// (1) mData <= mDataEnd
// (2) If mSegment is not the last segment, mData < mDataEnd
uintptr_t mSegment;
char* mData;
char* mDataEnd;
friend class BufferList;
public:
explicit IterImpl(const BufferList& aBuffers)
: mSegment(0),
mData(nullptr),
mDataEnd(nullptr)
{
if (!aBuffers.mSegments.empty()) {
mData = aBuffers.mSegments[0].Start();
mDataEnd = aBuffers.mSegments[0].End();
}
}
// Returns a pointer to the raw data. It is valid to access up to
// RemainingInSegment bytes of this buffer.
char* Data() const
{
MOZ_RELEASE_ASSERT(!Done());
return mData;
}
// Returns true if the memory in the range [Data(), Data() + aBytes) is all
// part of one contiguous buffer.
bool HasRoomFor(size_t aBytes) const
{
MOZ_RELEASE_ASSERT(mData <= mDataEnd);
return size_t(mDataEnd - mData) >= aBytes;
}
// Returns the maximum value aBytes for which HasRoomFor(aBytes) will be
// true.
size_t RemainingInSegment() const
{
MOZ_RELEASE_ASSERT(mData <= mDataEnd);
return mDataEnd - mData;
}
// Advances the iterator by aBytes bytes. aBytes must be less than
// RemainingInSegment(). If advancing by aBytes takes the iterator to the
// end of a buffer, it will be moved to the beginning of the next buffer
// unless it is the last buffer.
void Advance(const BufferList& aBuffers, size_t aBytes)
{
const Segment& segment = aBuffers.mSegments[mSegment];
MOZ_RELEASE_ASSERT(segment.Start() <= mData);
MOZ_RELEASE_ASSERT(mData <= mDataEnd);
MOZ_RELEASE_ASSERT(mDataEnd == segment.End());
MOZ_RELEASE_ASSERT(HasRoomFor(aBytes));
mData += aBytes;
if (mData == mDataEnd && mSegment + 1 < aBuffers.mSegments.length()) {
mSegment++;
const Segment& nextSegment = aBuffers.mSegments[mSegment];
mData = nextSegment.Start();
mDataEnd = nextSegment.End();
MOZ_RELEASE_ASSERT(mData < mDataEnd);
}
}
// Advance the iterator by aBytes, possibly crossing segments. This function
// returns false if it runs out of buffers to advance through. Otherwise it
// returns true.
bool AdvanceAcrossSegments(const BufferList& aBuffers, size_t aBytes)
{
size_t bytes = aBytes;
while (bytes) {
size_t toAdvance = std::min(bytes, RemainingInSegment());
if (!toAdvance) {
return false;
}
Advance(aBuffers, toAdvance);
bytes -= toAdvance;
}
return true;
}
// Returns true when the iterator reaches the end of the BufferList.
bool Done() const
{
return mData == mDataEnd;
}
};
// Special convenience method that returns Iter().Data().
char* Start() { return mSegments[0].mData; }
IterImpl Iter() const { return IterImpl(*this); }
// Copies aSize bytes from aData into the BufferList. The storage for these
// bytes may be split across multiple buffers. Size() is increased by aSize.
inline bool WriteBytes(const char* aData, size_t aSize);
// Copies possibly non-contiguous byte range starting at aIter into
// aData. aIter is advanced by aSize bytes. Returns false if it runs out of
// data before aSize.
inline bool ReadBytes(IterImpl& aIter, char* aData, size_t aSize) const;
// FlattenBytes reconfigures the BufferList so that data in the range
// [aIter, aIter + aSize) is stored contiguously. A pointer to this data is
// returned in aOutData. Returns false if not enough data is available. All
// other iterators are invalidated by this method.
//
// This method requires aIter and aSize to be 8-byte aligned.
inline bool FlattenBytes(IterImpl& aIter, const char** aOutData, size_t aSize);
// Return a new BufferList that shares storage with this BufferList. The new
// BufferList is read-only. It allows iteration over aSize bytes starting at
// aIter. Borrow can fail, in which case *aSuccess will be false upon
// return. The borrowed BufferList can use a different AllocPolicy than the
// original one. However, it is not responsible for freeing buffers, so the
// AllocPolicy is only used for the buffer vector.
template<typename BorrowingAllocPolicy>
BufferList<BorrowingAllocPolicy> Borrow(IterImpl& aIter, size_t aSize, bool* aSuccess,
BorrowingAllocPolicy aAP = BorrowingAllocPolicy());
private:
explicit BufferList(AllocPolicy aAP)
: AllocPolicy(aAP),
mOwning(false),
mSize(0),
mStandardCapacity(0)
{
}
void* AllocateSegment(size_t aSize, size_t aCapacity)
{
MOZ_RELEASE_ASSERT(mOwning);
char* data = this->template pod_malloc<char>(aCapacity);
if (!data) {
return nullptr;
}
if (!mSegments.append(Segment(data, aSize, aCapacity))) {
this->free_(data);
return nullptr;
}
mSize += aSize;
return data;
}
bool mOwning;
Vector<Segment, 1, AllocPolicy> mSegments;
size_t mSize;
size_t mStandardCapacity;
};
template<typename AllocPolicy>
bool
BufferList<AllocPolicy>::WriteBytes(const char* aData, size_t aSize)
{
MOZ_RELEASE_ASSERT(mOwning);
MOZ_RELEASE_ASSERT(mStandardCapacity);
size_t copied = 0;
size_t remaining = aSize;
if (!mSegments.empty()) {
Segment& lastSegment = mSegments.back();
size_t toCopy = std::min(aSize, lastSegment.mCapacity - lastSegment.mSize);
memcpy(lastSegment.mData + lastSegment.mSize, aData, toCopy);
lastSegment.mSize += toCopy;
mSize += toCopy;
copied += toCopy;
remaining -= toCopy;
}
while (remaining) {
size_t toCopy = std::min(remaining, mStandardCapacity);
void* data = AllocateSegment(toCopy, mStandardCapacity);
if (!data) {
return false;
}
memcpy(data, aData + copied, toCopy);
copied += toCopy;
remaining -= toCopy;
}
return true;
}
template<typename AllocPolicy>
bool
BufferList<AllocPolicy>::ReadBytes(IterImpl& aIter, char* aData, size_t aSize) const
{
size_t copied = 0;
size_t remaining = aSize;
while (remaining) {
size_t toCopy = std::min(aIter.RemainingInSegment(), remaining);
if (!toCopy) {
// We've run out of data in the last segment.
return false;
}
memcpy(aData + copied, aIter.Data(), toCopy);
copied += toCopy;
remaining -= toCopy;
aIter.Advance(*this, toCopy);
}
return true;
}
template<typename AllocPolicy>
bool
BufferList<AllocPolicy>::FlattenBytes(IterImpl& aIter, const char** aOutData, size_t aSize)
{
MOZ_RELEASE_ASSERT(aSize);
MOZ_RELEASE_ASSERT(mOwning);
if (aIter.HasRoomFor(aSize)) {
// If the data is already contiguous, just return a pointer.
*aOutData = aIter.Data();
aIter.Advance(*this, aSize);
return true;
}
// This buffer will become the new contiguous segment.
char* buffer = this->template pod_malloc<char>(Size());
if (!buffer) {
return false;
}
size_t copied = 0;
size_t offset;
bool found = false;
for (size_t i = 0; i < mSegments.length(); i++) {
Segment& segment = mSegments[i];
memcpy(buffer + copied, segment.Start(), segment.mSize);
if (i == aIter.mSegment) {
offset = copied + (aIter.mData - segment.Start());
// Do we have aSize bytes after aIter?
if (Size() - offset >= aSize) {
found = true;
*aOutData = buffer + offset;
aIter.mSegment = 0;
aIter.mData = buffer + offset + aSize;
aIter.mDataEnd = buffer + Size();
}
}
this->free_(segment.mData);
copied += segment.mSize;
}
mSegments.clear();
mSegments.infallibleAppend(Segment(buffer, Size(), Size()));
if (!found) {
aIter.mSegment = 0;
aIter.mData = Start();
aIter.mDataEnd = Start() + Size();
}
return found;
}
template<typename AllocPolicy> template<typename BorrowingAllocPolicy>
BufferList<BorrowingAllocPolicy>
BufferList<AllocPolicy>::Borrow(IterImpl& aIter, size_t aSize, bool* aSuccess,
BorrowingAllocPolicy aAP)
{
BufferList<BorrowingAllocPolicy> result(aAP);
size_t size = aSize;
while (size) {
size_t toAdvance = std::min(size, aIter.RemainingInSegment());
if (!toAdvance || !result.mSegments.append(Segment(aIter.mData, toAdvance, toAdvance))) {
*aSuccess = false;
return result;
}
aIter.Advance(*this, toAdvance);
size -= toAdvance;
}
result.mSize = aSize;
*aSuccess = true;
return result;
}
} // namespace mozilla
#endif /* mozilla_BufferList_h */
+1
View File
@@ -24,6 +24,7 @@ EXPORTS.mozilla = [
'Attributes.h',
'BinarySearch.h',
'BloomFilter.h',
'BufferList.h',
'Casting.h',
'ChaosMode.h',
'Char16.h',
+243
View File
@@ -0,0 +1,243 @@
/* -*- Mode: C++; tab-width: 9; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
// This is included first to ensure it doesn't implicitly depend on anything
// else.
#include "mozilla/BufferList.h"
// It would be nice if we could use the InfallibleAllocPolicy from mozalloc,
// but MFBT cannot use mozalloc.
class InfallibleAllocPolicy
{
public:
template <typename T>
T* pod_malloc(size_t aNumElems)
{
if (aNumElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
MOZ_CRASH("TestBufferList.cpp: overflow");
}
T* rv = static_cast<T*>(malloc(aNumElems * sizeof(T)));
if (!rv) {
MOZ_CRASH("TestBufferList.cpp: out of memory");
}
return rv;
}
void free_(void* aPtr) { free(aPtr); }
void reportAllocOverflow() const {}
bool checkSimulatedOOM() const { return true; }
};
typedef mozilla::BufferList<InfallibleAllocPolicy> BufferList;
int main(void)
{
const size_t kInitialSize = 16;
const size_t kInitialCapacity = 24;
const size_t kStandardCapacity = 32;
BufferList bl(kInitialSize, kInitialCapacity, kStandardCapacity);
memset(bl.Start(), 0x0c, kInitialSize);
MOZ_RELEASE_ASSERT(bl.Size() == kInitialSize);
// Simple iteration and access.
BufferList::IterImpl iter(bl.Iter());
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialSize);
MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialSize));
MOZ_RELEASE_ASSERT(!iter.HasRoomFor(kInitialSize + 1));
MOZ_RELEASE_ASSERT(!iter.HasRoomFor(size_t(-1)));
MOZ_RELEASE_ASSERT(*iter.Data() == 0x0c);
MOZ_RELEASE_ASSERT(!iter.Done());
iter.Advance(bl, 4);
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialSize - 4);
MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialSize - 4));
MOZ_RELEASE_ASSERT(*iter.Data() == 0x0c);
MOZ_RELEASE_ASSERT(!iter.Done());
iter.Advance(bl, 11);
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialSize - 4 - 11);
MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialSize - 4 - 11));
MOZ_RELEASE_ASSERT(!iter.HasRoomFor(kInitialSize - 4 - 11 + 1));
MOZ_RELEASE_ASSERT(*iter.Data() == 0x0c);
MOZ_RELEASE_ASSERT(!iter.Done());
iter.Advance(bl, kInitialSize - 4 - 11);
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == 0);
MOZ_RELEASE_ASSERT(!iter.HasRoomFor(1));
MOZ_RELEASE_ASSERT(iter.Done());
// Writing to the buffer.
const size_t kSmallWrite = 16;
char toWrite[kSmallWrite];
memset(toWrite, 0x0a, kSmallWrite);
bl.WriteBytes(toWrite, kSmallWrite);
MOZ_RELEASE_ASSERT(bl.Size() == kInitialSize + kSmallWrite);
iter = bl.Iter();
iter.Advance(bl, kInitialSize);
MOZ_RELEASE_ASSERT(!iter.Done());
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialCapacity - kInitialSize);
MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialCapacity - kInitialSize));
MOZ_RELEASE_ASSERT(*iter.Data() == 0x0a);
// AdvanceAcrossSegments.
iter = bl.Iter();
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kInitialCapacity - 4));
MOZ_RELEASE_ASSERT(!iter.Done());
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == 4);
MOZ_RELEASE_ASSERT(iter.HasRoomFor(4));
MOZ_RELEASE_ASSERT(*iter.Data() == 0x0a);
iter = bl.Iter();
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite - 4));
MOZ_RELEASE_ASSERT(!iter.Done());
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == 4);
MOZ_RELEASE_ASSERT(iter.HasRoomFor(4));
MOZ_RELEASE_ASSERT(*iter.Data() == 0x0a);
MOZ_RELEASE_ASSERT(bl.Iter().AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite - 1));
MOZ_RELEASE_ASSERT(bl.Iter().AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite));
MOZ_RELEASE_ASSERT(!bl.Iter().AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite + 1));
MOZ_RELEASE_ASSERT(!bl.Iter().AdvanceAcrossSegments(bl, size_t(-1)));
// Reading non-contiguous bytes.
char toRead[kSmallWrite];
iter = bl.Iter();
iter.Advance(bl, kInitialSize);
bl.ReadBytes(iter, toRead, kSmallWrite);
MOZ_RELEASE_ASSERT(memcmp(toRead, toWrite, kSmallWrite) == 0);
MOZ_RELEASE_ASSERT(iter.Done());
// Make sure reading up to the end of a segment advances the iter to the next
// segment.
iter = bl.Iter();
bl.ReadBytes(iter, toRead, kInitialSize);
MOZ_RELEASE_ASSERT(!iter.Done());
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialCapacity - kInitialSize);
const size_t kBigWrite = 1024;
char* toWriteBig = static_cast<char*>(malloc(kBigWrite));
for (unsigned i = 0; i < kBigWrite; i++) {
toWriteBig[i] = i % 37;
}
bl.WriteBytes(toWriteBig, kBigWrite);
char* toReadBig = static_cast<char*>(malloc(kBigWrite));
iter = bl.Iter();
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite));
bl.ReadBytes(iter, toReadBig, kBigWrite);
MOZ_RELEASE_ASSERT(memcmp(toReadBig, toWriteBig, kBigWrite) == 0);
MOZ_RELEASE_ASSERT(iter.Done());
free(toReadBig);
free(toWriteBig);
// Currently bl contains these segments:
// #0: offset 0, [0x0c]*16 + [0x0a]*8, size 24
// #1: offset 24, [0x0a]*8 + [i%37 for i in 0..24], size 32
// #2: offset 56, [i%37 for i in 24..56, size 32
// ...
// #32: offset 1016, [i%37 for i in 984..1016], size 32
// #33: offset 1048, [i%37 for i in 1016..1024], size 8
static size_t kTotalSize = kInitialSize + kSmallWrite + kBigWrite;
MOZ_RELEASE_ASSERT(bl.Size() == kTotalSize);
static size_t kLastSegmentSize = (kTotalSize - kInitialCapacity) % kStandardCapacity;
iter = bl.Iter();
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kTotalSize - kLastSegmentSize - kStandardCapacity));
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kStandardCapacity);
iter.Advance(bl, kStandardCapacity);
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kLastSegmentSize);
MOZ_RELEASE_ASSERT(unsigned(*iter.Data()) == (kTotalSize - kLastSegmentSize - kInitialSize - kSmallWrite) % 37);
// Flattening.
const size_t kFlattenSize = 1000;
const char* flat;
iter = bl.Iter();
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kInitialSize));
MOZ_RELEASE_ASSERT(bl.FlattenBytes(iter, &flat, kFlattenSize));
MOZ_RELEASE_ASSERT(flat[0] == 0x0a);
MOZ_RELEASE_ASSERT(flat[kSmallWrite / 2] == 0x0a);
for (size_t i = kSmallWrite; i < kFlattenSize; i++) {
MOZ_RELEASE_ASSERT(unsigned(flat[i]) == (i - kSmallWrite) % 37);
}
MOZ_RELEASE_ASSERT(unsigned(*iter.Data()) == (kFlattenSize - kSmallWrite) % 37);
const size_t kSecondFlattenSize = 40;
MOZ_RELEASE_ASSERT(bl.FlattenBytes(iter, &flat, kSecondFlattenSize));
for (size_t i = 0; i < kSecondFlattenSize; i++) {
MOZ_RELEASE_ASSERT(unsigned(flat[i]) == (i + kFlattenSize - kInitialSize) % 37);
}
MOZ_RELEASE_ASSERT(iter.Done());
// Clear.
bl.Clear();
MOZ_RELEASE_ASSERT(bl.Size() == 0);
MOZ_RELEASE_ASSERT(bl.Iter().Done());
// Move assignment.
const size_t kSmallCapacity = 8;
BufferList bl2(0, kSmallCapacity, kSmallCapacity);
bl2.WriteBytes(toWrite, kSmallWrite);
bl2.WriteBytes(toWrite, kSmallWrite);
bl2.WriteBytes(toWrite, kSmallWrite);
bl = mozilla::Move(bl2);
MOZ_RELEASE_ASSERT(bl2.Size() == 0);
MOZ_RELEASE_ASSERT(bl2.Iter().Done());
iter = bl.Iter();
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kSmallWrite * 3));
MOZ_RELEASE_ASSERT(iter.Done());
// Borrowing.
const size_t kBorrowStart = 4;
const size_t kBorrowSize = 24;
iter = bl.Iter();
iter.Advance(bl, kBorrowStart);
bool success;
bl2 = bl.Borrow<InfallibleAllocPolicy>(iter, kBorrowSize, &success);
MOZ_RELEASE_ASSERT(success);
MOZ_RELEASE_ASSERT(bl2.Size() == kBorrowSize);
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kSmallWrite * 3 - kBorrowSize - kBorrowStart));
MOZ_RELEASE_ASSERT(iter.Done());
iter = bl2.Iter();
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl2, kBorrowSize));
MOZ_RELEASE_ASSERT(iter.Done());
BufferList::IterImpl iter1(bl.Iter()), iter2(bl2.Iter());
iter1.Advance(bl, kBorrowStart);
MOZ_RELEASE_ASSERT(iter1.Data() == iter2.Data());
MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl, kBorrowSize - 5));
MOZ_RELEASE_ASSERT(iter2.AdvanceAcrossSegments(bl2, kBorrowSize - 5));
MOZ_RELEASE_ASSERT(iter1.Data() == iter2.Data());
return 0;
}
+1
View File
@@ -9,6 +9,7 @@ CppUnitTests([
'TestAtomics',
'TestBinarySearch',
'TestBloomFilter',
'TestBufferList',
'TestCasting',
'TestCeilingFloor',
'TestCheckedInt',
+2 -12
View File
@@ -121,12 +121,7 @@ struct ParamTraits<mozilla::net::NetAddr>
return false;
if (aResult->raw.family == AF_UNSPEC) {
const char *tmp;
if (aMsg->ReadBytes(aIter, &tmp, sizeof(aResult->raw.data))) {
memcpy(&(aResult->raw.data), tmp, sizeof(aResult->raw.data));
return true;
}
return false;
return aMsg->ReadBytesInto(aIter, &aResult->raw.data, sizeof(aResult->raw.data));
} else if (aResult->raw.family == AF_INET) {
return ReadParam(aMsg, aIter, &aResult->inet.port) &&
ReadParam(aMsg, aIter, &aResult->inet.ip);
@@ -138,12 +133,7 @@ struct ParamTraits<mozilla::net::NetAddr>
ReadParam(aMsg, aIter, &aResult->inet6.scope_id);
#if defined(XP_UNIX)
} else if (aResult->raw.family == AF_LOCAL) {
const char *tmp;
if (aMsg->ReadBytes(aIter, &tmp, sizeof(aResult->local.path))) {
memcpy(&(aResult->local.path), tmp, sizeof(aResult->local.path));
return true;
}
return false;
return aMsg->ReadBytesInto(aIter, &aResult->local.path, sizeof(aResult->local.path));
#endif
}
+1 -6
View File
@@ -49,12 +49,7 @@ struct ParamTraits<mozilla::BaseEventFlags>
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
{
const char* outp;
if (!aMsg->ReadBytes(aIter, &outp, sizeof(*aResult))) {
return false;
}
*aResult = *reinterpret_cast<const paramType*>(outp);
return true;
return aMsg->ReadBytesInto(aIter, aResult, sizeof(*aResult));
}
};