tools/fuzzing/faulty/Faulty.cpp
author Sylvestre Ledru <sledru@mozilla.com>
Wed, 28 Nov 2018 19:31:21 +0000
changeset 505000 361a75deb3cedb5dd1c65d881fa16d97efb0f144
parent 495612 d95dd4c9cdca35de156b2cf180591ac7ad094cf4
child 505383 6f3709b3878117466168c40affa7bca0b60cf75b
permissions -rw-r--r--
Bug 1503537 - Get rid of the pdfium & mortar code r=peterv Differential Revision: https://phabricator.services.mozilla.com/D10352

/* -*- 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 <prinrval.h>
#include <unistd.h>
#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 "mozilla/TypeTraits.h"
#include "nsNetCID.h"
#include "nsIEventTarget.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"


namespace mozilla {
namespace ipc {

using namespace mozilla::fuzzing;

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 + 1)) + 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>(0.0, 10.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;
      }
      MOZ_FALLTHROUGH;
    case 1:
      if (largeValues) {
        (*v) = RandomNumericLimit<T>();
        break;
      }
      MOZ_FALLTHROUGH;
    case 2:
      if (largeValues) {
        (*v) = RandomIntegerRange<T>(std::numeric_limits<T>::min(),
                                     std::numeric_limits<T>::max());
        break;
      }
      MOZ_FALLTHROUGH;
    default:
      switch(random() % 2) {
        case 0:
          // Prevent underflow
          if (*v != std::numeric_limits<T>::min()) {
            (*v)--;
            break;
          }
          MOZ_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(mozilla::IsFloatingPoint<T>::value == true,
                "T must be a floating point type");
  switch (random() % 6) {
    case 0:
      if (largeValues) {
        (*v) = RandomNumericLimit<T>();
        break;
    }
    MOZ_FALLTHROUGH;
    case 1:
      if (largeValues) {
        (*v) = RandomFloatingPointRange<T>(std::numeric_limits<T>::min(),
                                           std::numeric_limits<T>::max());
        break;
      }
    MOZ_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 (random() % 5) {
    case 4:
      v = v + v;
      MOZ_FALLTHROUGH;
    case 3:
      v = v + v;
      MOZ_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_ChildProcessTypeToString(XRE_GetProcessType()),
      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);
      }
    }
    srandom(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", sDefaultProbability);
    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");
  unsigned short int currentProcessType = XRE_GetProcessType();

  if (targetChildren && !targetParent) {
    // Fuzz every child process type but not the parent process.
    isValidProcessType = currentProcessType == GeckoProcessType_Default;
  } else if (!targetChildren && targetParent
          && (currentProcessType == GeckoProcessType_Plugin
          || currentProcessType == GeckoProcessType_Content
          || currentProcessType == GeckoProcessType_GMPlugin
          || currentProcessType == GeckoProcessType_GPU
          || currentProcessType == GeckoProcessType_VR)) {
    // 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_ChildProcessTypeToString(XRE_GetProcessType()),
      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");
}


// 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)
{
  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);
  }
}

//
// 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::MutateSize(size_t* aValue)
{
  FuzzIntegralType<size_t>(aValue, mUseLargeValues);
}

void
Faulty::FuzzSize(size_t* aValue, unsigned int aProbability)
{
  if (mIsValidProcessType) {
    if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) {
      size_t oldValue = *aValue;
      MutateSize(aValue);
      FAULTY_LOG("Message field |size_t| of value: %zu mutated 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 && 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) {
    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);
    }
}

IPC::Message *
Faulty::MutateIPCMessage(const char *aChannel, IPC::Message* aMsg, unsigned int aProbability) {
  if (!mIsValidProcessType || !mFuzzMessages) {
    return aMsg;
  }

  sMsgCounter += 1;
  LogMessage(aChannel, aMsg);

  /* 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));

  /* 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 << RandomIntegerRange<uint8_t>(0, 8));
        break;
      default:
        data.at(pos) = RandomIntegerRange<uint8_t>(255, 255);
    }
  }

  /* Build new message. */
  auto *mutatedMsg = new IPC::Message(reinterpret_cast<const char*>(data.data()), data.size());
  CopyFDs(mutatedMsg, aMsg);

  /* Dump original message for diff purposes. */
  DumpMessage(aChannel, aMsg, nsPrintfCString(".%zu.o", sMsgCounter).get());
  /* Dump mutated message for diff purposes. */
  DumpMessage(aChannel, mutatedMsg, nsPrintfCString(".%zu.m", sMsgCounter).get());

  delete aMsg;

  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