summaryrefslogtreecommitdiffstats
path: root/ipc/glue/Faulty.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/glue/Faulty.cpp')
-rw-r--r--ipc/glue/Faulty.cpp654
1 files changed, 654 insertions, 0 deletions
diff --git a/ipc/glue/Faulty.cpp b/ipc/glue/Faulty.cpp
new file mode 100644
index 000000000..5c326cd3f
--- /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 <cerrno>
+#include <prinrval.h>
+#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 <cmath>
+#include <climits>
+
+namespace mozilla {
+namespace ipc {
+
+const unsigned int Faulty::sDefaultProbability = Faulty::DefaultProbability();
+const bool Faulty::sIsLoggingEnabled = Faulty::Logging();
+
+/**
+ * RandomNumericValue generates negative and positive integrals.
+ */
+template <typename T>
+T RandomIntegral()
+{
+ static_assert(mozilla::IsIntegral<T>::value == true,
+ "T must be an integral type");
+ double r = static_cast<double>(random() % ((sizeof(T) * CHAR_BIT) + 1));
+ T x = static_cast<T>(pow(2.0, r)) - 1;
+ if (std::numeric_limits<T>::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 <typename T>
+T RandomNumericLimit() {
+ static_assert(mozilla::IsArithmetic<T>::value == true,
+ "T must be an arithmetic type");
+ return random() % 2 == 0 ? std::numeric_limits<T>::min()
+ : std::numeric_limits<T>::max();
+}
+
+/**
+ * RandomIntegerRange returns a random integral within a user defined range.
+ */
+template <typename T>
+T RandomIntegerRange(T min, T max)
+{
+ static_assert(mozilla::IsIntegral<T>::value == true,
+ "T must be an integral type");
+ MOZ_ASSERT(min < max);
+ return static_cast<T>(random() % (max - min) + min);
+}
+
+/**
+ * RandomFloatingPointRange returns a random floating-point number within a
+ * user defined range.
+ */
+template <typename T>
+T RandomFloatingPointRange(T min, T max)
+{
+ static_assert(mozilla::IsFloatingPoint<T>::value == true,
+ "T must be a floating point type");
+ MOZ_ASSERT(min < max);
+ T x = static_cast<T>(random()) / static_cast<T>(RAND_MAX);
+ return min + x * (max - min);
+}
+
+/**
+ * RandomFloatingPoint returns a random floating-point number.
+ */
+template <typename T>
+T RandomFloatingPoint()
+{
+ static_assert(mozilla::IsFloatingPoint<T>::value == true,
+ "T must be a floating point type");
+ int radix = RandomIntegerRange<int>(std::numeric_limits<T>::min_exponent,
+ std::numeric_limits<T>::max_exponent);
+ T x = static_cast<T>(pow(2.0, static_cast<double>(radix)));
+ return x * RandomFloatingPointRange<T>(1.0, 2.0);
+}
+
+/**
+ * FuzzIntegralType mutates an incercepted integral type of a pickled message.
+ */
+template <typename T>
+void FuzzIntegralType(T* v, bool largeValues)
+{
+ static_assert(mozilla::IsIntegral<T>::value == true,
+ "T must be an integral type");
+ switch (random() % 6) {
+ case 0:
+ if (largeValues) {
+ (*v) = RandomIntegral<T>();
+ break;
+ }
+ // Fall through
+ case 1:
+ if (largeValues) {
+ (*v) = RandomNumericLimit<T>();
+ break;
+ }
+ // Fall through
+ case 2:
+ if (largeValues) {
+ (*v) = RandomIntegerRange<T>(std::numeric_limits<T>::min(),
+ std::numeric_limits<T>::max());
+ break;
+ }
+ // Fall through
+ default:
+ switch(random() % 2) {
+ case 0:
+ // Prevent underflow
+ if (*v != std::numeric_limits<T>::min()) {
+ (*v)--;
+ break;
+ }
+ // Fall through
+ 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(mozilla::IsFloatingPoint<T>::value == true,
+ "T must be a floating point type");
+ switch (random() % 6) {
+ case 0:
+ if (largeValues) {
+ (*v) = RandomNumericLimit<T>();
+ break;
+ }
+ // Fall through
+ case 1:
+ if (largeValues) {
+ (*v) = RandomFloatingPointRange<T>(std::numeric_limits<T>::min(),
+ std::numeric_limits<T>::max());
+ break;
+ }
+ // Fall through
+ 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 (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<unsigned long>(PR_IntervalNow());
+ if (userSeed) {
+ long n = std::strtol(userSeed, nullptr, 10);
+ if (n != 0) {
+ randomSeed = static_cast<unsigned long>(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<unsigned int>(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<int>::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<char>(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<unsigned char>(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<int16_t>(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<uint16_t>(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<int>(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<uint32_t>(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<long>(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<unsigned long>(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<size_t>(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<uint64_t>(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<int64_t>(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<double>(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<float>(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<std::string>(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<std::wstring>(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<string16>(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<char*>(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<char>(&aValue[i], true);
+ }
+ }
+ }
+ }
+}
+
+} // namespace ipc
+} // namespace mozilla