Bug 1451859 - Part 1: Implement a fuzzer for IPC handlers using libFuzzer. r=jld, r=posidron
authorAlex Gaynor <agaynor@mozilla.com>
Fri, 23 Mar 2018 16:18:42 -0400
changeset 476033 833710acd427eac8599d31cef71300fcf8d82929
parent 476032 4da0d6998dd61f90fda3353c42df13f9cc834238
child 476034 1f3916c7d4a2dbd402c7e81a941873a6e8edf360
push id9374
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:43:20 +0000
treeherdermozilla-beta@160e085dfb0b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjld, posidron
bugs1451859
milestone62.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 1451859 - Part 1: Implement a fuzzer for IPC handlers using libFuzzer. r=jld, r=posidron
dom/ipc/ContentParent.h
dom/ipc/fuzztest/content_parent_ipc_libfuzz.cpp
dom/ipc/fuzztest/moz.build
dom/ipc/moz.build
ipc/glue/MessageChannel.h
tools/fuzzing/ipc/ProtocolFuzzer.cpp
tools/fuzzing/ipc/ProtocolFuzzer.h
tools/fuzzing/ipc/moz.build
tools/fuzzing/moz.build
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -73,16 +73,19 @@ class PrintingParent;
 }
 
 namespace ipc {
 class CrashReporterHost;
 class OptionalURIParams;
 class PFileDescriptorSetParent;
 class URIParams;
 class TestShellParent;
+#ifdef FUZZING
+class ProtocolFuzzerHelper;
+#endif
 } // namespace ipc
 
 namespace jsipc {
 class PJavaScriptParent;
 } // namespace jsipc
 
 namespace layers {
 struct TextureFactoryIdentifier;
@@ -118,16 +121,19 @@ class ContentParent final : public PCont
   typedef mozilla::ipc::PFileDescriptorSetParent PFileDescriptorSetParent;
   typedef mozilla::ipc::TestShellParent TestShellParent;
   typedef mozilla::ipc::URIParams URIParams;
   typedef mozilla::ipc::PrincipalInfo PrincipalInfo;
   typedef mozilla::dom::ClonedMessageData ClonedMessageData;
 
   friend class ContentProcessHost;
   friend class mozilla::PreallocatedProcessManagerImpl;
+#ifdef FUZZING
+  friend class mozilla::ipc::ProtocolFuzzerHelper;
+#endif
 
 public:
 
   virtual bool IsContentParent() const override { return true; }
 
   /**
    * Create a subprocess suitable for use later as a content process.
    */
new file mode 100644
--- /dev/null
+++ b/dom/ipc/fuzztest/content_parent_ipc_libfuzz.cpp
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=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 "gtest/gtest.h"
+
+#include "FuzzingInterface.h"
+#include "ProtocolFuzzer.h"
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/devtools/PHeapSnapshotTempFileHelper.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/gfx/gfxVars.h"
+
+int
+FuzzingInitContentParentIPC(int* argc, char*** argv)
+{
+  return 0;
+}
+
+static int
+RunContentParentIPCFuzzing(const uint8_t* data, size_t size)
+{
+  static mozilla::dom::ContentParent* p =
+    mozilla::ipc::ProtocolFuzzerHelper::CreateContentParent(
+      nullptr, NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE));
+
+  static nsTArray<nsCString> ignored = mozilla::ipc::LoadIPCMessageBlacklist(
+    getenv("MOZ_IPC_MESSAGE_FUZZ_BLACKLIST"));
+
+  mozilla::ipc::FuzzProtocol(p, data, size, ignored);
+
+  return 0;
+}
+
+MOZ_FUZZING_INTERFACE_RAW(FuzzingInitContentParentIPC,
+                          RunContentParentIPCFuzzing,
+                          ContentParentIPC);
new file mode 100644
--- /dev/null
+++ b/dom/ipc/fuzztest/moz.build
@@ -0,0 +1,22 @@
+# -*- Mode: python; 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/.
+
+Library('FuzzingContentParentIPC')
+
+LOCAL_INCLUDES += [
+    '/dom/base',
+]
+
+SOURCES += [
+    'content_parent_ipc_libfuzz.cpp'
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul-gtest'
+
+# Add libFuzzer configuration directives
+include('/tools/fuzzing/libfuzzer-config.mozbuild')
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -173,8 +173,13 @@ MOCHITEST_MANIFESTS += ['tests/mochitest
 
 CXXFLAGS += CONFIG['TK_CFLAGS']
 
 if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
     CXXFLAGS += ['-Wno-error=shadow']
 
 if CONFIG['NIGHTLY_BUILD']:
     DEFINES['ASYNC_CONTENTPROC_LAUNCH'] = True
+
+if CONFIG['FUZZING'] and CONFIG['FUZZING_INTERFACES']:
+    TEST_DIRS += [
+        'fuzztest'
+    ]
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -89,16 +89,19 @@ enum ChannelState {
 };
 
 class AutoEnterTransaction;
 
 class MessageChannel : HasResultCodes, MessageLoop::DestructionObserver
 {
     friend class ProcessLink;
     friend class ThreadLink;
+#ifdef FUZZING
+    friend class ProtocolFuzzerHelper;
+#endif
 
     class CxxStackFrame;
     class InterruptFrame;
 
     typedef mozilla::Monitor Monitor;
 
     // We could templatize the actor type but it would unnecessarily
     // expand the code size. Using the actor address as the
new file mode 100644
--- /dev/null
+++ b/tools/fuzzing/ipc/ProtocolFuzzer.cpp
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=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 "ProtocolFuzzer.h"
+
+namespace mozilla {
+namespace ipc {
+
+nsTArray<nsCString>
+LoadIPCMessageBlacklist(const char* aPath)
+{
+  nsTArray<nsCString> blacklist;
+  if (aPath) {
+    nsresult result = Faulty::ReadFile(aPath, blacklist);
+    MOZ_RELEASE_ASSERT(result == NS_OK);
+  }
+  return blacklist;
+}
+
+
+mozilla::dom::ContentParent*
+ProtocolFuzzerHelper::CreateContentParent(mozilla::dom::ContentParent* aOpener,
+                                          const nsAString& aRemoteType)
+{
+  auto* cp = new mozilla::dom::ContentParent(aOpener, aRemoteType);
+  // TODO: this duplicates MessageChannel::Open
+  cp->GetIPCChannel()->mWorkerThread = GetCurrentVirtualThread();
+  cp->GetIPCChannel()->mMonitor = new RefCountedMonitor();
+  return cp;
+}
+}
+}
new file mode 100644
--- /dev/null
+++ b/tools/fuzzing/ipc/ProtocolFuzzer.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=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_ProtocolFuzzer_h
+#define mozilla_ipc_ProtocolFuzzer_h
+
+#include "chrome/common/ipc_message.h"
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/dom/ContentParent.h"
+
+namespace mozilla {
+namespace ipc {
+
+template<typename T>
+void
+FuzzProtocol(T* aProtocol,
+             const uint8_t* aData,
+             size_t aSize,
+             const nsTArray<nsCString>& aIgnoredMessageTypes)
+{
+  while (true) {
+    uint32_t msg_size =
+      IPC::Message::MessageSize(reinterpret_cast<const char*>(aData),
+                                reinterpret_cast<const char*>(aData) + aSize);
+    if (msg_size == 0 || msg_size > aSize) {
+      break;
+    }
+    IPC::Message m(reinterpret_cast<const char*>(aData), msg_size);
+    aSize -= msg_size;
+    aData += msg_size;
+
+    // We ignore certain message types
+    if (aIgnoredMessageTypes.Contains(m.name())) {
+      continue;
+    }
+    // TODO: attach |m.header().num_fds| file descriptors to |m|. MVP can be
+    // empty files, next implementation maybe read a length header from |data|
+    // and then that many bytes. Probably need similar handling for shmem,
+    // other types?
+
+    if (m.is_sync()) {
+      nsAutoPtr<IPC::Message> reply;
+      aProtocol->OnMessageReceived(m, *getter_Transfers(reply));
+    } else {
+      aProtocol->OnMessageReceived(m);
+    }
+  }
+}
+
+nsTArray<nsCString> LoadIPCMessageBlacklist(const char* aPath);
+
+class ProtocolFuzzerHelper
+{
+public:
+  static mozilla::dom::ContentParent* CreateContentParent(
+    mozilla::dom::ContentParent* aOpener,
+    const nsAString& aRemoteType);
+};
+}
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/tools/fuzzing/ipc/moz.build
@@ -0,0 +1,27 @@
+# -*- 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/.
+
+Library('fuzzer-ipc-protocol')
+
+LOCAL_INCLUDES += [
+    '/dom/base',
+    '/dom/ipc',
+]
+
+SOURCES += [
+    'ProtocolFuzzer.cpp',
+]
+
+EXPORTS += [
+    'ProtocolFuzzer.h',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = "xul"
+
+# Add libFuzzer configuration directives
+include('/tools/fuzzing/libfuzzer-config.mozbuild')
--- a/tools/fuzzing/moz.build
+++ b/tools/fuzzing/moz.build
@@ -10,14 +10,15 @@ DIRS += [
 ]
 
 if not CONFIG['JS_STANDALONE']:
   DIRS += [
     'common',
     'faulty',
     'messagemanager',
     'shmem',
+    'ipc',
   ]
 
 if CONFIG['LIBFUZZER']:
   DIRS += [
     'libfuzzer',
   ]