Bug 777600 - Add MessageManager Fuzzer. r=billm
authorChristoph Diehl <cdiehl@mozilla.com>
Tue, 28 Feb 2017 16:24:43 -0800
changeset 347140 d2620293368cb56446f884124a99686fc72abb3f
parent 347139 2fb4572708e825fdeb0f25de5b67078429a21b9d
child 347141 200abfdbd8ef47d2f4a0b401437044fc719d1e38
push id31491
push usercbook@mozilla.com
push dateMon, 13 Mar 2017 14:24:00 +0000
treeherdermozilla-central@8d9fd089cabd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs777600
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 777600 - Add MessageManager Fuzzer. r=billm
dom/base/nsFrameMessageManager.cpp
tools/fuzzing/common/FuzzingTraits.cpp
tools/fuzzing/common/FuzzingTraits.h
tools/fuzzing/common/moz.build
tools/fuzzing/messagemanager/MessageManagerFuzzer.cpp
tools/fuzzing/messagemanager/MessageManagerFuzzer.h
tools/fuzzing/messagemanager/moz.build
tools/fuzzing/moz.build
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -60,16 +60,20 @@
 #endif
 #ifdef XP_WIN
 #include <windows.h>
 # if defined(SendMessage)
 #  undef SendMessage
 # endif
 #endif
 
+#ifdef FUZZING
+#include "MessageManagerFuzzer.h"
+#endif
+
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
 
 nsFrameMessageManager::nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback,
                                              nsFrameMessageManager* aParentManager,
                                              /* mozilla::dom::ipc::MessageManagerFlags */ uint32_t aFlags)
  : mChrome(!!(aFlags & mozilla::dom::ipc::MM_CHROME)),
@@ -628,16 +632,26 @@ nsFrameMessageManager::SendMessage(const
     return NS_ERROR_UNEXPECTED;
   }
 
   StructuredCloneData data;
   if (aArgc >= 2 && !GetParamsForMessage(aCx, aJSON, JS::UndefinedHandleValue, data)) {
     return NS_ERROR_DOM_DATA_CLONE_ERR;
   }
 
+#ifdef FUZZING
+  if (data.DataLength() > 0) {
+    MessageManagerFuzzer::TryMutate(
+      aCx,
+      aMessageName,
+      &data,
+      JS::UndefinedHandleValue);
+  }
+#endif
+
   if (!AllowMessage(data.DataLength(), aMessageName)) {
     return NS_ERROR_FAILURE;
   }
 
   JS::Rooted<JSObject*> objects(aCx);
   if (aArgc >= 3 && aObjects.isObject()) {
     objects = &aObjects.toObject();
   }
@@ -713,16 +727,22 @@ nsFrameMessageManager::DispatchAsyncMess
                                             JSContext* aCx,
                                             uint8_t aArgc)
 {
   StructuredCloneData data;
   if (aArgc >= 2 && !GetParamsForMessage(aCx, aJSON, aTransfers, data)) {
     return NS_ERROR_DOM_DATA_CLONE_ERR;
   }
 
+#ifdef FUZZING
+  if (data.DataLength()) {
+    MessageManagerFuzzer::TryMutate(aCx, aMessageName, &data, aTransfers);
+  }
+#endif
+
   if (!AllowMessage(data.DataLength(), aMessageName)) {
     return NS_ERROR_FAILURE;
   }
 
   JS::Rooted<JSObject*> objects(aCx);
   if (aArgc >= 3 && aObjects.isObject()) {
     objects = &aObjects.toObject();
   }
new file mode 100644
--- /dev/null
+++ b/tools/fuzzing/common/FuzzingTraits.cpp
@@ -0,0 +1,28 @@
+/* -*- 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 "FuzzingTraits.h"
+
+namespace mozilla {
+namespace fuzzing {
+
+/* static */
+unsigned int
+FuzzingTraits::Random(unsigned int aMax)
+{
+  MOZ_ASSERT(aMax > 0, "aMax needs to be bigger than 0");
+  return static_cast<unsigned int>(random() % aMax);
+}
+
+/* static */
+bool
+FuzzingTraits::Sometimes(unsigned int aProbability)
+{
+  return FuzzingTraits::Random(aProbability) == 0;
+}
+
+} // namespace fuzzing
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/tools/fuzzing/common/FuzzingTraits.h
@@ -0,0 +1,96 @@
+/* -*- 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_fuzzing_FuzzingTraits_h
+#define mozilla_fuzzing_FuzzingTraits_h
+
+#include "mozilla/Assertions.h"
+#include "mozilla/TypeTraits.h"
+#include <random>
+
+namespace mozilla {
+namespace fuzzing {
+
+/**
+ * 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();
+}
+
+/**
+ * RandomInteger generates negative and positive integers in 2**n increments.
+ */
+template <typename T>
+T RandomInteger()
+{
+  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;
+}
+
+/**
+ * RandomIntegerRange returns a random integral within a [min, max] 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
+ * [min, max] 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 in x^2 increments.
+ */
+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, 1.0);
+}
+
+class FuzzingTraits
+{
+public:
+  static unsigned int Random(unsigned int aMax);
+  static bool Sometimes(unsigned int aProbability);
+};
+
+} // namespace fuzzing
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/tools/fuzzing/common/moz.build
@@ -0,0 +1,15 @@
+# -*- 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 += [
+  'FuzzingTraits.cpp'
+]
+
+EXPORTS += [
+  'FuzzingTraits.h'
+]
+
+FINAL_LIBRARY = 'xul'
new file mode 100644
--- /dev/null
+++ b/tools/fuzzing/messagemanager/MessageManagerFuzzer.cpp
@@ -0,0 +1,337 @@
+/* -*- 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 <climits>
+#include <cmath>
+#include "FuzzingTraits.h"
+#include "jsapi.h"
+#include "jsfriendapi.h"
+#include "prenv.h"
+#include "MessageManagerFuzzer.h"
+#include "mozilla/ErrorResult.h"
+#include "nsDebug.h"
+#include "nsError.h"
+#include "nsFrameMessageManager.h"
+#include "nsJSUtils.h"
+#include "nsXULAppAPI.h"
+#include "nsNetCID.h"
+#include "nsString.h"
+#include "nsUnicharUtils.h"
+#include "nsIFile.h"
+#include "nsIFileStreams.h"
+#include "nsILineInputStream.h"
+#include "nsLocalFile.h"
+#include "nsTArray.h"
+
+
+#define MESSAGEMANAGER_FUZZER_DEFAULT_MUTATION_PROBABILITY 2
+#define MSGMGR_FUZZER_LOG(fmt, args...)                         \
+  if (MessageManagerFuzzer::IsLoggingEnabled()) {               \
+    printf_stderr("[MessageManagerFuzzer] " fmt "\n", ## args); \
+  }
+
+namespace mozilla {
+namespace dom {
+
+using namespace fuzzing;
+using namespace ipc;
+
+/* static */
+void
+MessageManagerFuzzer::ReadFile(const char* path, nsTArray<nsCString> &aArray)
+{
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(path),
+                                true,
+                                getter_AddRefs(file));
+  NS_ENSURE_SUCCESS_VOID(rv);
+
+  bool exists = false;
+  rv = file->Exists(&exists);
+  if (NS_FAILED(rv) || !exists) {
+    return;
+  }
+
+  nsCOMPtr<nsIFileInputStream> fileStream(
+    do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv));
+  NS_ENSURE_SUCCESS_VOID(rv);
+
+  rv = fileStream->Init(file, -1, -1, false);
+  NS_ENSURE_SUCCESS_VOID(rv);
+
+  nsCOMPtr<nsILineInputStream> lineStream(do_QueryInterface(fileStream, &rv));
+  NS_ENSURE_SUCCESS_VOID(rv);
+
+  nsAutoCString line;
+  bool more = true;
+  do {
+    rv = lineStream->ReadLine(line, &more);
+    NS_ENSURE_SUCCESS_VOID(rv);
+    aArray.AppendElement(line);
+  } while (more);
+}
+
+/* static */
+bool
+MessageManagerFuzzer::IsMessageNameBlacklisted(const nsAString& aMessageName) {
+  static bool sFileLoaded = false;
+  static nsTArray<nsCString> valuesInFile;
+
+  if (!sFileLoaded) {
+    ReadFile(PR_GetEnv("MESSAGEMANAGER_FUZZER_BLACKLIST"), valuesInFile);
+    sFileLoaded = true;
+  }
+
+  if (valuesInFile.Length() == 0) {
+    return false;
+  }
+
+  return valuesInFile.Contains(NS_ConvertUTF16toUTF8(aMessageName).get());
+}
+
+/* static */
+nsCString
+MessageManagerFuzzer::GetFuzzValueFromFile()
+{
+  static bool sFileLoaded = false;
+  static nsTArray<nsCString> valuesInFile;
+
+  if (!sFileLoaded) {
+    ReadFile(PR_GetEnv("MESSAGEMANAGER_FUZZER_STRINGSFILE"), valuesInFile);
+    sFileLoaded = true;
+  }
+
+  // If something goes wrong with importing the file we return an empty string.
+  if (valuesInFile.Length() == 0) {
+    return nsCString();
+  }
+
+  unsigned randIdx = RandomIntegerRange<unsigned>(0, valuesInFile.Length());
+  return valuesInFile.ElementAt(randIdx);
+}
+
+
+/* static */
+void
+MessageManagerFuzzer::MutateObject(JSContext* aCx,
+                                   JS::HandleValue aValue,
+                                   unsigned short int aRecursionCounter)
+{
+  JS::Rooted<JSObject*> object(aCx, &aValue.toObject());
+  JS::Rooted<JS::IdVector> ids(aCx, JS::IdVector(aCx));
+
+  if (!JS_Enumerate(aCx, object, &ids)) {
+    return;
+  }
+
+  for (size_t i = 0, n = ids.length(); i < n; i++) {
+    // Retrieve Property name.
+    nsAutoJSString propName;
+    if (!propName.init(aCx, ids[i])) {
+      continue;
+    }
+    MSGMGR_FUZZER_LOG("%*s- Property: %s",
+                      aRecursionCounter*4, "",
+                      NS_ConvertUTF16toUTF8(propName).get());
+
+    // The likelihood when a value gets fuzzed of this object.
+    if (!FuzzingTraits::Sometimes(DefaultMutationProbability())) {
+      continue;
+    }
+
+    // Retrieve Property value.
+    JS::RootedValue propertyValue(aCx);
+    JS_GetPropertyById(aCx, object, ids[i], &propertyValue);
+
+    JS::RootedValue newPropValue(aCx);
+    MutateValue(aCx, propertyValue, &newPropValue, aRecursionCounter);
+
+    JS_SetPropertyById(aCx, object, ids[i], newPropValue);
+  }
+}
+
+/* static */
+bool
+MessageManagerFuzzer::MutateValue(JSContext* aCx,
+                                  JS::HandleValue aValue,
+                                  JS::MutableHandleValue aOutMutationValue,
+                                  unsigned short int aRecursionCounter)
+{
+  if (aValue.isInt32()) {
+    if (FuzzingTraits::Sometimes(DefaultMutationProbability() * 2)) {
+      aOutMutationValue.set(JS::Int32Value(RandomNumericLimit<int>()));
+    } else {
+      aOutMutationValue.set(JS::Int32Value(RandomInteger<int>()));
+    }
+    MSGMGR_FUZZER_LOG("%*s! Mutated value of type |int32|: '%d' to '%d'",
+                      aRecursionCounter * 4, "",
+                      aValue.toInt32(), aOutMutationValue.toInt32());
+    return true;
+  }
+
+  if (aValue.isDouble()) {
+    aOutMutationValue.set(JS::DoubleValue(RandomFloatingPoint<double>()));
+    MSGMGR_FUZZER_LOG("%*s! Mutated value of type |double|: '%f' to '%f'",
+                      aRecursionCounter * 4, "",
+                      aValue.toDouble(), aOutMutationValue.toDouble());
+    return true;
+  }
+
+  if (aValue.isBoolean()) {
+    aOutMutationValue.set(JS::BooleanValue(bool(RandomIntegerRange(0, 2))));
+    MSGMGR_FUZZER_LOG("%*s! Mutated value of type |boolean|: '%d' to '%d'",
+                      aRecursionCounter * 4, "",
+                      aValue.toBoolean(), aOutMutationValue.toBoolean());
+    return true;
+  }
+
+  if (aValue.isString()) {
+    nsCString x = GetFuzzValueFromFile();
+    if (x.IsEmpty()) {
+      return false;
+    }
+    JSString* str = JS_NewStringCopyZ(aCx, x.get());
+    aOutMutationValue.set(JS::StringValue(str));
+    MSGMGR_FUZZER_LOG("%*s! Mutated value of type |string|: '%s' to '%s'",
+                      aRecursionCounter * 4, "",
+                      JS_EncodeString(aCx, aValue.toString()), x.get());
+    return true;
+  }
+
+  if (aValue.isObject()) {
+    aRecursionCounter++;
+    MSGMGR_FUZZER_LOG("%*s<Enumerating found object>",
+                      aRecursionCounter * 4, "");
+    MutateObject(aCx, aValue, aRecursionCounter);
+    aOutMutationValue.set(aValue);
+    return true;
+  }
+
+  return false;
+}
+
+/* static */
+bool
+MessageManagerFuzzer::Mutate(JSContext* aCx,
+                             const nsAString& aMessageName,
+                             ipc::StructuredCloneData* aData,
+                             const JS::Value& aTransfer)
+{
+  MSGMGR_FUZZER_LOG("Message: %s in process: %d",
+                   NS_ConvertUTF16toUTF8(aMessageName).get(),
+                   XRE_GetProcessType());
+
+  unsigned short int aRecursionCounter = 0;
+  ErrorResult rv;
+  JS::RootedValue t(aCx, aTransfer);
+
+  /* Read original StructuredCloneData. */
+  JS::RootedValue scdContent(aCx);
+  aData->Read(aCx, &scdContent, rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    rv.SuppressException();
+    JS_ClearPendingException(aCx);
+    return false;
+  }
+
+  JS::RootedValue scdMutationContent(aCx);
+  bool isMutated = MutateValue(aCx,
+                               scdContent,
+                               &scdMutationContent,
+                               aRecursionCounter);
+
+  /* Write mutated StructuredCloneData. */
+  ipc::StructuredCloneData mutatedStructuredCloneData;
+  mutatedStructuredCloneData.Write(aCx, scdMutationContent, t, rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    rv.SuppressException();
+    JS_ClearPendingException(aCx);
+    return false;
+  }
+
+  // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1346040
+  aData->Copy(mutatedStructuredCloneData);
+
+  /* Mutated and successfully written to StructuredCloneData object. */
+  if (isMutated) {
+    JS::RootedString str(aCx, JS_ValueToSource(aCx, scdMutationContent));
+    MSGMGR_FUZZER_LOG("Mutated '%s' Message: %s",
+                      NS_ConvertUTF16toUTF8(aMessageName).get(),
+                      JS_EncodeStringToUTF8(aCx, str));
+  }
+
+  return true;
+}
+
+/* static */
+unsigned int
+MessageManagerFuzzer::DefaultMutationProbability()
+{
+  static unsigned long sPropValue = MESSAGEMANAGER_FUZZER_DEFAULT_MUTATION_PROBABILITY;
+  static bool sInitialized = false;
+
+  if (sInitialized) {
+    return sPropValue;
+  }
+  sInitialized = true;
+
+  // Defines the likelihood of fuzzing a message.
+  const char* probability = PR_GetEnv("MESSAGEMANAGER_FUZZER_MUTATION_PROBABILITY");
+  if (probability) {
+    long n = std::strtol(probability, nullptr, 10);
+    if (n != 0) {
+      sPropValue = n;
+      return sPropValue;
+    }
+  }
+
+  return sPropValue;
+}
+
+/* static */
+bool
+MessageManagerFuzzer::IsLoggingEnabled()
+{
+  static bool sInitialized = false;
+  static bool sIsLoggingEnabled = false;
+
+  if (!sInitialized) {
+    sIsLoggingEnabled = !!PR_GetEnv("MESSAGEMANAGER_FUZZER_ENABLE_LOGGING");
+    sInitialized = true;
+  }
+
+  return sIsLoggingEnabled;
+}
+
+/* static */
+bool
+MessageManagerFuzzer::IsEnabled()
+{
+  return !!PR_GetEnv("MESSAGEMANAGER_FUZZER_ENABLE") && XRE_IsContentProcess();
+}
+
+/* static */
+void
+MessageManagerFuzzer::TryMutate(JSContext* aCx,
+                                const nsAString& aMessageName,
+                                ipc::StructuredCloneData* aData,
+                                const JS::Value& aTransfer)
+{
+  if (!IsEnabled()) {
+    return;
+  }
+
+  if (IsMessageNameBlacklisted(aMessageName)) {
+    MSGMGR_FUZZER_LOG("Blacklisted message: %s",
+                      NS_ConvertUTF16toUTF8(aMessageName).get());
+    return;
+  }
+
+  Mutate(aCx, aMessageName, aData, aTransfer);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/tools/fuzzing/messagemanager/MessageManagerFuzzer.h
@@ -0,0 +1,67 @@
+/* -*- 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_dom_MessageManagerFuzzer_h__
+#define mozilla_dom_MessageManagerFuzzer_h__
+
+#include "jspubtd.h"
+#include "nsAString.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace dom {
+
+namespace ipc {
+class StructuredCloneData;
+}
+
+/*
+Exposed environment variables:
+MESSAGEMANAGER_FUZZER_ENABLE=1
+MESSAGEMANAGER_FUZZER_ENABLE_LOGGING=1 (optional)
+MESSAGEMANAGER_FUZZER_MUTATION_PROBABILITY=2 (optional)
+MESSAGEMANAGER_FUZZER_STRINGSFILE=<path> (optional)
+MESSAGEMANAGER_FUZZER_BLACKLIST=<path> (optional)
+*/
+
+class MessageManagerFuzzer
+{
+public:
+  static void TryMutate(
+    JSContext* aCx,
+    const nsAString& aMessageName,
+    ipc::StructuredCloneData* aData,
+    const JS::Value& aTransfer);
+
+private:
+  static void ReadFile(const char* path, nsTArray<nsCString> &aArray);
+  static nsCString GetFuzzValueFromFile();
+  static bool IsMessageNameBlacklisted(const nsAString& aMessageName);
+  static bool Mutate(
+    JSContext* aCx,
+    const nsAString& aMessageName,
+    ipc::StructuredCloneData* aData,
+  const JS::Value& aTransfer);
+  static void Mutate(JSContext* aCx, JS::Rooted<JS::Value>& aMutation);
+  static void MutateObject(
+    JSContext* aCx,
+    JS::HandleValue aValue,
+    unsigned short int aRecursionCounter);
+  static bool MutateValue(
+    JSContext* aCx,
+    JS::HandleValue aValue,
+    JS::MutableHandleValue aOutMutationValue,
+    unsigned short int aRecursionCounter);
+  static unsigned int DefaultMutationProbability();
+  static nsAutoString ReadJSON(JSContext* aCx, const JS::Value& aJSON);
+  static bool IsEnabled();
+  static bool IsLoggingEnabled();
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/tools/fuzzing/messagemanager/moz.build
@@ -0,0 +1,15 @@
+# -*- 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 += [
+  'MessageManagerFuzzer.cpp'
+]
+
+EXPORTS += [
+  'MessageManagerFuzzer.h'
+]
+
+FINAL_LIBRARY = 'xul'
--- a/tools/fuzzing/moz.build
+++ b/tools/fuzzing/moz.build
@@ -1,14 +1,16 @@
 # -*- 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/.
 
 DIRS += [
+  'common',
   'interface',
+  'messagemanager',
 ]
 
 if CONFIG['LIBFUZZER']:
   DIRS += [
     'libfuzzer',
   ]