summaryrefslogtreecommitdiffstats
path: root/tools/fuzzing/faulty
diff options
context:
space:
mode:
Diffstat (limited to 'tools/fuzzing/faulty')
-rw-r--r--tools/fuzzing/faulty/Faulty.cpp786
-rw-r--r--tools/fuzzing/faulty/Faulty.h134
-rw-r--r--tools/fuzzing/faulty/moz.build13
3 files changed, 933 insertions, 0 deletions
diff --git a/tools/fuzzing/faulty/Faulty.cpp b/tools/fuzzing/faulty/Faulty.cpp
new file mode 100644
index 0000000000..526ec50c86
--- /dev/null
+++ b/tools/fuzzing/faulty/Faulty.cpp
@@ -0,0 +1,786 @@
+/* -*- 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 <cerrno>
+#include <climits>
+#include <cmath>
+#include <fstream>
+#include <mutex>
+#include <prinrval.h>
+#include <type_traits>
+#ifdef _WINDOWS
+# include <process.h>
+# define getpid _getpid
+#else
+# include <unistd.h>
+#endif
+#include "base/string_util.h"
+#include "FuzzingMutate.h"
+#include "FuzzingTraits.h"
+#include "chrome/common/ipc_channel.h"
+#include "chrome/common/ipc_message.h"
+#include "chrome/common/file_descriptor_set_posix.h"
+#include "mozilla/ipc/Faulty.h"
+#include "nsComponentManagerUtils.h"
+#include "nsNetCID.h"
+#include "nsIFile.h"
+#include "nsIFileStreams.h"
+#include "nsILineInputStream.h"
+#include "nsIRunnable.h"
+#include "nsThreadUtils.h"
+#include "nsLocalFile.h"
+#include "nsNetCID.h"
+#include "nsPrintfCString.h"
+#include "nsTArray.h"
+#include "nsXULAppAPI.h"
+#include "prenv.h"
+
+#ifdef IsLoggingEnabled
+// This is defined in the Windows SDK urlmon.h
+# undef IsLoggingEnabled
+#endif
+
+namespace mozilla {
+namespace ipc {
+
+using namespace mozilla::fuzzing;
+
+/**
+ * FuzzIntegralType mutates an incercepted integral type of a pickled message.
+ */
+template <typename T>
+void FuzzIntegralType(T* v, bool largeValues) {
+ static_assert(std::is_integral_v<T> == true, "T must be an integral type");
+ switch (FuzzingTraits::Random(6)) {
+ case 0:
+ if (largeValues) {
+ (*v) = RandomInteger<T>();
+ break;
+ }
+ [[fallthrough]];
+ case 1:
+ if (largeValues) {
+ (*v) = RandomNumericLimit<T>();
+ break;
+ }
+ [[fallthrough]];
+ case 2:
+ if (largeValues) {
+ (*v) = RandomIntegerRange<T>(std::numeric_limits<T>::min(),
+ std::numeric_limits<T>::max());
+ break;
+ }
+ [[fallthrough]];
+ default:
+ switch (FuzzingTraits::Random(2)) {
+ case 0:
+ // Prevent underflow
+ if (*v != std::numeric_limits<T>::min()) {
+ (*v)--;
+ break;
+ }
+ [[fallthrough]];
+ case 1:
+ // Prevent overflow
+ if (*v != std::numeric_limits<T>::max()) {
+ (*v)++;
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * FuzzFloatingPointType mutates an incercepted floating-point type of a
+ * pickled message.
+ */
+template <typename T>
+void FuzzFloatingPointType(T* v, bool largeValues) {
+ static_assert(std::is_floating_point_v<T> == true,
+ "T must be a floating point type");
+ switch (FuzzingTraits::Random(6)) {
+ case 0:
+ if (largeValues) {
+ (*v) = RandomNumericLimit<T>();
+ break;
+ }
+ [[fallthrough]];
+ case 1:
+ if (largeValues) {
+ (*v) = RandomFloatingPointRange<T>(std::numeric_limits<T>::min(),
+ std::numeric_limits<T>::max());
+ break;
+ }
+ [[fallthrough]];
+ default:
+ (*v) = RandomFloatingPoint<T>();
+ }
+}
+
+/**
+ * FuzzStringType mutates an incercepted string type of a pickled message.
+ */
+template <typename T>
+void FuzzStringType(T& v, const T& literal1, const T& literal2) {
+ switch (FuzzingTraits::Random(5)) {
+ case 4:
+ v = v + v;
+ [[fallthrough]];
+ case 3:
+ v = v + v;
+ [[fallthrough]];
+ case 2:
+ v = v + v;
+ break;
+ case 1:
+ v += literal1;
+ break;
+ case 0:
+ v = literal2;
+ break;
+ }
+}
+
+Faulty::Faulty()
+ // Mutate messages as a blob.
+ : mFuzzMessages(!!PR_GetEnv("FAULTY_MESSAGES"))
+ // 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"))
+ // Use the provided blacklist as whitelist.
+ ,
+ mUseAsWhitelist(!!PR_GetEnv("FAULTY_AS_WHITELIST"))
+ // Sets up our target process.
+ ,
+ mIsValidProcessType(IsValidProcessType()) {
+ if (mIsValidProcessType) {
+ FAULTY_LOG("Initializing for new process of type '%s' with pid %u.",
+ XRE_GetProcessTypeString(), getpid());
+
+ /* Setup random seed. */
+ const char* userSeed = PR_GetEnv("FAULTY_SEED");
+ unsigned long randomSeed = static_cast<unsigned long>(PR_IntervalNow());
+ if (userSeed) {
+ long n = std::strtol(userSeed, nullptr, 10);
+ if (n != 0) {
+ randomSeed = static_cast<unsigned long>(n);
+ }
+ }
+ FuzzingTraits::Rng().seed(randomSeed);
+
+ /* Setup directory for dumping messages. */
+ mMessagePath = PR_GetEnv("FAULTY_MESSAGE_PATH");
+ if (mMessagePath && *mMessagePath) {
+ if (CreateOutputDirectory(mMessagePath) != NS_OK) {
+ mMessagePath = nullptr;
+ }
+ }
+
+ /* Set IPC messages blacklist. */
+ mBlacklistPath = PR_GetEnv("FAULTY_BLACKLIST");
+ if (mBlacklistPath && *mBlacklistPath) {
+ FAULTY_LOG("* Using message blacklist = %s", mBlacklistPath);
+ }
+
+ FAULTY_LOG("* Fuzzing strategy: messages = %s",
+ mFuzzMessages ? "enabled" : "disabled");
+ FAULTY_LOG("* Fuzzing strategy: pickle = %s",
+ mFuzzPickle ? "enabled" : "disabled");
+ FAULTY_LOG("* Fuzzing strategy: pipe = %s",
+ mFuzzPipes ? "enabled" : "disabled");
+ FAULTY_LOG("* Fuzzing probability = %u", DefaultProbability());
+ FAULTY_LOG("* Fuzzing mutation factor = %u", MutationFactor());
+ FAULTY_LOG("* RNG seed = %lu", randomSeed);
+
+ sMsgCounter = 0;
+ }
+}
+
+// static
+bool Faulty::IsValidProcessType(void) {
+ bool isValidProcessType;
+ const bool targetChildren = !!PR_GetEnv("FAULTY_CHILDREN");
+ const bool targetParent = !!PR_GetEnv("FAULTY_PARENT");
+ const bool isParent = XRE_IsParentProcess();
+
+ if (targetChildren && !targetParent) {
+ // Fuzz every child process type but not the parent process.
+ isValidProcessType = isParent;
+ } else if (!targetChildren && targetParent && !isParent) {
+ // Fuzz inside any of the above child process only.
+ isValidProcessType = true;
+ } else if (targetChildren && targetParent) {
+ // Fuzz every process type.
+ isValidProcessType = true;
+ } else {
+ // Fuzz no process type at all.
+ isValidProcessType = false;
+ }
+
+ if (!isValidProcessType) {
+ FAULTY_LOG("Disabled for this process of type '%s' with pid %d.",
+ XRE_GetProcessTypeString(), getpid());
+ }
+
+ return isValidProcessType;
+}
+
+// static
+unsigned int Faulty::DefaultProbability() {
+ static std::once_flag flag;
+ static unsigned probability;
+
+ std::call_once(flag, [&] {
+ probability = FAULTY_DEFAULT_PROBABILITY;
+ // Defines the likelihood of fuzzing a message.
+ if (const char* p = PR_GetEnv("FAULTY_PROBABILITY")) {
+ long n = std::strtol(p, nullptr, 10);
+ if (n != 0) {
+ probability = n;
+ }
+ }
+ });
+
+ return probability;
+}
+
+// static
+bool Faulty::IsLoggingEnabled(void) {
+ static bool enabled;
+ static std::once_flag flag;
+ std::call_once(flag, [&] { enabled = !!PR_GetEnv("FAULTY_ENABLE_LOGGING"); });
+ return enabled;
+}
+
+// static
+uint32_t Faulty::MutationFactor() {
+ static uint64_t sPropValue = FAULTY_DEFAULT_MUTATION_FACTOR;
+ static bool sInitialized = false;
+
+ if (sInitialized) {
+ return sPropValue;
+ }
+ sInitialized = true;
+
+ const char* factor = PR_GetEnv("FAULTY_MUTATION_FACTOR");
+ if (factor) {
+ long n = strtol(factor, nullptr, 10);
+ if (n != 0) {
+ sPropValue = n;
+ return sPropValue;
+ }
+ }
+ return sPropValue;
+}
+
+// static
+Faulty& Faulty::instance() {
+ static Faulty faulty;
+ return faulty;
+}
+
+//
+// Strategy: Pipes
+//
+
+void Faulty::MaybeCollectAndClosePipe(int aPipe, unsigned int aProbability) {
+#ifndef _WINDOWS
+ if (!mFuzzPipes) {
+ return;
+ }
+
+ if (aPipe > -1) {
+ FAULTY_LOG("Collecting pipe %d to bucket of pipes (count: %zu)", aPipe,
+ mFds.size());
+ mFds.insert(aPipe);
+ }
+
+ if (mFds.size() > 0 && FuzzingTraits::Sometimes(aProbability)) {
+ std::set<int>::iterator it(mFds.begin());
+ std::advance(it, FuzzingTraits::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);
+ }
+#endif
+}
+
+//
+// Strategy: Pickle
+//
+
+void Faulty::MutateBool(bool* aValue) { *aValue = !(*aValue); }
+
+void Faulty::FuzzBool(bool* aValue, unsigned int aProbability) {
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) {
+ bool oldValue = *aValue;
+ MutateBool(aValue);
+ FAULTY_LOG("Message field |bool| of value: %d mutated to: %d",
+ (int)oldValue, (int)*aValue);
+ }
+ }
+}
+
+void Faulty::MutateChar(char* aValue) { FuzzIntegralType<char>(aValue, true); }
+
+void Faulty::FuzzChar(char* aValue, unsigned int aProbability) {
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) {
+ char oldValue = *aValue;
+ MutateChar(aValue);
+ FAULTY_LOG("Message field |char| of value: %c mutated to: %c", oldValue,
+ *aValue);
+ }
+ }
+}
+
+void Faulty::MutateUChar(unsigned char* aValue) {
+ FuzzIntegralType<unsigned char>(aValue, true);
+}
+
+void Faulty::FuzzUChar(unsigned char* aValue, unsigned int aProbability) {
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) {
+ unsigned char oldValue = *aValue;
+ MutateUChar(aValue);
+ FAULTY_LOG("Message field |unsigned char| of value: %u mutated to: %u",
+ oldValue, *aValue);
+ }
+ }
+}
+
+void Faulty::MutateInt16(int16_t* aValue) {
+ FuzzIntegralType<int16_t>(aValue, true);
+}
+
+void Faulty::FuzzInt16(int16_t* aValue, unsigned int aProbability) {
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) {
+ int16_t oldValue = *aValue;
+ MutateInt16(aValue);
+ FAULTY_LOG("Message field |int16| of value: %d mutated to: %d", oldValue,
+ *aValue);
+ }
+ }
+}
+
+void Faulty::MutateUInt16(uint16_t* aValue) {
+ FuzzIntegralType<uint16_t>(aValue, true);
+}
+
+void Faulty::FuzzUInt16(uint16_t* aValue, unsigned int aProbability) {
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) {
+ uint16_t oldValue = *aValue;
+ MutateUInt16(aValue);
+ FAULTY_LOG("Message field |uint16| of value: %d mutated to: %d", oldValue,
+ *aValue);
+ }
+ }
+}
+
+void Faulty::MutateInt(int* aValue) {
+ FuzzIntegralType<int>(aValue, mUseLargeValues);
+}
+
+void Faulty::FuzzInt(int* aValue, unsigned int aProbability) {
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) {
+ int oldValue = *aValue;
+ MutateInt(aValue);
+ FAULTY_LOG("Message field |int| of value: %d mutated to: %d", oldValue,
+ *aValue);
+ }
+ }
+}
+
+void Faulty::MutateUInt32(uint32_t* aValue) {
+ FuzzIntegralType<uint32_t>(aValue, mUseLargeValues);
+}
+
+void Faulty::FuzzUInt32(uint32_t* aValue, unsigned int aProbability) {
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) {
+ uint32_t oldValue = *aValue;
+ MutateUInt32(aValue);
+ FAULTY_LOG("Message field |uint32| of value: %u mutated to: %u", oldValue,
+ *aValue);
+ }
+ }
+}
+
+void Faulty::MutateLong(long* aValue) {
+ FuzzIntegralType<long>(aValue, mUseLargeValues);
+}
+
+void Faulty::FuzzLong(long* aValue, unsigned int aProbability) {
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) {
+ long oldValue = *aValue;
+ MutateLong(aValue);
+ FAULTY_LOG("Message field |long| of value: %ld mutated to: %ld", oldValue,
+ *aValue);
+ }
+ }
+}
+
+void Faulty::MutateULong(unsigned long* aValue) {
+ FuzzIntegralType<unsigned long>(aValue, mUseLargeValues);
+}
+
+void Faulty::FuzzULong(unsigned long* aValue, unsigned int aProbability) {
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) {
+ unsigned long oldValue = *aValue;
+ MutateULong(aValue);
+ FAULTY_LOG("Message field |unsigned long| of value: %lu mutated to: %lu",
+ oldValue, *aValue);
+ }
+ }
+}
+
+void Faulty::MutateUInt64(uint64_t* aValue) {
+ FuzzIntegralType<uint64_t>(aValue, mUseLargeValues);
+}
+
+void Faulty::FuzzUInt64(uint64_t* aValue, unsigned int aProbability) {
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) {
+ uint64_t oldValue = *aValue;
+ MutateUInt64(aValue);
+ FAULTY_LOG("Message field |uint64| of value: %" PRIu64
+ " mutated to: %" PRIu64,
+ oldValue, *aValue);
+ }
+ }
+}
+
+void Faulty::MutateInt64(int64_t* aValue) {
+ FuzzIntegralType<int64_t>(aValue, mUseLargeValues);
+}
+
+void Faulty::FuzzInt64(int64_t* aValue, unsigned int aProbability) {
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) {
+ int64_t oldValue = *aValue;
+ MutateInt64(aValue);
+ FAULTY_LOG("Message field |int64| of value: %" PRIu64
+ " mutated to: %" PRIu64,
+ oldValue, *aValue);
+ }
+ }
+}
+
+void Faulty::MutateDouble(double* aValue) {
+ FuzzFloatingPointType<double>(aValue, mUseLargeValues);
+}
+
+void Faulty::FuzzDouble(double* aValue, unsigned int aProbability) {
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) {
+ double oldValue = *aValue;
+ MutateDouble(aValue);
+ FAULTY_LOG("Message field |double| of value: %f mutated to: %f", oldValue,
+ *aValue);
+ }
+ }
+}
+
+void Faulty::MutateFloat(float* aValue) {
+ FuzzFloatingPointType<float>(aValue, mUseLargeValues);
+}
+
+void Faulty::FuzzFloat(float* aValue, unsigned int aProbability) {
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) {
+ float oldValue = *aValue;
+ MutateFloat(aValue);
+ FAULTY_LOG("Message field |float| of value: %f mutated to: %f", oldValue,
+ *aValue);
+ }
+ }
+}
+
+void Faulty::FuzzString(std::string& aValue, unsigned int aProbability) {
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) {
+ std::string oldValue = aValue;
+ FuzzStringType<std::string>(aValue, "xoferiF", std::string());
+ FAULTY_LOG("Message field |string| of value: %s mutated to: %s",
+ oldValue.c_str(), aValue.c_str());
+ }
+ }
+}
+
+void Faulty::FuzzWString(std::wstring& aValue, unsigned int aProbability) {
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) {
+ std::wstring oldValue = aValue;
+ FAULTY_LOG("Message field |wstring|");
+ FuzzStringType<std::wstring>(aValue, L"xoferiF", std::wstring());
+ }
+ }
+}
+
+// static
+nsresult Faulty::CreateOutputDirectory(const char* aPathname) {
+ nsCOMPtr<nsIFile> path;
+ bool exists;
+ nsresult rv;
+
+ rv = NS_NewNativeLocalFile(nsDependentCString(aPathname), true,
+ getter_AddRefs(path));
+
+ rv = path->Exists(&exists);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!exists) {
+ rv = path->Create(nsIFile::DIRECTORY_TYPE, 0755);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ return NS_OK;
+}
+
+/* static */
+nsresult Faulty::ReadFile(const char* aPathname, nsTArray<nsCString>& aArray) {
+ nsresult rv;
+ nsCOMPtr<nsIFile> file;
+
+ rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(aPathname), true,
+ getter_AddRefs(file));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ bool exists = false;
+ rv = file->Exists(&exists);
+ if (NS_WARN_IF(NS_FAILED(rv)) || !exists) {
+ return rv;
+ }
+
+ nsCOMPtr<nsIFileInputStream> fileStream(
+ do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = fileStream->Init(file, -1, -1, 0);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCOMPtr<nsILineInputStream> lineStream(do_QueryInterface(fileStream, &rv));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsAutoCString line;
+ bool more = true;
+ do {
+ rv = lineStream->ReadLine(line, &more);
+ if (line.IsEmpty()) {
+ continue;
+ }
+ if (line.CharAt(0) == '#') {
+ /* Ignore comments. */
+ continue;
+ }
+ aArray.AppendElement(line);
+ } while (more);
+
+ return NS_OK;
+}
+
+bool Faulty::IsMessageNameBlacklisted(const char* aMessageName) {
+ static bool sFileLoaded = false;
+ static nsTArray<nsCString> sMessageBlacklist;
+
+ if (!sFileLoaded && mBlacklistPath) {
+ /* Run ReadFile() on the main thread to prevent
+ MOZ_ASSERT(NS_IsMainThread()) in nsStandardURL via nsNetStartup(). */
+ nsCOMPtr<nsIRunnable> r =
+ NS_NewRunnableFunction("Fuzzer::ReadBlacklistOnMainThread", [&]() {
+ if (Faulty::ReadFile(mBlacklistPath, sMessageBlacklist) != NS_OK) {
+ sFileLoaded = false;
+ } else {
+ sFileLoaded = true;
+ }
+ });
+ NS_DispatchToMainThread(r.forget(), NS_DISPATCH_SYNC);
+ }
+
+ if (!sFileLoaded) {
+ return false;
+ }
+
+ if (sMessageBlacklist.Length() == 0) {
+ return false;
+ }
+
+ return sMessageBlacklist.Contains(aMessageName);
+}
+
+// static
+std::vector<uint8_t> Faulty::GetDataFromIPCMessage(IPC::Message* aMsg) {
+ const Pickle::BufferList& buffers = aMsg->Buffers();
+ std::vector<uint8_t> data;
+ data.reserve(buffers.Size());
+
+ Pickle::BufferList::IterImpl i = buffers.Iter();
+ while (!i.Done()) {
+ size_t s = i.RemainingInSegment();
+ data.insert(data.end(), i.Data(), i.Data() + s);
+
+ i.Advance(buffers, s);
+ }
+
+ return data;
+}
+
+// static
+void Faulty::CopyFDs(IPC::Message* aDstMsg, IPC::Message* aSrcMsg) {
+#ifndef _WINDOWS
+ FileDescriptorSet* dstFdSet = aDstMsg->file_descriptor_set();
+ FileDescriptorSet* srcFdSet = aSrcMsg->file_descriptor_set();
+ for (size_t i = 0; i < srcFdSet->size(); i++) {
+ int fd = srcFdSet->GetDescriptorAt(i);
+ dstFdSet->Add(fd);
+ }
+#endif
+}
+
+UniquePtr<IPC::Message> Faulty::MutateIPCMessage(const char* aChannel,
+ UniquePtr<IPC::Message> aMsg,
+ unsigned int aProbability) {
+ if (!mIsValidProcessType || !mFuzzMessages) {
+ return aMsg;
+ }
+
+ sMsgCounter += 1;
+ LogMessage(aChannel, aMsg.get());
+
+ /* Skip immediately if we shall not try to fuzz this message. */
+ if (!FuzzingTraits::Sometimes(aProbability)) {
+ return aMsg;
+ }
+
+ const bool isMessageListed = IsMessageNameBlacklisted(aMsg->name());
+
+ /* Check if this message is blacklisted and shall not get fuzzed. */
+ if (isMessageListed && !mUseAsWhitelist) {
+ FAULTY_LOG("BLACKLISTED: %s", aMsg->name());
+ return aMsg;
+ }
+
+ /* Check if the message is whitelisted. */
+ if (!isMessageListed && mUseAsWhitelist) {
+ /* Silently skip this message. */
+ return aMsg;
+ }
+
+ /* Retrieve BufferLists as data from original message. */
+ std::vector<uint8_t> data(GetDataFromIPCMessage(aMsg.get()));
+
+ /* Check if there is enough data in the message to fuzz. */
+ uint32_t headerSize = aMsg->HeaderSizeFromData(nullptr, nullptr);
+ if (headerSize == data.size()) {
+ FAULTY_LOG("IGNORING: %s", aMsg->name());
+ return aMsg;
+ }
+
+ /* Mutate the message data. */
+ size_t maxMutations = FuzzingTraits::Frequency(data.size(), MutationFactor());
+ FAULTY_LOG("FUZZING (%zu bytes): %s", maxMutations, aMsg->name());
+ while (maxMutations--) {
+ /* Ignore the header data of the message. */
+ uint32_t pos = RandomIntegerRange<uint32_t>(headerSize, data.size() - 1);
+ switch (FuzzingTraits::Random(6)) {
+ case 0:
+ break;
+ case 1:
+ data.at(pos) = RandomIntegerRange<uint8_t>(0, 1);
+ break;
+ case 2:
+ data.at(pos) ^= (1 << FuzzingTraits::Random(9));
+ break;
+ case 3:
+ data.at(pos) = RandomIntegerRange<uint8_t>(254, 255);
+ break;
+ default:
+ data.at(pos) = RandomIntegerRange<uint8_t>(0, 255);
+ }
+ }
+
+ /* Build new message. */
+ auto mutatedMsg = MakeUnique<IPC::Message>(
+ reinterpret_cast<const char*>(data.data()), data.size());
+ CopyFDs(mutatedMsg.get(), aMsg.get());
+
+ /* Dump original message for diff purposes. */
+ DumpMessage(aChannel, aMsg.get(),
+ nsPrintfCString(".%zu.o", sMsgCounter).get());
+ /* Dump mutated message for diff purposes. */
+ DumpMessage(aChannel, mutatedMsg.get(),
+ nsPrintfCString(".%zu.m", sMsgCounter).get());
+
+ return mutatedMsg;
+}
+
+void Faulty::LogMessage(const char* aChannel, IPC::Message* aMsg) {
+ if (!mIsValidProcessType) {
+ return;
+ }
+
+ std::string fileName =
+ nsPrintfCString("message.%u.%zu", getpid(), sMsgCounter).get();
+
+ FAULTY_LOG("Process: %u | Size: %10zu | %-20s | %s => %s",
+ XRE_GetProcessType(), aMsg->Buffers().Size(), fileName.c_str(),
+ aChannel, aMsg->name());
+}
+
+void Faulty::DumpMessage(const char* aChannel, IPC::Message* aMsg,
+ std::string aAppendix) {
+ if (!mIsValidProcessType || !mMessagePath) {
+ return;
+ }
+
+ std::vector<uint8_t> data(GetDataFromIPCMessage(aMsg));
+ std::string fileName;
+
+ if (!aAppendix.empty()) {
+ fileName = nsPrintfCString("%s/message.%u%s", mMessagePath, getpid(),
+ aAppendix.c_str())
+ .get();
+ } else {
+ fileName = nsPrintfCString("%s/%s", mMessagePath, fileName.c_str()).get();
+ }
+
+ std::fstream fp;
+ fp.open(fileName, std::fstream::out | std::fstream::binary);
+ fp.write(reinterpret_cast<const char*>(data.data()), data.size());
+ fp.close();
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/tools/fuzzing/faulty/Faulty.h b/tools/fuzzing/faulty/Faulty.h
new file mode 100644
index 0000000000..21ea1276eb
--- /dev/null
+++ b/tools/fuzzing/faulty/Faulty.h
@@ -0,0 +1,134 @@
+/* -*- 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 <set>
+#include <string>
+#include <vector>
+#include "base/string16.h"
+#include "nsDebug.h"
+#include "nsTArray.h"
+#include "mozilla/UniquePtr.h"
+
+#ifdef IsLoggingEnabled
+// This is defined in the Windows SDK urlmon.h
+# undef IsLoggingEnabled
+#endif
+
+#define FAULTY_DEFAULT_PROBABILITY 1000
+#define FAULTY_DEFAULT_MUTATION_FACTOR 10
+#define FAULTY_LOG(fmt, args...) \
+ if (mozilla::ipc::Faulty::IsLoggingEnabled()) { \
+ printf_stderr("[Faulty] (%10u) " fmt "\n", getpid(), ##args); \
+ }
+
+namespace IPC {
+// Needed for blacklisting messages.
+class Message;
+} // namespace IPC
+
+namespace mozilla {
+namespace ipc {
+
+class Faulty {
+ public:
+ // Used as a default argument for the Fuzz|datatype| methods.
+ static unsigned int DefaultProbability();
+ static bool IsLoggingEnabled(void);
+ static std::vector<uint8_t> GetDataFromIPCMessage(IPC::Message* aMsg);
+ static nsresult CreateOutputDirectory(const char* aPathname);
+ static nsresult ReadFile(const char* aPathname, nsTArray<nsCString>& aArray);
+ static void CopyFDs(IPC::Message* aDstMsg, IPC::Message* aSrcMsg);
+
+ static Faulty& instance();
+
+ // Fuzzing methods for Pickle.
+ void FuzzBool(bool* aValue, unsigned int aProbability = DefaultProbability());
+ void FuzzChar(char* aValue, unsigned int aProbability = DefaultProbability());
+ void FuzzUChar(unsigned char* aValue,
+ unsigned int aProbability = DefaultProbability());
+ void FuzzInt16(int16_t* aValue,
+ unsigned int aProbability = DefaultProbability());
+ void FuzzUInt16(uint16_t* aValue,
+ unsigned int aProbability = DefaultProbability());
+ void FuzzInt(int* aValue, unsigned int aProbability = DefaultProbability());
+ void FuzzUInt32(uint32_t* aValue,
+ unsigned int aProbability = DefaultProbability());
+ void FuzzLong(long* aValue, unsigned int aProbability = DefaultProbability());
+ void FuzzULong(unsigned long* aValue,
+ unsigned int aProbability = DefaultProbability());
+ void FuzzInt64(int64_t* aValue,
+ unsigned int aProbability = DefaultProbability());
+ void FuzzUInt64(uint64_t* aValue,
+ unsigned int aProbability = DefaultProbability());
+ void FuzzFloat(float* aValue,
+ unsigned int aProbability = DefaultProbability());
+ void FuzzDouble(double* aValue,
+ unsigned int aProbability = DefaultProbability());
+ void FuzzString(std::string& aValue,
+ unsigned int aProbability = DefaultProbability());
+ void FuzzWString(std::wstring& aValue,
+ unsigned int aProbability = DefaultProbability());
+ void FuzzBytes(void* aData, int aLength,
+ unsigned int aProbability = DefaultProbability());
+
+ // Fuzzing methods for pipe fuzzing.
+ void MaybeCollectAndClosePipe(
+ int aPipe, unsigned int aProbability = DefaultProbability());
+
+ // Fuzzing methods for message blob fuzzing.
+ void DumpMessage(const char* aChannel, IPC::Message* aMsg,
+ std::string aAppendix = nullptr);
+ bool IsMessageNameBlacklisted(const char* aMessageName);
+ UniquePtr<IPC::Message> MutateIPCMessage(
+ const char* aChannel, UniquePtr<IPC::Message> aMsg,
+ unsigned int aProbability = DefaultProbability());
+
+ void LogMessage(const char* aChannel, IPC::Message* aMsg);
+
+ private:
+ std::set<int> mFds;
+
+ const bool mFuzzMessages;
+ const bool mFuzzPipes;
+ const bool mFuzzPickle;
+ const bool mUseLargeValues;
+ const bool mUseAsWhitelist;
+ const bool mIsValidProcessType;
+
+ const char* mMessagePath;
+ const char* mBlacklistPath;
+
+ size_t sMsgCounter;
+
+ Faulty();
+ DISALLOW_EVIL_CONSTRUCTORS(Faulty);
+
+ static bool IsValidProcessType(void);
+ static uint32_t MutationFactor();
+
+ // Fuzzing methods for Pickle
+ 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 MutateFloat(float* aValue);
+ void MutateDouble(double* aValue);
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif
diff --git a/tools/fuzzing/faulty/moz.build b/tools/fuzzing/faulty/moz.build
new file mode 100644
index 0000000000..afe1aa7a45
--- /dev/null
+++ b/tools/fuzzing/faulty/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += ["Faulty.cpp"]
+
+EXPORTS.mozilla.ipc += ["Faulty.h"]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"