diff --git a/configure.in b/configure.in index 1bcdbfb08d..15791f89e4 100644 --- a/configure.in +++ b/configure.in @@ -1742,6 +1742,24 @@ if test "$MOZ_PROFILING" -a -z "$STRIP_FLAGS"; then esac fi +dnl ======================================================== +dnl = Enable Gecko integrated IPC fuzzer +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(ipc-fuzzer, +[ --enable-ipc-fuzzer Enable IPC fuzzer (default=no)], + MOZ_FAULTY=1, + MOZ_FAULTY= ) +AC_SUBST(MOZ_FAULTY) + +if test -n "$MOZ_FAULTY" && test "$OS_ARCH" = "WINNT"; then + AC_MSG_ERROR([--enable-ipc-fuzzer is not supported on this platform.]) +fi + +if test -n "$MOZ_FAULTY"; then + MOZ_FAULTY=1 + AC_DEFINE(MOZ_FAULTY) +fi + dnl ======================================================== dnl = Enable DMD dnl ======================================================== diff --git a/dom/base/WebSocket.cpp b/dom/base/WebSocket.cpp index 3dca8ffe9c..14f2ca7f3c 100644 --- a/dom/base/WebSocket.cpp +++ b/dom/base/WebSocket.cpp @@ -2824,6 +2824,12 @@ WebSocketImpl::Dispatch(already_AddRefed&& aEvent, uint32_t aFlags) return NS_OK; } +NS_IMETHODIMP +WebSocketImpl::DelayedDispatch(already_AddRefed&&, uint32_t) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + NS_IMETHODIMP WebSocketImpl::IsOnCurrentThread(bool* aResult) { diff --git a/dom/canvas/WebGLContextLossHandler.cpp b/dom/canvas/WebGLContextLossHandler.cpp index 6c7530d5be..771ce4c31c 100644 --- a/dom/canvas/WebGLContextLossHandler.cpp +++ b/dom/canvas/WebGLContextLossHandler.cpp @@ -77,6 +77,13 @@ ContextLossWorkerEventTarget::Dispatch(already_AddRefed&& aEvent, return mEventTarget->Dispatch(wrappedEvent, aFlags); } +NS_IMETHODIMP +ContextLossWorkerEventTarget::DelayedDispatch(already_AddRefed&&, + uint32_t) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + NS_IMETHODIMP ContextLossWorkerEventTarget::IsOnCurrentThread(bool* aResult) { diff --git a/dom/plugins/base/nsJSNPRuntime.h b/dom/plugins/base/nsJSNPRuntime.h index c9274ecf54..a745e8ff8f 100644 --- a/dom/plugins/base/nsJSNPRuntime.h +++ b/dom/plugins/base/nsJSNPRuntime.h @@ -38,8 +38,17 @@ public: JS::TraceEdge(trc, &mJSObj, "nsJSObjWrapperKey"); } + nsJSObjWrapperKey(const nsJSObjWrapperKey& other) + : mJSObj(other.mJSObj), + mNpp(other.mNpp) + {} + void operator=(const nsJSObjWrapperKey& other) { + mJSObj = other.mJSObj; + mNpp = other.mNpp; + } + JS::Heap mJSObj; - const NPP mNpp; + NPP mNpp; }; class nsJSObjWrapper : public NPObject diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 2cb18e76c9..af8e14e256 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -1826,6 +1826,12 @@ TimerThreadEventTarget::Dispatch(already_AddRefed&& aRunnable, uint return NS_OK; } +NS_IMETHODIMP +TimerThreadEventTarget::DelayedDispatch(already_AddRefed&&, uint32_t) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + NS_IMETHODIMP TimerThreadEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread) { @@ -6720,6 +6726,14 @@ EventTarget::Dispatch(already_AddRefed&& aRunnable, uint32_t aFlags return NS_OK; } +template +NS_IMETHODIMP +WorkerPrivateParent:: +EventTarget::DelayedDispatch(already_AddRefed&&, uint32_t) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + template NS_IMETHODIMP WorkerPrivateParent:: diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index 7f48c49cdc..80255e7df9 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -1528,6 +1528,9 @@ protected: NS_IMETHOD Dispatch(already_AddRefed&& aRunnable, uint32_t aFlags) override; + NS_IMETHOD + DelayedDispatch(already_AddRefed&&, uint32_t) override; + NS_IMETHOD IsOnCurrentThread(bool* aIsOnCurrentThread) override; }; diff --git a/dom/workers/WorkerThread.cpp b/dom/workers/WorkerThread.cpp index 3cd21d960b..a93075a8a4 100644 --- a/dom/workers/WorkerThread.cpp +++ b/dom/workers/WorkerThread.cpp @@ -292,6 +292,12 @@ WorkerThread::Dispatch(already_AddRefed&& aRunnable, uint32_t aFlag return NS_OK; } +NS_IMETHODIMP +WorkerThread::DelayedDispatch(already_AddRefed&&, uint32_t) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + uint32_t WorkerThread::RecursionDepth(const WorkerThreadFriendKey& /* aKey */) const { diff --git a/dom/workers/WorkerThread.h b/dom/workers/WorkerThread.h index 3140432a64..1df76c4bc6 100644 --- a/dom/workers/WorkerThread.h +++ b/dom/workers/WorkerThread.h @@ -88,6 +88,9 @@ private: NS_IMETHOD DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) override; + + NS_IMETHOD + DelayedDispatch(already_AddRefed&&, uint32_t) override; }; } // namespace workers diff --git a/ipc/chromium/src/base/logging.h b/ipc/chromium/src/base/logging.h index d0950fb99c..37523d307e 100644 --- a/ipc/chromium/src/base/logging.h +++ b/ipc/chromium/src/base/logging.h @@ -118,7 +118,7 @@ const mozilla::EmptyLog& operator <<(const mozilla::EmptyLog& log, const T&) #define NOTIMPLEMENTED() CHROMIUM_LOG(ERROR) #undef CHECK -#define CHECK(condition) LOG_IF(FATAL, condition) +#define CHECK(condition) LOG_IF(WARNING, condition) #define DCHECK_EQ(v1, v2) DCHECK((v1) == (v2)) #define DCHECK_NE(v1, v2) DCHECK((v1) != (v2)) diff --git a/ipc/chromium/src/base/message_loop.cc b/ipc/chromium/src/base/message_loop.cc index 8a19bafa7f..683ff3e153 100644 --- a/ipc/chromium/src/base/message_loop.cc +++ b/ipc/chromium/src/base/message_loop.cc @@ -280,6 +280,17 @@ void MessageLoop::PostIdleTask(already_AddRefed task) { // Possibly called on a background thread! void MessageLoop::PostTask_Helper(already_AddRefed task, int delay_ms) { + if (nsIEventTarget* target = pump_->GetXPCOMThread()) { + nsresult rv; + if (delay_ms) { + rv = target->DelayedDispatch(Move(task), delay_ms); + } else { + rv = target->Dispatch(Move(task), 0); + } + MOZ_ALWAYS_SUCCEEDS(rv); + return; + } + PendingTask pending_task(Move(task), true); if (delay_ms > 0) { diff --git a/ipc/chromium/src/base/message_pump.h b/ipc/chromium/src/base/message_pump.h index ffacaa9311..08bdbe30a5 100644 --- a/ipc/chromium/src/base/message_pump.h +++ b/ipc/chromium/src/base/message_pump.h @@ -9,6 +9,8 @@ #include "nsISupportsImpl.h" +class nsIEventTarget; + namespace base { class TimeTicks; @@ -126,6 +128,12 @@ class MessagePump { // used on the thread that called Run. virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) = 0; + // If returned, just use the nsThread. + virtual nsIEventTarget* GetXPCOMThread() + { + return nullptr; + } + protected: virtual ~MessagePump() {}; }; diff --git a/ipc/chromium/src/base/pickle.cc b/ipc/chromium/src/base/pickle.cc index 1c3b9764c6..68116e843e 100644 --- a/ipc/chromium/src/base/pickle.cc +++ b/ipc/chromium/src/base/pickle.cc @@ -516,22 +516,47 @@ bool Pickle::WriteBytes(const void* data, uint32_t data_len, uint32_t alignment) } bool Pickle::WriteString(const std::string& value) { +#ifdef MOZ_FAULTY + std::string v(value); + Singleton::get()->FuzzString(v); + if (!WriteInt(static_cast(v.size()))) + return false; + + return WriteBytes(v.data(), static_cast(v.size())); +#else if (!WriteInt(static_cast(value.size()))) return false; return WriteBytes(value.data(), static_cast(value.size())); +#endif } bool Pickle::WriteWString(const std::wstring& value) { +#ifdef MOZ_FAULTY + std::wstring v(value); + Singleton::get()->FuzzWString(v); + if (!WriteInt(static_cast(v.size()))) + return false; + + return WriteBytes(v.data(), + static_cast(v.size() * sizeof(wchar_t))); +#else if (!WriteInt(static_cast(value.size()))) return false; return WriteBytes(value.data(), static_cast(value.size() * sizeof(wchar_t))); +#endif } bool Pickle::WriteData(const char* data, uint32_t length) { - return WriteInt(length) && WriteBytes(data, length); +#ifdef MOZ_FAULTY + std::string v(data, length); + Singleton::get()->FuzzData(v, v.size()); + return WriteInt(v.size()) && WriteBytes(v.data(), v.size()); +#else + return WriteInt(length) && WriteBytes(data, length); +#endif } void Pickle::InputBytes(const char* data, uint32_t length) { diff --git a/ipc/chromium/src/base/pickle.h b/ipc/chromium/src/base/pickle.h index f3d57bd674..955b7a3929 100644 --- a/ipc/chromium/src/base/pickle.h +++ b/ipc/chromium/src/base/pickle.h @@ -17,6 +17,11 @@ #include "mozilla/BufferList.h" #include "mozilla/mozalloc.h" +#ifdef MOZ_FAULTY +#include "base/singleton.h" +#include "mozilla/ipc/Faulty.h" +#endif + class Pickle; class PickleIterator { @@ -118,45 +123,81 @@ class Pickle { // Pickle, it is important to read them in the order in which they were added // to the Pickle. bool WriteBool(bool value) { +#ifdef MOZ_FAULTY + Singleton::get()->FuzzBool(&value); +#endif return WriteInt(value ? 1 : 0); } bool WriteInt16(int16_t value) { +#ifdef MOZ_FAULTY + Singleton::get()->FuzzInt16(&value); +#endif return WriteBytes(&value, sizeof(value)); } bool WriteUInt16(uint16_t value) { +#ifdef MOZ_FAULTY + Singleton::get()->FuzzUInt16(&value); +#endif return WriteBytes(&value, sizeof(value)); } bool WriteInt(int value) { +#ifdef MOZ_FAULTY + Singleton::get()->FuzzInt(&value); +#endif return WriteBytes(&value, sizeof(value)); } bool WriteLong(long value) { // Always written as a 64-bit value since the size for this type can // differ between architectures. +#ifdef MOZ_FAULTY + Singleton::get()->FuzzLong(&value); +#endif return WriteInt64(int64_t(value)); } bool WriteULong(unsigned long value) { // Always written as a 64-bit value since the size for this type can // differ between architectures. +#ifdef MOZ_FAULTY + Singleton::get()->FuzzULong(&value); +#endif return WriteUInt64(uint64_t(value)); } bool WriteSize(size_t value) { // Always written as a 64-bit value since the size for this type can // differ between architectures. +#ifdef MOZ_FAULTY + Singleton::get()->FuzzSize(&value); +#endif return WriteUInt64(uint64_t(value)); } bool WriteInt32(int32_t value) { +#ifdef MOZ_FAULTY + Singleton::get()->FuzzInt(&value); +#endif return WriteBytes(&value, sizeof(value)); } bool WriteUInt32(uint32_t value) { +#ifdef MOZ_FAULTY + Singleton::get()->FuzzUInt32(&value); +#endif return WriteBytes(&value, sizeof(value)); } bool WriteInt64(int64_t value) { +#ifdef MOZ_FAULTY + Singleton::get()->FuzzInt64(&value); +#endif return WriteBytes(&value, sizeof(value)); } bool WriteUInt64(uint64_t value) { +#ifdef MOZ_FAULTY + Singleton::get()->FuzzUInt64(&value); +#endif return WriteBytes(&value, sizeof(value)); } bool WriteDouble(double value) { +#ifdef MOZ_FAULTY + Singleton::get()->FuzzDouble(&value); +#endif return WriteBytes(&value, sizeof(value)); } bool WriteIntPtr(intptr_t value) { @@ -165,6 +206,9 @@ class Pickle { return WriteInt64(int64_t(value)); } bool WriteUnsignedChar(unsigned char value) { +#ifdef MOZ_FAULTY + Singleton::get()->FuzzUChar(&value); +#endif return WriteBytes(&value, sizeof(value)); } bool WriteString(const std::string& value); diff --git a/ipc/chromium/src/base/process_util_bsd.cc b/ipc/chromium/src/base/process_util_bsd.cc index 36c366b8ee..57cf7fef97 100644 --- a/ipc/chromium/src/base/process_util_bsd.cc +++ b/ipc/chromium/src/base/process_util_bsd.cc @@ -17,6 +17,12 @@ #include "nspr.h" #include "base/eintr_wrapper.h" +namespace { + +static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG"); + +} // namespace + namespace base { void FreeEnvVarsArray(char* array[], int length) @@ -135,6 +141,8 @@ bool LaunchApp(const std::vector& argv, if (!spawn_succeeded || !process_handle_valid) { retval = false; } else { + gProcessLog.print("==> process %d launched child process %d\n", + GetCurrentProcId(), pid); if (wait) HANDLE_EINTR(waitpid(pid, 0, 0)); diff --git a/ipc/chromium/src/base/process_util_mac.mm b/ipc/chromium/src/base/process_util_mac.mm index 77453d5ed4..7d06411b25 100644 --- a/ipc/chromium/src/base/process_util_mac.mm +++ b/ipc/chromium/src/base/process_util_mac.mm @@ -18,6 +18,12 @@ #include "base/string_util.h" #include "base/time.h" +namespace { + +static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG"); + +} // namespace + namespace base { void FreeEnvVarsArray(char* array[], int length) @@ -170,6 +176,8 @@ bool LaunchApp(const std::vector& argv, if (!spawn_succeeded || !process_handle_valid) { retval = false; } else { + gProcessLog.print("==> process %d launched child process %d\n", + GetCurrentProcId(), pid); if (wait) HANDLE_EINTR(waitpid(pid, 0, 0)); diff --git a/ipc/chromium/src/chrome/common/ipc_channel.h b/ipc/chromium/src/chrome/common/ipc_channel.h index ece2559db4..d3898a6ff4 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 = 128 * 1024 * 1024, + kMaximumMessageSize = 256 * 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 66d4469cfc..707aea080b 100644 --- a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc +++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc @@ -35,6 +35,10 @@ #include "mozilla/ipc/ProtocolUtils.h" #include "mozilla/UniquePtr.h" +#ifdef MOZ_FAULTY +#include "mozilla/ipc/Faulty.h" +#endif + // Work around possible OS limitations. static const size_t kMaxIOVecSize = 256; @@ -561,6 +565,9 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() { // Write out all the messages we can till the write blocks or there are no // more outgoing messages. while (!output_queue_.empty()) { +#ifdef MOZ_FAULTY + Singleton::get()->MaybeCollectAndClosePipe(pipe_); +#endif Message* msg = output_queue_.front(); struct msghdr msgh = {0}; diff --git a/ipc/glue/Faulty.cpp b/ipc/glue/Faulty.cpp new file mode 100644 index 0000000000..5c326cd3f4 --- /dev/null +++ b/ipc/glue/Faulty.cpp @@ -0,0 +1,654 @@ +/* -*- 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 "mozilla/ipc/Faulty.h" +#include +#include +#include "nsXULAppAPI.h" +#include "base/string_util.h" +#include "chrome/common/ipc_message.h" +#include "chrome/common/ipc_channel.h" +#include "prenv.h" +#include "mozilla/TypeTraits.h" +#include +#include + +namespace mozilla { +namespace ipc { + +const unsigned int Faulty::sDefaultProbability = Faulty::DefaultProbability(); +const bool Faulty::sIsLoggingEnabled = Faulty::Logging(); + +/** + * RandomNumericValue generates negative and positive integrals. + */ +template +T RandomIntegral() +{ + static_assert(mozilla::IsIntegral::value == true, + "T must be an integral type"); + double r = static_cast(random() % ((sizeof(T) * CHAR_BIT) + 1)); + T x = static_cast(pow(2.0, r)) - 1; + if (std::numeric_limits::is_signed && random() % 2 == 0) { + return (x * -1) - 1; + } + return x; +} + +/** + * RandomNumericLimit returns either the min or max limit of an arithmetic + * data type. + */ +template +T RandomNumericLimit() { + static_assert(mozilla::IsArithmetic::value == true, + "T must be an arithmetic type"); + return random() % 2 == 0 ? std::numeric_limits::min() + : std::numeric_limits::max(); +} + +/** + * RandomIntegerRange returns a random integral within a user defined range. + */ +template +T RandomIntegerRange(T min, T max) +{ + static_assert(mozilla::IsIntegral::value == true, + "T must be an integral type"); + MOZ_ASSERT(min < max); + return static_cast(random() % (max - min) + min); +} + +/** + * RandomFloatingPointRange returns a random floating-point number within a + * user defined range. + */ +template +T RandomFloatingPointRange(T min, T max) +{ + static_assert(mozilla::IsFloatingPoint::value == true, + "T must be a floating point type"); + MOZ_ASSERT(min < max); + T x = static_cast(random()) / static_cast(RAND_MAX); + return min + x * (max - min); +} + +/** + * RandomFloatingPoint returns a random floating-point number. + */ +template +T RandomFloatingPoint() +{ + static_assert(mozilla::IsFloatingPoint::value == true, + "T must be a floating point type"); + int radix = RandomIntegerRange(std::numeric_limits::min_exponent, + std::numeric_limits::max_exponent); + T x = static_cast(pow(2.0, static_cast(radix))); + return x * RandomFloatingPointRange(1.0, 2.0); +} + +/** + * FuzzIntegralType mutates an incercepted integral type of a pickled message. + */ +template +void FuzzIntegralType(T* v, bool largeValues) +{ + static_assert(mozilla::IsIntegral::value == true, + "T must be an integral type"); + switch (random() % 6) { + case 0: + if (largeValues) { + (*v) = RandomIntegral(); + break; + } + // Fall through + case 1: + if (largeValues) { + (*v) = RandomNumericLimit(); + break; + } + // Fall through + case 2: + if (largeValues) { + (*v) = RandomIntegerRange(std::numeric_limits::min(), + std::numeric_limits::max()); + break; + } + // Fall through + default: + switch(random() % 2) { + case 0: + // Prevent underflow + if (*v != std::numeric_limits::min()) { + (*v)--; + break; + } + // Fall through + case 1: + // Prevent overflow + if (*v != std::numeric_limits::max()) { + (*v)++; + break; + } + } + } +} + +/** + * FuzzFloatingPointType mutates an incercepted floating-point type of a + * pickled message. + */ +template +void FuzzFloatingPointType(T* v, bool largeValues) +{ + static_assert(mozilla::IsFloatingPoint::value == true, + "T must be a floating point type"); + switch (random() % 6) { + case 0: + if (largeValues) { + (*v) = RandomNumericLimit(); + break; + } + // Fall through + case 1: + if (largeValues) { + (*v) = RandomFloatingPointRange(std::numeric_limits::min(), + std::numeric_limits::max()); + break; + } + // Fall through + default: + (*v) = RandomFloatingPoint(); + } +} + +/** + * FuzzStringType mutates an incercepted string type of a pickled message. + */ +template +void FuzzStringType(T& v, const T& literal1, const T& literal2) +{ + switch (random() % 5) { + case 4: + v = v + v; + // Fall through + case 3: + v = v + v; + // Fall through + case 2: + v = v + v; + break; + case 1: + v += literal1; + break; + case 0: + v = literal2; + break; + } +} + + +Faulty::Faulty() + // Enables the strategy for fuzzing pipes. + : mFuzzPipes(!!PR_GetEnv("FAULTY_PIPE")) + // Enables the strategy for fuzzing pickled messages. + , mFuzzPickle(!!PR_GetEnv("FAULTY_PICKLE")) + // Uses very large values while fuzzing pickled messages. + // This may cause a high amount of malloc_abort() / NS_ABORT_OOM crashes. + , mUseLargeValues(!!PR_GetEnv("FAULTY_LARGE_VALUES")) + // Sets up our target process. + , mIsValidProcessType(IsValidProcessType()) +{ + FAULTY_LOG("Initializing."); + + const char* userSeed = PR_GetEnv("FAULTY_SEED"); + unsigned long randomSeed = static_cast(PR_IntervalNow()); + if (userSeed) { + long n = std::strtol(userSeed, nullptr, 10); + if (n != 0) { + randomSeed = static_cast(n); + } + } + srandom(randomSeed); + + FAULTY_LOG("Fuzz probability = %u", sDefaultProbability); + FAULTY_LOG("Random seed = %lu", randomSeed); + FAULTY_LOG("Strategy: pickle = %s", mFuzzPickle ? "enabled" : "disabled"); + FAULTY_LOG("Strategy: pipe = %s", mFuzzPipes ? "enabled" : "disabled"); +} + +// static +bool +Faulty::IsValidProcessType(void) +{ + bool isValidProcessType; + const bool targetChildren = !!PR_GetEnv("FAULTY_CHILDREN"); + const bool targetParent = !!PR_GetEnv("FAULTY_PARENT"); + + if (targetChildren && !targetParent) { + // Fuzz every process type but not the content process. + isValidProcessType = XRE_GetProcessType() != GeckoProcessType_Content; + } else if (targetChildren && targetParent) { + // Fuzz every process type. + isValidProcessType = true; + } else { + // Fuzz the content process only. + isValidProcessType = XRE_GetProcessType() == GeckoProcessType_Content; + } + + // Parent and children are different threads in the same process on + // desktop builds. + if (!isValidProcessType) { + FAULTY_LOG("Invalid process type for pid=%d", getpid()); + } + + return isValidProcessType; +} + +// static +unsigned int +Faulty::DefaultProbability(void) +{ + // Defines the likelihood of fuzzing a message. + const char* probability = PR_GetEnv("FAULTY_PROBABILITY"); + if (probability) { + long n = std::strtol(probability, nullptr, 10); + if (n != 0) { + return n; + } + } + return FAULTY_DEFAULT_PROBABILITY; +} + +// static +bool +Faulty::Logging(void) +{ + // Enables logging of sendmsg() calls even in optimized builds. + return !!PR_GetEnv("FAULTY_ENABLE_LOGGING"); +} + +unsigned int +Faulty::Random(unsigned int aMax) +{ + MOZ_ASSERT(aMax > 0); + return static_cast(random() % aMax); +} + +bool +Faulty::GetChance(unsigned int aProbability) +{ + return Random(aProbability) == 0; +} + +// +// Strategy: Pipes +// + +void +Faulty::MaybeCollectAndClosePipe(int aPipe, unsigned int aProbability) +{ + if (!mFuzzPipes) { + return; + } + + if (aPipe > -1) { + FAULTY_LOG("collecting pipe %d to bucket of pipes (count: %ld)", + aPipe, mFds.size()); + mFds.insert(aPipe); + } + + if (mFds.size() > 0 && GetChance(aProbability)) { + std::set::iterator it(mFds.begin()); + std::advance(it, Random(mFds.size())); + FAULTY_LOG("trying to close collected pipe: %d", *it); + errno = 0; + while ((close(*it) == -1 && (errno == EINTR))) { + ; + } + FAULTY_LOG("pipe status after attempt to close: %d", errno); + mFds.erase(it); + } +} + +// +// Strategy: Pickle +// + +void +Faulty::MutateBool(bool* aValue) +{ + *aValue = !(*aValue); +} + +void +Faulty::FuzzBool(bool* aValue, unsigned int aProbability) +{ + if (mIsValidProcessType) { + if (mFuzzPickle && GetChance(aProbability)) { + bool oldValue = *aValue; + MutateBool(aValue); + FAULTY_LOG("pickle field {bool} of value: %d changed to: %d", + (int)oldValue, (int)*aValue); + } + } +} + +void +Faulty::MutateChar(char* aValue) +{ + FuzzIntegralType(aValue, true); +} + +void +Faulty::FuzzChar(char* aValue, unsigned int aProbability) +{ + if (mIsValidProcessType) { + if (mFuzzPickle && GetChance(aProbability)) { + char oldValue = *aValue; + MutateChar(aValue); + FAULTY_LOG("pickle field {char} of value: %c changed to: %c", + oldValue, *aValue); + } + } +} + +void +Faulty::MutateUChar(unsigned char* aValue) +{ + FuzzIntegralType(aValue, true); +} + +void +Faulty::FuzzUChar(unsigned char* aValue, unsigned int aProbability) +{ + if (mIsValidProcessType) { + if (mFuzzPickle && GetChance(aProbability)) { + unsigned char oldValue = *aValue; + MutateUChar(aValue); + FAULTY_LOG("pickle field {unsigned char} of value: %u changed to: %u", + oldValue, *aValue); + } + } +} + +void +Faulty::MutateInt16(int16_t* aValue) +{ + FuzzIntegralType(aValue, true); +} + +void +Faulty::FuzzInt16(int16_t* aValue, unsigned int aProbability) +{ + if (mIsValidProcessType) { + if (mFuzzPickle && GetChance(aProbability)) { + int16_t oldValue = *aValue; + MutateInt16(aValue); + FAULTY_LOG("pickle field {Int16} of value: %d changed to: %d", + oldValue, *aValue); + } + } +} + +void +Faulty::MutateUInt16(uint16_t* aValue) +{ + FuzzIntegralType(aValue, true); +} + +void +Faulty::FuzzUInt16(uint16_t* aValue, unsigned int aProbability) +{ + if (mIsValidProcessType) { + if (mFuzzPickle && GetChance(aProbability)) { + uint16_t oldValue = *aValue; + MutateUInt16(aValue); + FAULTY_LOG("pickle field {UInt16} of value: %d changed to: %d", + oldValue, *aValue); + } + } +} + +void +Faulty::MutateInt(int* aValue) +{ + FuzzIntegralType(aValue, mUseLargeValues); +} + +void +Faulty::FuzzInt(int* aValue, unsigned int aProbability) +{ + if (mIsValidProcessType) { + if (mFuzzPickle && GetChance(aProbability)) { + int oldValue = *aValue; + MutateInt(aValue); + FAULTY_LOG("pickle field {int} of value: %d changed to: %d", + oldValue, *aValue); + } + } +} + +void +Faulty::MutateUInt32(uint32_t* aValue) +{ + FuzzIntegralType(aValue, mUseLargeValues); +} + +void +Faulty::FuzzUInt32(uint32_t* aValue, unsigned int aProbability) +{ + if (mIsValidProcessType) { + if (mFuzzPickle && GetChance(aProbability)) { + uint32_t oldValue = *aValue; + MutateUInt32(aValue); + FAULTY_LOG("pickle field {UInt32} of value: %u changed to: %u", + oldValue, *aValue); + } + } +} + +void +Faulty::MutateLong(long* aValue) +{ + FuzzIntegralType(aValue, mUseLargeValues); +} + +void +Faulty::FuzzLong(long* aValue, unsigned int aProbability) +{ + if (mIsValidProcessType) { + if (mFuzzPickle && GetChance(aProbability)) { + long oldValue = *aValue; + MutateLong(aValue); + FAULTY_LOG("pickle field {long} of value: %ld changed to: %ld", + oldValue, *aValue); + } + } +} + +void +Faulty::MutateULong(unsigned long* aValue) +{ + FuzzIntegralType(aValue, mUseLargeValues); +} + +void +Faulty::FuzzULong(unsigned long* aValue, unsigned int aProbability) +{ + if (mIsValidProcessType) { + if (mFuzzPickle && GetChance(aProbability)) { + unsigned long oldValue = *aValue; + MutateULong(aValue); + FAULTY_LOG("pickle field {unsigned long} of value: %lu changed to: %lu", + oldValue, *aValue); + } + } +} + +void +Faulty::MutateSize(size_t* aValue) +{ + FuzzIntegralType(aValue, mUseLargeValues); +} + +void +Faulty::FuzzSize(size_t* aValue, unsigned int aProbability) +{ + if (mIsValidProcessType) { + if (mFuzzPickle && GetChance(aProbability)) { + size_t oldValue = *aValue; + MutateSize(aValue); + FAULTY_LOG("pickle field {size_t} of value: %zu changed to: %zu", + oldValue, *aValue); + } + } +} + +void +Faulty::MutateUInt64(uint64_t* aValue) +{ + FuzzIntegralType(aValue, mUseLargeValues); +} + +void +Faulty::FuzzUInt64(uint64_t* aValue, unsigned int aProbability) +{ + if (mIsValidProcessType) { + if (mFuzzPickle && GetChance(aProbability)) { + uint64_t oldValue = *aValue; + MutateUInt64(aValue); + FAULTY_LOG("pickle field {UInt64} of value: %llu changed to: %llu", + oldValue, *aValue); + } + } +} + +void +Faulty::MutateInt64(int64_t* aValue) +{ + FuzzIntegralType(aValue, mUseLargeValues); +} + +void +Faulty::FuzzInt64(int64_t* aValue, unsigned int aProbability) +{ + if (mIsValidProcessType) { + if (mFuzzPickle && GetChance(aProbability)) { + int64_t oldValue = *aValue; + MutateInt64(aValue); + FAULTY_LOG("pickle field {Int64} of value: %lld changed to: %lld", + oldValue, *aValue); + } + } +} + +void +Faulty::MutateDouble(double* aValue) +{ + FuzzFloatingPointType(aValue, mUseLargeValues); +} + +void +Faulty::FuzzDouble(double* aValue, unsigned int aProbability) +{ + if (mIsValidProcessType) { + if (mFuzzPickle && GetChance(aProbability)) { + double oldValue = *aValue; + MutateDouble(aValue); + FAULTY_LOG("pickle field {double} of value: %f changed to: %f", + oldValue, *aValue); + } + } +} + +void +Faulty::MutateFloat(float* aValue) +{ + FuzzFloatingPointType(aValue, mUseLargeValues); +} + +void +Faulty::FuzzFloat(float* aValue, unsigned int aProbability) +{ + if (mIsValidProcessType) { + if (mFuzzPickle && GetChance(aProbability)) { + float oldValue = *aValue; + MutateFloat(aValue); + FAULTY_LOG("pickle field {float} of value: %f changed to: %f", + oldValue, *aValue); + } + } +} + +void +Faulty::FuzzString(std::string& aValue, unsigned int aProbability) +{ + if (mIsValidProcessType) { + if (mFuzzPickle && GetChance(aProbability)) { + std::string oldValue = aValue; + FuzzStringType(aValue, "xoferiF", std::string()); + FAULTY_LOG("pickle field {string} of value: %s changed to: %s", + oldValue.c_str(), aValue.c_str()); + } + } +} + +void +Faulty::FuzzWString(std::wstring& aValue, unsigned int aProbability) +{ + if (mIsValidProcessType) { + if (mFuzzPickle && GetChance(aProbability)) { + std::wstring oldValue = aValue; + FAULTY_LOG("pickle field {wstring}"); + FuzzStringType(aValue, L"xoferiF", std::wstring()); + } + } +} + +void +Faulty::FuzzString16(string16& aValue, unsigned int aProbability) +{ + if (mIsValidProcessType) { + if (mFuzzPickle && GetChance(aProbability)) { + string16 oldValue = aValue; + FAULTY_LOG("pickle field {string16}"); + FuzzStringType(aValue, + string16(ASCIIToUTF16(std::string("xoferiF"))), + string16(ASCIIToUTF16(std::string()))); + } + } +} + +void +Faulty::FuzzBytes(void* aData, int aLength, unsigned int aProbability) +{ + if (mIsValidProcessType) { + if (mFuzzPickle && GetChance(aProbability)) { + FAULTY_LOG("pickle field {bytes}"); + // Too destructive. |WriteBytes| is used in many of the above data + // types as base function. + //FuzzData(static_cast(aData), aLength); + } + } +} + +void +Faulty::FuzzData(std::string& aValue, int aLength, unsigned int aProbability) +{ + if (mIsValidProcessType) { + if (mFuzzPickle && GetChance(aProbability)) { + FAULTY_LOG("pickle field {data}"); + for (int i = 0; i < aLength; ++i) { + if (GetChance(aProbability)) { + FuzzIntegralType(&aValue[i], true); + } + } + } + } +} + +} // namespace ipc +} // namespace mozilla diff --git a/ipc/glue/Faulty.h b/ipc/glue/Faulty.h new file mode 100644 index 0000000000..8afe86b063 --- /dev/null +++ b/ipc/glue/Faulty.h @@ -0,0 +1,100 @@ +/* -*- 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_ipc_Faulty_h +#define mozilla_ipc_Faulty_h + +#include +#include +#include "nsDebug.h" +#include "base/string16.h" +#include "base/singleton.h" + +#define FAULTY_DEFAULT_PROBABILITY 1000 +#define FAULTY_LOG(fmt, args...) \ + if (mozilla::ipc::Faulty::IsLoggingEnabled()) { \ + printf_stderr("[Faulty] " fmt "\n", ## args); \ + } + +namespace IPC { + // Needed for blacklisting messages. + class Message; +} + +namespace mozilla { +namespace ipc { + +class Faulty +{ + public: + // Used as a default argument for the Fuzz|datatype| methods. + static const unsigned int sDefaultProbability; + + static unsigned int DefaultProbability(void); + static bool Logging(void); + static bool IsLoggingEnabled(void) { return sIsLoggingEnabled; } + + void FuzzBool(bool* aValue, unsigned int aProbability=sDefaultProbability); + void FuzzChar(char* aValue, unsigned int aProbability=sDefaultProbability); + void FuzzUChar(unsigned char* aValue, unsigned int aProbability=sDefaultProbability); + void FuzzInt16(int16_t* aValue, unsigned int aProbability=sDefaultProbability); + void FuzzUInt16(uint16_t* aValue, unsigned int aProbability=sDefaultProbability); + void FuzzInt(int* aValue, unsigned int aProbability=sDefaultProbability); + void FuzzUInt32(uint32_t* aValue, unsigned int aProbability=sDefaultProbability); + void FuzzLong(long* aValue, unsigned int aProbability=sDefaultProbability); + void FuzzULong(unsigned long* aValue, unsigned int aProbability=sDefaultProbability); + void FuzzInt64(int64_t* aValue, unsigned int aProbability=sDefaultProbability); + void FuzzUInt64(uint64_t* aValue, unsigned int aProbability=sDefaultProbability); + void FuzzSize(size_t* aValue, unsigned int aProbability=sDefaultProbability); + void FuzzFloat(float* aValue, unsigned int aProbability=sDefaultProbability); + void FuzzDouble(double* aValue, unsigned int aProbability=sDefaultProbability); + void FuzzString(std::string& aValue, unsigned int aProbability=sDefaultProbability); + void FuzzWString(std::wstring& aValue, unsigned int aProbability=sDefaultProbability); + void FuzzString16(string16& aValue, unsigned int aProbability=sDefaultProbability); + void FuzzData(std::string& aData, int aLength, unsigned int aProbability=sDefaultProbability); + void FuzzBytes(void* aData, int aLength, unsigned int aProbability=sDefaultProbability); + + void MaybeCollectAndClosePipe(int aPipe, unsigned int aProbability=sDefaultProbability); + + private: + std::set mFds; + + const bool mFuzzPipes; + const bool mFuzzPickle; + const bool mUseLargeValues; + const bool mIsValidProcessType; + + static const bool sIsLoggingEnabled; + + Faulty(); + friend struct DefaultSingletonTraits; + DISALLOW_EVIL_CONSTRUCTORS(Faulty); + + static bool IsValidProcessType(void); + + unsigned int Random(unsigned int aMax); + bool GetChance(unsigned int aProbability); + + void MutateBool(bool* aValue); + void MutateChar(char* aValue); + void MutateUChar(unsigned char* aValue); + void MutateInt16(int16_t* aValue); + void MutateUInt16(uint16_t* aValue); + void MutateInt(int* aValue); + void MutateUInt32(uint32_t* aValue); + void MutateLong(long* aValue); + void MutateULong(unsigned long* aValue); + void MutateInt64(int64_t* aValue); + void MutateUInt64(uint64_t* aValue); + void MutateSize(size_t* aValue); + void MutateFloat(float* aValue); + void MutateDouble(double* aValue); +}; + +} // namespace ipc +} // namespace mozilla + +#endif diff --git a/ipc/glue/MessageChannel.h b/ipc/glue/MessageChannel.h index 2ef3232260..6ef230abbe 100644 --- a/ipc/glue/MessageChannel.h +++ b/ipc/glue/MessageChannel.h @@ -485,14 +485,20 @@ class MessageChannel : HasResultCodes // Wrap an existing task which can be cancelled at any time // without the wrapper's knowledge. - class DequeueTask : public Runnable + class DequeueTask : public CancelableRunnable { public: explicit DequeueTask(RefCountedTask* aTask) : mTask(aTask) { } NS_IMETHOD Run() override { - mTask->Run(); + if (mTask) { + mTask->Run(); + } + return NS_OK; + } + nsresult Cancel() override { + mTask = nullptr; return NS_OK; } diff --git a/ipc/glue/MessagePump.cpp b/ipc/glue/MessagePump.cpp index 4535ee2f95..39362da3d3 100644 --- a/ipc/glue/MessagePump.cpp +++ b/ipc/glue/MessagePump.cpp @@ -200,6 +200,18 @@ MessagePump::ScheduleDelayedWork(const base::TimeTicks& aDelayedTime) nsITimer::TYPE_ONE_SHOT); } +nsIEventTarget* +MessagePump::GetXPCOMThread() +{ + if (mThread) { + return mThread; + } + + // Main thread + nsCOMPtr mainThread = do_GetMainThread(); + return mainThread; +} + void MessagePump::DoDelayedWork(base::MessagePump::Delegate* aDelegate) { diff --git a/ipc/glue/MessagePump.h b/ipc/glue/MessagePump.h index 16e3a554a0..b2f3921648 100644 --- a/ipc/glue/MessagePump.h +++ b/ipc/glue/MessagePump.h @@ -48,6 +48,9 @@ public: virtual void ScheduleDelayedWork(const base::TimeTicks& aDelayedWorkTime) override; + virtual nsIEventTarget* + GetXPCOMThread() override; + protected: virtual ~MessagePump(); @@ -128,6 +131,12 @@ public: // The main run loop for this thread. virtual void DoRunLoop() override; + virtual nsIEventTarget* + GetXPCOMThread() override + { + return nullptr; // not sure what to do with this one + } + protected: void SetInWait() { MutexAutoLock lock(mWaitLock); diff --git a/ipc/glue/moz.build b/ipc/glue/moz.build index 2db7affe11..bfce72c257 100644 --- a/ipc/glue/moz.build +++ b/ipc/glue/moz.build @@ -37,6 +37,10 @@ EXPORTS.mozilla.ipc += [ 'WindowsMessageLoop.h', ] +if CONFIG['MOZ_FAULTY'] == '1': + EXPORTS.mozilla.ipc += ['Faulty.h'] + SOURCES += ['Faulty.cpp'] + if CONFIG['OS_ARCH'] == 'WINNT': DEFINES['WEBRTC_WIN'] = True EXPORTS.mozilla.ipc += [ diff --git a/js/public/HashTable.h b/js/public/HashTable.h index 56653e885e..deff486510 100644 --- a/js/public/HashTable.h +++ b/js/public/HashTable.h @@ -704,6 +704,11 @@ class HashMapEntry value_(mozilla::Move(rhs.value_)) {} + void operator=(HashMapEntry&& rhs) { + key_ = mozilla::Move(rhs.key_); + value_ = mozilla::Move(rhs.value_); + } + typedef Key KeyType; typedef Value ValueType; @@ -774,8 +779,16 @@ class HashTableEntry } void swap(HashTableEntry* other) { + if (this == other) + return; + MOZ_ASSERT(isLive()); + if (other->isLive()) { + mozilla::Swap(*mem.addr(), *other->mem.addr()); + } else { + *other->mem.addr() = mozilla::Move(*mem.addr()); + destroy(); + } mozilla::Swap(keyHash, other->keyHash); - mozilla::Swap(mem, other->mem); } T& get() { MOZ_ASSERT(isLive()); return *mem.addr(); } diff --git a/js/src/jit/JitCompartment.h b/js/src/jit/JitCompartment.h index 20bc8ca6fd..2f6211c07b 100644 --- a/js/src/jit/JitCompartment.h +++ b/js/src/jit/JitCompartment.h @@ -403,6 +403,10 @@ struct CacheIRStubKey : public DefaultHasher { explicit CacheIRStubKey(CacheIRStubInfo* info) : stubInfo(info) {} CacheIRStubKey(CacheIRStubKey&& other) : stubInfo(Move(other.stubInfo)) { } + + void operator=(CacheIRStubKey&& other) { + stubInfo = Move(other.stubInfo); + } }; class JitCompartment diff --git a/js/src/jit/RegisterSets.h b/js/src/jit/RegisterSets.h index c66baf4c46..20174e3497 100644 --- a/js/src/jit/RegisterSets.h +++ b/js/src/jit/RegisterSets.h @@ -7,7 +7,6 @@ #ifndef jit_RegisterSets_h #define jit_RegisterSets_h -#include "mozilla/Alignment.h" #include "mozilla/MathAlgorithms.h" #include "jit/JitAllocPolicy.h" @@ -26,8 +25,8 @@ struct AnyRegister { Code code_; public: - AnyRegister() - { } + AnyRegister() = default; + explicit AnyRegister(Register gpr) { code_ = gpr.code(); } @@ -169,46 +168,25 @@ class TypedOrValueRegister // Type of value being stored. MIRType type_; - // Space to hold either an AnyRegister or a ValueOperand. union U { - mozilla::AlignedStorage2 typed; - mozilla::AlignedStorage2 value; + AnyRegister typed; + ValueOperand value; } data; - AnyRegister& dataTyped() { - MOZ_ASSERT(hasTyped()); - return *data.typed.addr(); - } - ValueOperand& dataValue() { - MOZ_ASSERT(hasValue()); - return *data.value.addr(); - } - - AnyRegister dataTyped() const { - MOZ_ASSERT(hasTyped()); - return *data.typed.addr(); - } - const ValueOperand& dataValue() const { - MOZ_ASSERT(hasValue()); - return *data.value.addr(); - } - public: - TypedOrValueRegister() - : type_(MIRType::None) - {} + TypedOrValueRegister() = default; TypedOrValueRegister(MIRType type, AnyRegister reg) : type_(type) { - dataTyped() = reg; + data.typed = reg; } MOZ_IMPLICIT TypedOrValueRegister(ValueOperand value) : type_(MIRType::Value) { - dataValue() = value; + data.value = value; } MIRType type() const { @@ -224,11 +202,13 @@ class TypedOrValueRegister } AnyRegister typedReg() const { - return dataTyped(); + MOZ_ASSERT(hasTyped()); + return data.typed; } ValueOperand valueReg() const { - return dataValue(); + MOZ_ASSERT(hasValue()); + return data.value; } AnyRegister scratchReg() { @@ -246,17 +226,17 @@ class ConstantOrRegister // Space to hold either a Value or a TypedOrValueRegister. union U { - mozilla::AlignedStorage2 constant; - mozilla::AlignedStorage2 reg; + Value constant; + TypedOrValueRegister reg; } data; Value& dataValue() { MOZ_ASSERT(constant()); - return *data.constant.addr(); + return data.constant; } TypedOrValueRegister& dataReg() { MOZ_ASSERT(!constant()); - return *data.reg.addr(); + return data.reg; } public: diff --git a/js/src/jit/Snapshots.h b/js/src/jit/Snapshots.h index 834e1fd5b7..bbc463d1f6 100644 --- a/js/src/jit/Snapshots.h +++ b/js/src/jit/Snapshots.h @@ -504,7 +504,25 @@ class SnapshotReader } }; -typedef mozilla::AlignedStorage<4 * sizeof(uint32_t)> RInstructionStorage; +class RInstructionStorage +{ + static const size_t Size = 4 * sizeof(uint32_t); + mozilla::AlignedStorage mem; + + public: + const void* addr() const { return mem.addr(); } + void* addr() { return mem.addr(); } + + RInstructionStorage() = default; + + RInstructionStorage(const RInstructionStorage& other) { + memcpy(addr(), other.addr(), Size); + } + void operator=(const RInstructionStorage& other) { + memcpy(addr(), other.addr(), Size); + } +}; + class RInstruction; class RecoverReader diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp index 2c2788f2d9..677b214dc6 100644 --- a/js/src/vm/ObjectGroup.cpp +++ b/js/src/vm/ObjectGroup.cpp @@ -1342,6 +1342,13 @@ struct ObjectGroupCompartment::AllocationSiteKey : public DefaultHasheroffsetToPC(key.offset)) ^ key.kind ^ diff --git a/mfbt/Alignment.h b/mfbt/Alignment.h index 4098b1d4e5..48360925df 100644 --- a/mfbt/Alignment.h +++ b/mfbt/Alignment.h @@ -120,6 +120,15 @@ struct AlignedStorage const void* addr() const { return u.mBytes; } void* addr() { return u.mBytes; } + +#if !defined(_MSC_VER) || (defined(_MSC_VER) && _MSC_VER >= 1900) + AlignedStorage() = default; + + // AlignedStorage is non-copyable: the default copy constructor violates + // strict aliasing rules, per bug 1269319. + AlignedStorage(const AlignedStorage&) = delete; + void operator=(const AlignedStorage&) = delete; +#endif }; template @@ -133,6 +142,15 @@ struct MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS AlignedStorage2 const T* addr() const { return reinterpret_cast(u.mBytes); } T* addr() { return static_cast(static_cast(u.mBytes)); } + +#if !defined(_MSC_VER) || (defined(_MSC_VER) && _MSC_VER >= 1900) + AlignedStorage2() = default; + + // AlignedStorage2 is non-copyable: the default copy constructor violates + // strict aliasing rules, per bug 1269319. + AlignedStorage2(const AlignedStorage2&) = delete; + void operator=(const AlignedStorage2&) = delete; +#endif }; } /* namespace mozilla */ diff --git a/mfbt/AllocPolicy.h b/mfbt/AllocPolicy.h index 139ecbde5b..81f62b0382 100644 --- a/mfbt/AllocPolicy.h +++ b/mfbt/AllocPolicy.h @@ -122,7 +122,7 @@ public: { } - bool checkSimulatedOOM() const + MOZ_MUST_USE bool checkSimulatedOOM() const { return true; } diff --git a/mfbt/Compression.h b/mfbt/Compression.h index ce5e78250a..eda867130a 100644 --- a/mfbt/Compression.h +++ b/mfbt/Compression.h @@ -61,8 +61,7 @@ public: /** * If the source stream is malformed, the function will stop decoding - * and return a negative result, indicating the byte position of the - * faulty instruction + * and return false. * * This function never writes outside of provided buffers, and never * modifies input buffer. @@ -71,9 +70,9 @@ public: * minimum of |aOutputSize| bytes. * * @param aOutputSize is the output size, therefore the original size - * @return the number of bytes read in the source buffer + * @return true on success, false on failure */ - static MFBT_API bool + static MFBT_API MOZ_MUST_USE bool decompress(const char* aSource, char* aDest, size_t aOutputSize); /** @@ -91,8 +90,9 @@ public: * already allocated) * @param aOutputSize the actual number of bytes decoded in the destination * buffer (necessarily <= aMaxOutputSize) + * @return true on success, false on failure */ - static MFBT_API bool + static MFBT_API MOZ_MUST_USE bool decompress(const char* aSource, size_t aInputSize, char* aDest, size_t aMaxOutputSize, size_t* aOutputSize); diff --git a/mfbt/SplayTree.h b/mfbt/SplayTree.h index 1a7e031a67..58760dfbef 100644 --- a/mfbt/SplayTree.h +++ b/mfbt/SplayTree.h @@ -76,19 +76,19 @@ public: return Comparator::compare(aValue, *last) == 0 ? last : nullptr; } - bool insert(T* aValue) + void insert(T* aValue) { MOZ_ASSERT(!find(*aValue), "Duplicate elements are not allowed."); if (!mRoot) { mRoot = aValue; - return true; + return; } T* last = lookup(*aValue); int cmp = Comparator::compare(*aValue, *last); finishInsertion(last, cmp, aValue); - return true; + return; } T* findOrInsert(const T& aValue); @@ -194,7 +194,7 @@ private: return parent; } - T* finishInsertion(T* aLast, int32_t aCmp, T* aNew) + void finishInsertion(T* aLast, int32_t aCmp, T* aNew) { MOZ_ASSERT(aCmp, "Nodes shouldn't be equal!"); @@ -204,7 +204,6 @@ private: aNew->mParent = aLast; splay(aNew); - return aNew; } /** @@ -321,7 +320,9 @@ SplayTree::findOrInsert(const T& aValue) return last; } - return finishInsertion(last, cmp, new T(aValue)); + T* t = new T(aValue); + finishInsertion(last, cmp, t); + return t; } } /* namespace mozilla */ diff --git a/mfbt/Vector.h b/mfbt/Vector.h index a137845933..eed9a804c0 100644 --- a/mfbt/Vector.h +++ b/mfbt/Vector.h @@ -128,7 +128,7 @@ struct VectorImpl * aNewCap has not overflowed, and (2) multiplying aNewCap by sizeof(T) will * not overflow. */ - static inline bool + static inline MOZ_MUST_USE bool growTo(Vector& aV, size_t aNewCap) { MOZ_ASSERT(!aV.usingInlineStorage()); @@ -215,7 +215,7 @@ struct VectorImpl } } - static inline bool + static inline MOZ_MUST_USE bool growTo(Vector& aV, size_t aNewCap) { MOZ_ASSERT(!aV.usingInlineStorage()); diff --git a/netwerk/base/nsSocketTransportService2.cpp b/netwerk/base/nsSocketTransportService2.cpp index 39cc577f67..7a7c01496a 100644 --- a/netwerk/base/nsSocketTransportService2.cpp +++ b/netwerk/base/nsSocketTransportService2.cpp @@ -173,6 +173,12 @@ nsSocketTransportService::Dispatch(already_AddRefed&& event, uint32 return rv; } +NS_IMETHODIMP +nsSocketTransportService::DelayedDispatch(already_AddRefed&&, uint32_t) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + NS_IMETHODIMP nsSocketTransportService::IsOnCurrentThread(bool *result) { diff --git a/netwerk/base/nsStreamTransportService.cpp b/netwerk/base/nsStreamTransportService.cpp index 2e15585627..6a7ca3fe6e 100644 --- a/netwerk/base/nsStreamTransportService.cpp +++ b/netwerk/base/nsStreamTransportService.cpp @@ -526,6 +526,12 @@ nsStreamTransportService::Dispatch(already_AddRefed&& task, uint32_ return pool->Dispatch(event.forget(), flags); } +NS_IMETHODIMP +nsStreamTransportService::DelayedDispatch(already_AddRefed&&, uint32_t) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + NS_IMETHODIMP nsStreamTransportService::IsOnCurrentThread(bool *result) { diff --git a/xpcom/threads/LazyIdleThread.cpp b/xpcom/threads/LazyIdleThread.cpp index 535a48a8e4..2c744977f2 100644 --- a/xpcom/threads/LazyIdleThread.cpp +++ b/xpcom/threads/LazyIdleThread.cpp @@ -430,6 +430,12 @@ LazyIdleThread::Dispatch(already_AddRefed&& aEvent, return mThread->Dispatch(event.forget(), aFlags); } +NS_IMETHODIMP +LazyIdleThread::DelayedDispatch(already_AddRefed&&, uint32_t) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + NS_IMETHODIMP LazyIdleThread::IsOnCurrentThread(bool* aIsOnCurrentThread) { diff --git a/xpcom/threads/SharedThreadPool.h b/xpcom/threads/SharedThreadPool.h index 304baa3c15..ba586c6d9c 100644 --- a/xpcom/threads/SharedThreadPool.h +++ b/xpcom/threads/SharedThreadPool.h @@ -67,6 +67,9 @@ public: NS_IMETHOD Dispatch(already_AddRefed&& event, uint32_t flags) override { return !mEventTarget ? NS_ERROR_NULL_POINTER : mEventTarget->Dispatch(Move(event), flags); } + NS_IMETHOD DelayedDispatch(already_AddRefed&&, uint32_t) override + { return NS_ERROR_NOT_IMPLEMENTED; } + using nsIEventTarget::Dispatch; NS_IMETHOD IsOnCurrentThread(bool *_retval) override { return !mEventTarget ? NS_ERROR_NULL_POINTER : mEventTarget->IsOnCurrentThread(_retval); } diff --git a/xpcom/threads/nsIEventTarget.idl b/xpcom/threads/nsIEventTarget.idl index 52a023cf0c..3d9d15817d 100644 --- a/xpcom/threads/nsIEventTarget.idl +++ b/xpcom/threads/nsIEventTarget.idl @@ -97,6 +97,26 @@ interface nsIEventTarget : nsISupports * events, so this event would never run and has not been dispatched. */ [binaryname(DispatchFromScript)] void dispatch(in nsIRunnable event, in unsigned long flags); + /** + * Dispatch an event to this event target, but do not run it before delay + * milliseconds have passed. This function may be called from any thread. + * + * @param event + * The alreadyAddrefed<> event to dispatch. + * @param delay + * The delay (in ms) before running the event. If event does not rise to + * the top of the event queue before the delay has passed, it will be set + * aside to execute once the delay has passed. Otherwise, it will be + * executed immediately. + * + * @throws NS_ERROR_INVALID_ARG + * Indicates that event is null. + * @throws NS_ERROR_UNEXPECTED + * Indicates that the thread is shutting down and has finished processing + * events, so this event would never run and has not been dispatched, or + * that delay is zero. + */ + [noscript] void delayedDispatch(in alreadyAddRefed_nsIRunnable event, in unsigned long delay); }; %{C++ diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp index 4aca421951..098ac06858 100644 --- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -230,6 +230,75 @@ private: ReentrantMonitor mMon; bool mInitialized; }; +//----------------------------------------------------------------------------- + +namespace { +class DelayedRunnable : public Runnable, + public nsITimerCallback +{ +public: + DelayedRunnable(already_AddRefed aRunnable, + uint32_t aDelay) + : mWrappedRunnable(aRunnable), + mDelayedFrom(TimeStamp::NowLoRes()), + mDelay(aDelay) + { } + + NS_DECL_ISUPPORTS_INHERITED + + nsresult Init() + { + nsresult rv; + mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + MOZ_ASSERT(mTimer); + return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT); + } + + nsresult DoRun() + { + nsCOMPtr r = mWrappedRunnable.forget(); + return r->Run(); + } + + NS_IMETHOD Run() override + { + // Already ran? + if (!mWrappedRunnable) { + return NS_OK; + } + + // Are we too early? + if ((TimeStamp::NowLoRes() - mDelayedFrom).ToMilliseconds() < mDelay) { + return NS_OK; // Let the nsITimer run us. + } + + mTimer->Cancel(); + return DoRun(); + } + + NS_IMETHOD Notify(nsITimer* aTimer) override + { + // If we already ran, the timer should have been canceled. + MOZ_ASSERT(mWrappedRunnable); + MOZ_ASSERT(aTimer == mTimer); + + return DoRun(); + } + +private: + ~DelayedRunnable() {} + + nsCOMPtr mWrappedRunnable; + nsCOMPtr mTimer; + TimeStamp mDelayedFrom; + uint32_t mDelay; +}; + +NS_IMPL_ISUPPORTS_INHERITED(DelayedRunnable, Runnable, nsITimerCallback) + +} // anonymous namespace //----------------------------------------------------------------------------- @@ -668,6 +737,18 @@ nsThread::Dispatch(already_AddRefed&& aEvent, uint32_t aFlags) return DispatchInternal(Move(aEvent), aFlags, nullptr); } +NS_IMETHODIMP +nsThread::DelayedDispatch(already_AddRefed&& aEvent, uint32_t aDelayMs) +{ + NS_ENSURE_TRUE(!!aDelayMs, NS_ERROR_UNEXPECTED); + + RefPtr r = new DelayedRunnable(Move(aEvent), aDelayMs); + nsresult rv = r->Init(); + NS_ENSURE_SUCCESS(rv, rv); + + return DispatchInternal(r.forget(), 0, nullptr); +} + NS_IMETHODIMP nsThread::IsOnCurrentThread(bool* aResult) { @@ -1217,6 +1298,12 @@ nsThread::nsNestedEventTarget::Dispatch(already_AddRefed&& aEvent, return mThread->DispatchInternal(Move(aEvent), aFlags, this); } +NS_IMETHODIMP +nsThread::nsNestedEventTarget::DelayedDispatch(already_AddRefed&&, uint32_t) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + NS_IMETHODIMP nsThread::nsNestedEventTarget::IsOnCurrentThread(bool* aResult) { diff --git a/xpcom/threads/nsThreadPool.cpp b/xpcom/threads/nsThreadPool.cpp index 0b4ac6047a..6760e7bdad 100644 --- a/xpcom/threads/nsThreadPool.cpp +++ b/xpcom/threads/nsThreadPool.cpp @@ -278,6 +278,12 @@ nsThreadPool::Dispatch(already_AddRefed&& aEvent, uint32_t aFlags) return NS_OK; } +NS_IMETHODIMP +nsThreadPool::DelayedDispatch(already_AddRefed&&, uint32_t) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + NS_IMETHODIMP nsThreadPool::IsOnCurrentThread(bool* aResult) {