diff options
Diffstat (limited to 'tools/fuzzing/faulty')
-rw-r--r-- | tools/fuzzing/faulty/Faulty.cpp | 786 | ||||
-rw-r--r-- | tools/fuzzing/faulty/Faulty.h | 134 | ||||
-rw-r--r-- | tools/fuzzing/faulty/moz.build | 13 |
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" |