diff --git a/dom/ipc/ContentBridgeChild.cpp b/dom/ipc/ContentBridgeChild.cpp index d40d9d5a2d..f206eb7c4c 100644 --- a/dom/ipc/ContentBridgeChild.cpp +++ b/dom/ipc/ContentBridgeChild.cpp @@ -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; diff --git a/dom/ipc/ContentBridgeParent.cpp b/dom/ipc/ContentBridgeParent.cpp index e71febd056..b8f247699b 100644 --- a/dom/ipc/ContentBridgeParent.cpp +++ b/dom/ipc/ContentBridgeParent.cpp @@ -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; diff --git a/dom/ipc/StructuredCloneData.cpp b/dom/ipc/StructuredCloneData.cpp index 6b3d6c60fc..273f0d05f5 100644 --- a/dom/ipc/StructuredCloneData.cpp +++ b/dom/ipc/StructuredCloneData.cpp @@ -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(reinterpret_cast(&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; } diff --git a/dom/ipc/StructuredCloneData.h b/dom/ipc/StructuredCloneData.h index 849bc8a956..016f8104ec 100644 --- a/dom/ipc/StructuredCloneData.h +++ b/dom/ipc/StructuredCloneData.h @@ -31,19 +31,27 @@ public: } static already_AddRefed - 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 sharedData = new SharedJSAllocatedData(data, aDataLength); return sharedData.forget(); } + static already_AddRefed + CreateFromExternalData(const void* aData, size_t aDataLength) + { + RefPtr sharedData = + AllocateForExternalData(aDataLength); + memcpy(sharedData->Data(), aData, aDataLength); + return sharedData.forget(); + } + NS_INLINE_DECL_REFCOUNTING(SharedJSAllocatedData) uint64_t* Data() const { return mData; } diff --git a/dom/media/gmp/GMPChild.cpp b/dom/media/gmp/GMPChild.cpp index abaca5f113..d94fe522ee 100644 --- a/dom/media/gmp/GMPChild.cpp +++ b/dom/media/gmp/GMPChild.cpp @@ -26,6 +26,7 @@ #include #include "GMPUtils.h" #include "prio.h" +#include "base/task.h" #ifdef MOZ_WIDEVINE_EME #include "widevine-adapter/WidevineAdapter.h" #endif diff --git a/dom/media/gmp/GMPContentChild.cpp b/dom/media/gmp/GMPContentChild.cpp index 523d279fc4..198372abdb 100644 --- a/dom/media/gmp/GMPContentChild.cpp +++ b/dom/media/gmp/GMPContentChild.cpp @@ -9,6 +9,7 @@ #include "GMPDecryptorChild.h" #include "GMPVideoDecoderChild.h" #include "GMPVideoEncoderChild.h" +#include "base/task.h" namespace mozilla { namespace gmp { diff --git a/dom/media/gmp/GMPContentParent.cpp b/dom/media/gmp/GMPContentParent.cpp index 4eea05ae2d..4bf256e72e 100644 --- a/dom/media/gmp/GMPContentParent.cpp +++ b/dom/media/gmp/GMPContentParent.cpp @@ -13,6 +13,7 @@ #include "mozIGeckoMediaPluginService.h" #include "mozilla/Logging.h" #include "mozilla/unused.h" +#include "base/task.h" namespace mozilla { diff --git a/dom/media/gmp/GMPDecryptorChild.cpp b/dom/media/gmp/GMPDecryptorChild.cpp index ebecf239e8..fe0f729105 100644 --- a/dom/media/gmp/GMPDecryptorChild.cpp +++ b/dom/media/gmp/GMPDecryptorChild.cpp @@ -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" diff --git a/dom/media/gmp/GMPServiceChild.cpp b/dom/media/gmp/GMPServiceChild.cpp index 0569a67418..45ba696658 100644 --- a/dom/media/gmp/GMPServiceChild.cpp +++ b/dom/media/gmp/GMPServiceChild.cpp @@ -13,6 +13,7 @@ #include "nsXPCOMPrivate.h" #include "mozilla/SyncRunnable.h" #include "runnable_utils.h" +#include "base/task.h" namespace mozilla { diff --git a/dom/media/gmp/GMPServiceParent.cpp b/dom/media/gmp/GMPServiceParent.cpp index ba59c82ce8..473b6ddcfa 100644 --- a/dom/media/gmp/GMPServiceParent.cpp +++ b/dom/media/gmp/GMPServiceParent.cpp @@ -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" diff --git a/dom/media/gmp/GMPStorageChild.cpp b/dom/media/gmp/GMPStorageChild.cpp index 37186c16bd..052b56d1b0 100644 --- a/dom/media/gmp/GMPStorageChild.cpp +++ b/dom/media/gmp/GMPStorageChild.cpp @@ -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()) diff --git a/dom/plugins/ipc/BrowserStreamChild.h b/dom/plugins/ipc/BrowserStreamChild.h index 77f7fb813e..ad334e4a3b 100644 --- a/dom/plugins/ipc/BrowserStreamChild.h +++ b/dom/plugins/ipc/BrowserStreamChild.h @@ -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 { diff --git a/dom/plugins/ipc/NPEventAndroid.h b/dom/plugins/ipc/NPEventAndroid.h index 902657da26..f664af8573 100644 --- a/dom/plugins/ipc/NPEventAndroid.h +++ b/dom/plugins/ipc/NPEventAndroid.h @@ -39,14 +39,7 @@ struct ParamTraits 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) diff --git a/dom/plugins/ipc/NPEventUnix.h b/dom/plugins/ipc/NPEventUnix.h index 5c3c0c8bf5..4cc9a54564 100644 --- a/dom/plugins/ipc/NPEventUnix.h +++ b/dom/plugins/ipc/NPEventUnix.h @@ -57,14 +57,10 @@ struct ParamTraits // 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 diff --git a/dom/plugins/ipc/NPEventWindows.h b/dom/plugins/ipc/NPEventWindows.h index 3e9aba201b..1da8fa7388 100644 --- a/dom/plugins/ipc/NPEventWindows.h +++ b/dom/plugins/ipc/NPEventWindows.h @@ -132,12 +132,9 @@ struct ParamTraits 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 diff --git a/dom/plugins/ipc/PluginMessageUtils.h b/dom/plugins/ipc/PluginMessageUtils.h index aab77f7014..72daaf272d 100644 --- a/dom/plugins/ipc/PluginMessageUtils.h +++ b/dom/plugins/ipc/PluginMessageUtils.h @@ -459,15 +459,19 @@ struct ParamTraits return false; } - UniChar* buffer = nullptr; + // Avoid integer multiplication overflow. + if (length > INT_MAX / static_cast(sizeof(UniChar))) { + return false; + } + + auto chars = mozilla::MakeUnique(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 return false; } - uint8_t* data = nullptr; + auto data = mozilla::MakeUnique(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; } diff --git a/gfx/ipc/D3DMessageUtils.cpp b/gfx/ipc/D3DMessageUtils.cpp index 16642fd456..f3895fdae9 100644 --- a/gfx/ipc/D3DMessageUtils.cpp +++ b/gfx/ipc/D3DMessageUtils.cpp @@ -56,11 +56,9 @@ bool ParamTraits::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) && diff --git a/ipc/chromium/moz.build b/ipc/chromium/moz.build index 0e8d333d68..c35fa243f4 100644 --- a/ipc/chromium/moz.build +++ b/ipc/chromium/moz.build @@ -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', diff --git a/ipc/chromium/src/base/buffer.cc b/ipc/chromium/src/base/buffer.cc deleted file mode 100644 index 789a8873ce..0000000000 --- a/ipc/chromium/src/base/buffer.cc +++ /dev/null @@ -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; -} diff --git a/ipc/chromium/src/base/buffer.h b/ipc/chromium/src/base/buffer.h deleted file mode 100644 index 4e95a5bb3a..0000000000 --- a/ipc/chromium/src/base/buffer.h +++ /dev/null @@ -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_ diff --git a/ipc/chromium/src/base/histogram.cc b/ipc/chromium/src/base/histogram.cc index 0cafff5e09..dd4a6aec73 100644 --- a/ipc/chromium/src/base/histogram.cc +++ b/ipc/chromium/src/base/histogram.cc @@ -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(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(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(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. diff --git a/ipc/chromium/src/base/histogram.h b/ipc/chromium/src/base/histogram.h index aeafbd9776..132898ae78 100644 --- a/ipc/chromium/src/base/histogram.h +++ b/ipc/chromium/src/base/histogram.h @@ -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); } 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 diff --git a/ipc/chromium/src/base/message_loop.h b/ipc/chromium/src/base/message_loop.h index 2d1a16c2a2..c07d7e0798 100644 --- a/ipc/chromium/src/base/message_loop.h +++ b/ipc/chromium/src/base/message_loop.h @@ -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; diff --git a/ipc/chromium/src/base/pickle.cc b/ipc/chromium/src/base/pickle.cc index 0a0118bb4f..9cb964ee08 100644 --- a/ipc/chromium/src/base/pickle.cc +++ b/ipc/chromium/src/base/pickle.cc @@ -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 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 struct Copier { - 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 #endif static_assert(MOZ_ALIGNOF(uint32_t*) == MOZ_ALIGNOF(void*), "Pointers have different alignments"); - uint32_t* src = *reinterpret_cast(iter); + const uint32_t* src = reinterpret_cast(iter); uint32_t* uint32dest = reinterpret_cast(dest); uint32dest[loIndex] = src[loIndex]; uint32dest[hiIndex] = src[hiIndex]; @@ -80,96 +81,81 @@ struct Copier template struct Copier { - 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(iter)); + static void Copy(T* dest, const char* iter) { + *dest = *reinterpret_cast(iter); } }; } // anonymous namespace -template -void -PickleIterator::CopyFrom(T* dest) { - static_assert(mozilla::IsPod::value, "Copied type must be a POD type"); - Copier::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(pickle.payload())) { +template +void +PickleIterator::CopyInto(T* dest) { + static_assert(mozilla::IsPod::value, "Copied type must be a POD type"); + Copier::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(header_size) >= sizeof(Header)); - DCHECK(header_size <= kPayloadUnit); - Resize(kPayloadUnit); - if (!header_) { - NS_ABORT_OOM(kPayloadUnit); - } + DCHECK(header_size_ <= kHeaderSegmentCapacity); + header_ = reinterpret_cast(buffers_.Start()); header_->payload_size = 0; } -Pickle::Pickle(const char* data, int data_len, Ownership ownership) - : header_(reinterpret_cast(const_cast(data))), - header_size_(0), - capacity_(ownership == BORROWS ? kCapacityReadOnly : data_len), - variable_buffer_offset_(0) { - if (data_len >= static_cast(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(header_size) >= sizeof(Header)); + DCHECK(header_size <= kHeaderSegmentCapacity); + MOZ_RELEASE_ASSERT(header_size <= length); - if (header_size_ > static_cast(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(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(big_result); - iter->CopyFrom(&bigResult); - DCHECK(bigResult <= LONG_MAX && bigResult >= LONG_MIN); - *result = static_cast(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(big_result); - iter->CopyFrom(&bigResult); - DCHECK(bigResult <= ULONG_MAX); - *result = static_cast(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::max()); + *result = static_cast(big_result); - iter->CopyFrom(&bigResult); - DCHECK(bigResult <= std::numeric_limits::max()); - *result = static_cast(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::max() && bigResult >= std::numeric_limits::min()); - *result = static_cast(bigResult); + DCHECK(big_result <= std::numeric_limits::max() && big_result >= std::numeric_limits::min()); + *result = static_cast(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(len); + if (!ReadBytesInto(iter, chars.get(), len)) { return false; + } + result->assign(chars.get(), len); - char* chars = reinterpret_cast(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(sizeof(wchar_t))) return false; - if (!IteratorHasRoomFor(*iter, len * sizeof(wchar_t))) + + auto chars = mozilla::MakeUnique(len); + if (!ReadBytesInto(iter, chars.get(), len * sizeof(wchar_t))) { return false; + } + result->assign(chars.get(), len); - wchar_t* chars = reinterpret_cast(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(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(iter->iter_) + paddingLen; - DCHECK(intptr_t(*data) % alignment == 0); + if (!buffers_.FlattenBytes(iter->iter_, data, length)) { + return false; + } - UpdateIter(iter, length); + header_ = reinterpret_cast(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(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(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::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(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(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(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::align(new_capacity); - - void* p = moz_xrealloc(header_, new_capacity); - - header_ = reinterpret_cast(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(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(kPayloadUnit)); - - if (end < start) - return nullptr; - size_t length = static_cast(end - start); - if (length < sizeof(Header)) - return nullptr; - - const Header* hdr = reinterpret_cast(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(kPayloadUnit)); + DCHECK(header_size <= static_cast(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 sum(header_size); + sum += hdr->payload_size; + + if (!sum.isValid()) + return 0; + + return sum.value(); } diff --git a/ipc/chromium/src/base/pickle.h b/ipc/chromium/src/base/pickle.h index 55b5e7081f..f3d57bd674 100644 --- a/ipc/chromium/src/base/pickle.h +++ b/ipc/chromium/src/base/pickle.h @@ -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 - void CopyFrom(T* dest); + mozilla::BufferList::IterImpl iter_; - void* iter_; + template + 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(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 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(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(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(header_) + header_size_; - } - const char* payload() const { - return reinterpret_cast(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::align(bytes); } + static uint32_t AlignCapacity(int bytes) { + return ConstantAligner::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(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__ diff --git a/ipc/chromium/src/chrome/common/ipc_channel.h b/ipc/chromium/src/chrome/common/ipc_channel.h index d3898a6ff4..ece2559db4 100644 --- a/ipc/chromium/src/chrome/common/ipc_channel.h +++ b/ipc/chromium/src/chrome/common/ipc_channel.h @@ -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, diff --git a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc index 94e972b0f9..66d4469cfc 100644 --- a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc +++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc @@ -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(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(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(&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(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(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(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(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()) diff --git a/ipc/chromium/src/chrome/common/ipc_channel_posix.h b/ipc/chromium/src/chrome/common/ipc_channel_posix.h index a55863f5a4..1dfa330575 100644 --- a/ipc/chromium/src/chrome/common/ipc_channel_posix.h +++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.h @@ -16,12 +16,14 @@ #include #include -#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 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 incoming_message_; std::vector input_overflow_fds_; // In server-mode, we have to wait for the client to connect before we diff --git a/ipc/chromium/src/chrome/common/ipc_channel_win.cc b/ipc/chromium/src/chrome/common/ipc_channel_win.cc index 7f2e3647af..785a35c761 100644 --- a/ipc/chromium/src/chrome/common/ipc_channel_win.cc +++ b/ipc/chromium/src/chrome/common/ipc_channel_win.cc @@ -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(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) { diff --git a/ipc/chromium/src/chrome/common/ipc_channel_win.h b/ipc/chromium/src/chrome/common/ipc_channel_win.h index 68c4670168..1d5cd7aa25 100644 --- a/ipc/chromium/src/chrome/common/ipc_channel_win.h +++ b/ipc/chromium/src/chrome/common/ipc_channel_win.h @@ -12,10 +12,12 @@ #include #include -#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 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 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 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 diff --git a/ipc/chromium/src/chrome/common/ipc_message.cc b/ipc/chromium/src/chrome/common/ipc_message.cc index 02c3dca4f7..926fee0c68 100644 --- a/ipc/chromium/src/chrome/common/ipc_message.cc +++ b/ipc/chromium/src/chrome/common/ipc_message.cc @@ -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(); diff --git a/ipc/chromium/src/chrome/common/ipc_message.h b/ipc/chromium/src/chrome/common/ipc_message.h index 5256cd0827..cd8e143820 100644 --- a/ipc/chromium/src/chrome/common/ipc_message.h +++ b/ipc/chromium/src/chrome/common/ipc_message.h @@ -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) diff --git a/ipc/chromium/src/chrome/common/ipc_message_utils.h b/ipc/chromium/src/chrome/common/ipc_message_utils.h index 09c9dbd6d6..e8ea9ad3aa 100644 --- a/ipc/chromium/src/chrome/common/ipc_message_utils.h +++ b/ipc/chromium/src/chrome/common/ipc_message_utils.h @@ -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 { typedef long long param_type; static void Write(Message* m, const param_type& p) { - m->WriteData(reinterpret_cast(&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 { typedef unsigned long long param_type; static void Write(Message* m, const param_type& p) { - m->WriteData(reinterpret_cast(&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 { typedef double param_type; static void Write(Message* m, const param_type& p) { - m->WriteData(reinterpret_cast(&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)); diff --git a/ipc/glue/BackgroundImpl.cpp b/ipc/glue/BackgroundImpl.cpp index 5a77e436dc..31650de398 100644 --- a/ipc/glue/BackgroundImpl.cpp +++ b/ipc/glue/BackgroundImpl.cpp @@ -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" diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp index 0a2296c41d..83c4f9d2b5 100644 --- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -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 diff --git a/ipc/glue/IPCMessageUtils.h b/ipc/glue/IPCMessageUtils.h index 4195e653ac..0d309e3716 100644 --- a/ipc/glue/IPCMessageUtils.h +++ b/ipc/glue/IPCMessageUtils.h @@ -232,12 +232,7 @@ struct ParamTraits 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(outp); - return true; + return aMsg->ReadBytesInto(aIter, aResult, sizeof(*aResult)); } }; @@ -253,12 +248,7 @@ struct ParamTraits 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(outp); - return true; + return aMsg->ReadBytesInto(aIter, aResult, sizeof(*aResult)); } }; @@ -309,14 +299,12 @@ struct ParamTraits } 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 } uint32_t length; - if (ReadParam(aMsg, aIter, &length)) { - const char16_t* buf; - if (aMsg->ReadBytes(aIter, reinterpret_cast(&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> 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> return false; } } + return true; } - - return true; } static void Log(const paramType& aParam, std::wstring* aLog) @@ -552,11 +530,7 @@ struct ParamTraits 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(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 return false; } - if (aResult->dataLength) { - const char** buffer = - const_cast(reinterpret_cast(&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(reinterpret_cast(&aResult->data)); + // Structured clone data must be 64-bit aligned. + if (!const_cast(aMsg)->FlattenBytes(aIter, buffer, aResult->dataLength, + sizeof(uint64_t))) { + return false; } return true; diff --git a/ipc/glue/MessageChannel.cpp b/ipc/glue/MessageChannel.cpp index 753f945ac1..e4d5b45b18 100644 --- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -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 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; } diff --git a/ipc/glue/MessageLink.cpp b/ipc/glue/MessageLink.cpp index 1dc56687c9..0e011fc773 100644 --- a/ipc/glue/MessageLink.cpp +++ b/ipc/glue/MessageLink.cpp @@ -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(); diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py index f081245770..dff1cfade4 100644 --- a/ipc/ipdl/ipdl/lower.py +++ b/ipc/ipdl/ipdl/lower.py @@ -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'), diff --git a/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp b/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp index 23ced5c302..6a91033bf4 100644 --- a/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp +++ b/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp @@ -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" diff --git a/ipc/ipdl/test/cxx/TestActorPunning.cpp b/ipc/ipdl/test/cxx/TestActorPunning.cpp index ab2f2a4091..0e69ae16a0 100644 --- a/ipc/ipdl/test/cxx/TestActorPunning.cpp +++ b/ipc/ipdl/test/cxx/TestActorPunning.cpp @@ -110,9 +110,8 @@ using namespace mozilla::ipc; /*static*/ void ParamTraits::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(ptr); if (ah->mId != -3) fail("guessed wrong offset (value is %d, should be -3)", ah->mId); @@ -122,9 +121,6 @@ ParamTraits::Write(Message* aMsg, const paramType& aParam) /*static*/ bool ParamTraits::Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) { - const char* ptr; - int len; - mozilla::Unused << aMsg->ReadData(aIter, &ptr, &len); return true; } diff --git a/ipc/ipdl/test/cxx/TestBridgeMain.cpp b/ipc/ipdl/test/cxx/TestBridgeMain.cpp index 9856a43cf0..59c1c522d0 100644 --- a/ipc/ipdl/test/cxx/TestBridgeMain.cpp +++ b/ipc/ipdl/test/cxx/TestBridgeMain.cpp @@ -1,5 +1,6 @@ #include "TestBridgeMain.h" +#include "base/task.h" #include "IPDLUnitTests.h" // fail etc. #include "IPDLUnitTestSubprocess.h" diff --git a/ipc/ipdl/test/cxx/TestCrashCleanup.cpp b/ipc/ipdl/test/cxx/TestCrashCleanup.cpp index 5a0fc339b1..2f335daae0 100644 --- a/ipc/ipdl/test/cxx/TestCrashCleanup.cpp +++ b/ipc/ipdl/test/cxx/TestCrashCleanup.cpp @@ -1,5 +1,6 @@ #include "TestCrashCleanup.h" +#include "base/task.h" #include "mozilla/CondVar.h" #include "mozilla/Mutex.h" diff --git a/ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp index 1efa8482e6..d5e4644cfa 100644 --- a/ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp +++ b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp @@ -3,6 +3,7 @@ #include "TestEndpointBridgeMain.h" +#include "base/task.h" #include "IPDLUnitTests.h" // fail etc. #include "IPDLUnitTestSubprocess.h" diff --git a/ipc/ipdl/test/cxx/TestEndpointOpens.cpp b/ipc/ipdl/test/cxx/TestEndpointOpens.cpp index 27966b2676..9e8898ba83 100644 --- a/ipc/ipdl/test/cxx/TestEndpointOpens.cpp +++ b/ipc/ipdl/test/cxx/TestEndpointOpens.cpp @@ -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" diff --git a/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp index 748f1c5e65..21a5bc5924 100644 --- a/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp +++ b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp @@ -1,5 +1,6 @@ #include "TestInterruptErrorCleanup.h" +#include "base/task.h" #include "mozilla/CondVar.h" #include "mozilla/Mutex.h" diff --git a/ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp b/ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp index 9f766798ab..38f7993da7 100644 --- a/ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp +++ b/ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp @@ -1,5 +1,6 @@ #include "TestInterruptShutdownRace.h" +#include "base/task.h" #include "IPDLUnitTests.h" // fail etc. #include "IPDLUnitTestSubprocess.h" diff --git a/ipc/ipdl/test/cxx/TestOpens.cpp b/ipc/ipdl/test/cxx/TestOpens.cpp index 0f6ea07793..2ddca74fd3 100644 --- a/ipc/ipdl/test/cxx/TestOpens.cpp +++ b/ipc/ipdl/test/cxx/TestOpens.cpp @@ -1,3 +1,4 @@ +#include "base/task.h" #include "base/thread.h" #include "TestOpens.h" diff --git a/ipc/ipdl/test/cxx/TestStackHooks.cpp b/ipc/ipdl/test/cxx/TestStackHooks.cpp index 0f6e93e368..b4181985c5 100644 --- a/ipc/ipdl/test/cxx/TestStackHooks.cpp +++ b/ipc/ipdl/test/cxx/TestStackHooks.cpp @@ -1,5 +1,6 @@ #include "TestStackHooks.h" +#include "base/task.h" #include "IPDLUnitTests.h" // fail etc. diff --git a/ipc/ipdl/test/cxx/TestSyncHang.cpp b/ipc/ipdl/test/cxx/TestSyncHang.cpp index 830aa3fb74..62b9ae723a 100644 --- a/ipc/ipdl/test/cxx/TestSyncHang.cpp +++ b/ipc/ipdl/test/cxx/TestSyncHang.cpp @@ -1,4 +1,5 @@ #include "TestSyncHang.h" +#include "base/task.h" #include "mozilla/ipc/GeckoChildProcessHost.h" #include "IPDLUnitTests.h" // fail etc. diff --git a/memory/mozalloc/mozalloc.h b/memory/mozalloc/mozalloc.h index c9d8c4fefa..f9c7617d5c 100644 --- a/memory/mozalloc/mozalloc.h +++ b/memory/mozalloc/mozalloc.h @@ -329,6 +329,11 @@ public: void reportAllocOverflow() const { } + + bool checkSimulatedOOM() const + { + return true; + } }; #endif /* ifdef __cplusplus */ diff --git a/mfbt/AllocPolicy.h b/mfbt/AllocPolicy.h index 0237ce429d..139ecbde5b 100644 --- a/mfbt/AllocPolicy.h +++ b/mfbt/AllocPolicy.h @@ -12,6 +12,7 @@ #ifndef mozilla_AllocPolicy_h #define mozilla_AllocPolicy_h +#include "mozilla/Attributes.h" #include "mozilla/TemplateLib.h" #include diff --git a/mfbt/BufferList.h b/mfbt/BufferList.h new file mode 100644 index 0000000000..5965c457f0 --- /dev/null +++ b/mfbt/BufferList.h @@ -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 +#include "mozilla/AllocPolicy.h" +#include "mozilla/Move.h" +#include "mozilla/Types.h" +#include "mozilla/TypeTraits.h" +#include "mozilla/Vector.h" +#include + +// 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 +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 + BufferList 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(aCapacity); + if (!data) { + return nullptr; + } + if (!mSegments.append(Segment(data, aSize, aCapacity))) { + this->free_(data); + return nullptr; + } + mSize += aSize; + return data; + } + + bool mOwning; + Vector mSegments; + size_t mSize; + size_t mStandardCapacity; +}; + +template +bool +BufferList::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 +bool +BufferList::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 +bool +BufferList::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(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 template +BufferList +BufferList::Borrow(IterImpl& aIter, size_t aSize, bool* aSuccess, + BorrowingAllocPolicy aAP) +{ + BufferList 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 */ diff --git a/mfbt/moz.build b/mfbt/moz.build index 8e5b005931..d7f104e545 100644 --- a/mfbt/moz.build +++ b/mfbt/moz.build @@ -24,6 +24,7 @@ EXPORTS.mozilla = [ 'Attributes.h', 'BinarySearch.h', 'BloomFilter.h', + 'BufferList.h', 'Casting.h', 'ChaosMode.h', 'Char16.h', diff --git a/mfbt/tests/TestBufferList.cpp b/mfbt/tests/TestBufferList.cpp new file mode 100644 index 0000000000..e736ee13be --- /dev/null +++ b/mfbt/tests/TestBufferList.cpp @@ -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 + T* pod_malloc(size_t aNumElems) + { + if (aNumElems & mozilla::tl::MulOverflowMask::value) { + MOZ_CRASH("TestBufferList.cpp: overflow"); + } + T* rv = static_cast(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 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(malloc(kBigWrite)); + for (unsigned i = 0; i < kBigWrite; i++) { + toWriteBig[i] = i % 37; + } + bl.WriteBytes(toWriteBig, kBigWrite); + + char* toReadBig = static_cast(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(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; +} diff --git a/mfbt/tests/moz.build b/mfbt/tests/moz.build index f95e36a7e4..0bbad94e07 100644 --- a/mfbt/tests/moz.build +++ b/mfbt/tests/moz.build @@ -9,6 +9,7 @@ CppUnitTests([ 'TestAtomics', 'TestBinarySearch', 'TestBloomFilter', + 'TestBufferList', 'TestCasting', 'TestCeilingFloor', 'TestCheckedInt', diff --git a/netwerk/ipc/NeckoMessageUtils.h b/netwerk/ipc/NeckoMessageUtils.h index 7a38d254b4..710b307279 100644 --- a/netwerk/ipc/NeckoMessageUtils.h +++ b/netwerk/ipc/NeckoMessageUtils.h @@ -121,12 +121,7 @@ struct ParamTraits 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 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 } diff --git a/widget/nsGUIEventIPC.h b/widget/nsGUIEventIPC.h index adb373e23b..6e3628b6a9 100644 --- a/widget/nsGUIEventIPC.h +++ b/widget/nsGUIEventIPC.h @@ -49,12 +49,7 @@ struct ParamTraits 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(outp); - return true; + return aMsg->ReadBytesInto(aIter, aResult, sizeof(*aResult)); } };