merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 21 Feb 2017 14:15:35 +0100
changeset 372984 b69d4316561aa125612509a202922455d2de03e5
parent 372969 e25127db470ca6f01bd9650b347a369e18e55a45 (current diff)
parent 372983 e3c55cc9ffa36a578224bbcdd0c4b2c8a886908a (diff)
child 372985 b0773801c3f436bc0331b178a8c47cbe2572049a
child 373030 c75a80093d4a2567a2da7673fa7d6214b6cc8215
child 373049 5896d14617c1f2c63428f5af91dc8fd0df2f275c
child 373090 0be3bfee0e9ea84ad2dcfcdaca010d97cd50c4f1
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone54.0a1
merge mozilla-inbound to mozilla-central a=merge
dom/base/test/test_bug793311.html
dom/ipc/ContentParent.cpp
toolkit/components/telemetry/Histograms.json
--- a/browser/components/sessionstore/SessionHistory.jsm
+++ b/browser/components/sessionstore/SessionHistory.jsm
@@ -405,24 +405,16 @@ var SessionHistoryInternal = {
         docIdentMap[entry.docIdentifier] = matchingEntry;
       }
       else {
         shEntry.adoptBFCacheEntry(matchingEntry.shEntry);
         childDocIdents = matchingEntry.childDocIdents;
       }
     }
 
-    // The field entry.owner_b64 got renamed to entry.triggeringPricipal_b64 in
-    // Bug 1286472. To remain backward compatible we still have to support that
-    // field for a few cycles before we can remove it within Bug 1289785.
-    if (entry.owner_b64) {
-      entry.triggeringPricipal_b64 = entry.owner_b64;
-      delete entry.owner_b64;
-    }
-
     // Before introducing the concept of principalToInherit we only had
     // a triggeringPrincipal within every entry which basically is the
     // equivalent of the new principalToInherit. To avoid compatibility
     // issues, we first check if the entry has entries for
     // triggeringPrincipal_base64 and principalToInherit_base64. If not
     // we fall back to using the principalToInherit (which is stored
     // as triggeringPrincipal_b64) as the triggeringPrincipal and
     // the principalToInherit.
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -728,16 +728,17 @@
 @BINPATH@/wow_helper.exe
 #endif
 #endif
 #endif
 
 #if defined(MOZ_SANDBOX)
 #if defined(XP_LINUX)
 @BINPATH@/@DLL_PREFIX@mozsandbox@DLL_SUFFIX@
+@RESPATH@/components/sandbox.xpt
 #endif
 #endif
 
 ; for Solaris SPARC
 #ifdef SOLARIS
 bin/libfreebl_32fpu_3.so
 bin/libfreebl_32int_3.so
 bin/libfreebl_32int64_3.so
--- a/dom/media/platforms/wmf/DXVA2Manager.cpp
+++ b/dom/media/platforms/wmf/DXVA2Manager.cpp
@@ -620,21 +620,19 @@ private:
 
   bool CanCreateDecoder(const D3D11_VIDEO_DECODER_DESC& aDesc,
                         const float aFramerate) const;
 
   already_AddRefed<ID3D11VideoDecoder>
   CreateDecoder(const D3D11_VIDEO_DECODER_DESC& aDesc) const;
 
   RefPtr<ID3D11Device> mDevice;
-  RefPtr<ID3D11DeviceContext> mContext;
   RefPtr<IMFDXGIDeviceManager> mDXGIDeviceManager;
   RefPtr<MFTDecoder> mTransform;
   RefPtr<D3D11RecycleAllocator> mTextureClientAllocator;
-  RefPtr<ID3D11Texture2D> mSyncSurface;
   RefPtr<ID3D11VideoDecoder> mDecoder;
   GUID mDecoderGUID;
   uint32_t mWidth = 0;
   uint32_t mHeight = 0;
   UINT mDeviceManagerToken = 0;
 };
 
 bool
@@ -674,23 +672,16 @@ D3D11DXVA2Manager::Init(layers::KnowsCom
   }
 
   mDevice = gfx::DeviceManagerDx::Get()->CreateDecoderDevice();
   if (!mDevice) {
     aFailureReason.AssignLiteral("Failed to create D3D11 device for decoder");
     return E_FAIL;
   }
 
-  mDevice->GetImmediateContext(getter_AddRefs(mContext));
-  if (!mContext) {
-    aFailureReason.AssignLiteral(
-      "Failed to get immediate context for d3d11 device");
-    return E_FAIL;
-  }
-
   hr = wmf::MFCreateDXGIDeviceManager(&mDeviceManagerToken,
                                       getter_AddRefs(mDXGIDeviceManager));
   if (!SUCCEEDED(hr)) {
     aFailureReason =
       nsPrintfCString("MFCreateDXGIDeviceManager failed with code %X", hr);
     return hr;
   }
 
@@ -786,32 +777,16 @@ D3D11DXVA2Manager::Init(layers::KnowsCom
     for (const auto& model : sAMDPreUVD4) {
       if (adapterDesc.DeviceId == model) {
         mIsAMDPreUVD4 = true;
         break;
       }
     }
   }
 
-  D3D11_TEXTURE2D_DESC desc;
-  desc.Width = kSyncSurfaceSize;
-  desc.Height = kSyncSurfaceSize;
-  desc.MipLevels = 1;
-  desc.ArraySize = 1;
-  desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
-  desc.SampleDesc.Count = 1;
-  desc.SampleDesc.Quality = 0;
-  desc.Usage = D3D11_USAGE_STAGING;
-  desc.BindFlags = 0;
-  desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
-  desc.MiscFlags = 0;
-
-  hr = mDevice->CreateTexture2D(&desc, NULL, getter_AddRefs(mSyncSurface));
-  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
-
   if (layers::ImageBridgeChild::GetSingleton()) {
     // There's no proper KnowsCompositor for ImageBridge currently (and it
     // implements the interface), so just use that if it's available.
     mTextureClientAllocator = new D3D11RecycleAllocator(
       layers::ImageBridgeChild::GetSingleton().get(), mDevice);
   } else {
     mTextureClientAllocator =
       new D3D11RecycleAllocator(aKnowsCompositor, mDevice);
@@ -860,36 +835,33 @@ D3D11DXVA2Manager::CopyToImage(IMFSample
   RefPtr<D3D11ShareHandleImage> image =
     new D3D11ShareHandleImage(gfx::IntSize(mWidth, mHeight), aRegion);
   bool ok = image->AllocateTexture(mTextureClientAllocator, mDevice);
   NS_ENSURE_TRUE(ok, E_FAIL);
 
   HRESULT hr = mTransform->Input(aVideoSample);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
+  RefPtr<IDXGIKeyedMutex> mutex;
   RefPtr<IMFSample> sample;
   RefPtr<ID3D11Texture2D> texture = image->GetTexture();
+
+  texture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
+
+  hr = mutex->AcquireSync(0, 2000);
+
+  if (hr != S_OK) {
+    NS_WARNING("Acquire sync didn't manage to return within 2 seconds.");
+  }
+
   hr = CreateOutputSample(sample, texture);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   hr = mTransform->Output(&sample);
-
-  RefPtr<ID3D11DeviceContext> ctx;
-  mDevice->GetImmediateContext(getter_AddRefs(ctx));
-
-  // Copy a small rect into our sync surface, and then map it
-  // to block until decoding/color conversion completes.
-  D3D11_BOX rect = { 0, 0, 0, kSyncSurfaceSize, kSyncSurfaceSize, 1 };
-  ctx->CopySubresourceRegion(mSyncSurface, 0, 0, 0, 0, texture, 0, &rect);
-
-  D3D11_MAPPED_SUBRESOURCE mapped;
-  hr = ctx->Map(mSyncSurface, 0, D3D11_MAP_READ, 0, &mapped);
-  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
-
-  ctx->Unmap(mSyncSurface, 0);
+  mutex->ReleaseSync(0);
 
   image.forget(aOutImage);
 
   return S_OK;
 }
 
 HRESULT ConfigureOutput(IMFMediaType* aOutput, void* aData)
 {
--- a/gfx/layers/D3D11ShareHandleImage.cpp
+++ b/gfx/layers/D3D11ShareHandleImage.cpp
@@ -58,17 +58,17 @@ D3D11ShareHandleImage::GetTextureClient(
   return mTextureClient;
 }
 
 already_AddRefed<gfx::SourceSurface>
 D3D11ShareHandleImage::GetAsSourceSurface()
 {
   RefPtr<ID3D11Texture2D> texture = GetTexture();
   if (!texture) {
-    NS_WARNING("Cannot readback from shared texture because no texture is available.");
+    gfxWarning() << "Cannot readback from shared texture because no texture is available.";
     return nullptr;
   }
 
   RefPtr<ID3D11Device> device;
   texture->GetDevice(getter_AddRefs(device));
 
   D3D11_TEXTURE2D_DESC desc;
   texture->GetDesc(&desc);
@@ -91,17 +91,29 @@ D3D11ShareHandleImage::GetAsSourceSurfac
   }
 
   RefPtr<ID3D11DeviceContext> context;
   device->GetImmediateContext(getter_AddRefs(context));
   if (!context) {
     return nullptr;
   }
 
+  RefPtr<IDXGIKeyedMutex> mutex;
+  hr = texture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
+
+  if (SUCCEEDED(hr) && mutex) {
+    hr = mutex->AcquireSync(0, 2000);
+    if (hr != S_OK) {
+      NS_WARNING("Acquire sync didn't manage to return within 2 seconds.");
+    }
+  }
   context->CopyResource(softTexture, texture);
+  if (SUCCEEDED(hr) && mutex) {
+    mutex->ReleaseSync(0);
+  }
 
   RefPtr<gfx::DataSourceSurface> surface =
     gfx::Factory::CreateDataSourceSurface(mSize, gfx::SurfaceFormat::B8G8R8A8);
   if (NS_WARN_IF(!surface)) {
     return nullptr;
   }
 
   gfx::DataSourceSurface::MappedSurface mappedSurface;
@@ -149,15 +161,15 @@ already_AddRefed<TextureClient>
 D3D11RecycleAllocator::CreateOrRecycleClient(gfx::SurfaceFormat aFormat,
                                              const gfx::IntSize& aSize)
 {
   RefPtr<TextureClient> textureClient =
     CreateOrRecycle(aFormat,
                     aSize,
                     BackendSelector::Content,
                     layers::TextureFlags::DEFAULT,
-                    TextureAllocationFlags::ALLOC_MANUAL_SYNCHRONIZATION);
+                    TextureAllocationFlags::ALLOC_DEFAULT);
   return textureClient.forget();
 }
 
 
 } // namespace layers
 } // namespace mozilla
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -46,16 +46,20 @@
 
 #if defined(MOZ_SANDBOX)
 #include "mozilla/Preferences.h"
 #include "mozilla/sandboxing/sandboxLogging.h"
 #include "nsDirectoryServiceUtils.h"
 #endif
 #endif
 
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+#include "mozilla/SandboxReporter.h"
+#endif
+
 #include "nsTArray.h"
 #include "nsClassHashtable.h"
 #include "nsHashKeys.h"
 #include "nsNativeCharsetUtils.h"
 #include "nscore.h" // for NS_FREE_PERMANENT_DATA
 
 using mozilla::MonitorAutoLock;
 using mozilla::ipc::GeckoChildProcessHost;
@@ -907,16 +911,25 @@ GeckoChildProcessHost::PerformAsyncLaunc
     // "false" == crash reporting disabled
     childArgv.push_back("false");
   }
 #  elif defined(MOZ_WIDGET_COCOA)
   childArgv.push_back(CrashReporter::GetChildNotificationPipe());
 #  endif  // OS_LINUX
 #endif
 
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+  {
+    int srcFd, dstFd;
+    SandboxReporter::Singleton()
+      ->GetClientFileDescriptorMapping(&srcFd, &dstFd);
+    mFileMap.push_back(std::make_pair(srcFd, dstFd));
+  }
+#endif
+
 #ifdef MOZ_WIDGET_COCOA
   // Add a mach port to the command line so the child can communicate its
   // 'task_t' back to the parent.
   //
   // Put a random number into the channel name, so that a compromised renderer
   // can't pretend being the child that's forked off.
   std::string mach_connection_name = StringPrintf("org.mozilla.machname.%d",
                                                   base::RandInt(0, std::numeric_limits<int>::max()));
--- a/mobile/android/components/SessionStore.js
+++ b/mobile/android/components/SessionStore.js
@@ -1407,24 +1407,16 @@ SessionStore.prototype = {
         matchingEntry = {shEntry: shEntry, childDocIdents: childDocIdents};
         aDocIdentMap[aEntry.docIdentifier] = matchingEntry;
       } else {
         shEntry.adoptBFCacheEntry(matchingEntry.shEntry);
         childDocIdents = matchingEntry.childDocIdents;
       }
     }
 
-    // The field aEntry.owner_b64 got renamed to aEntry.triggeringPricipal_b64 in
-    // Bug 1286472. To remain backward compatible we still have to support that
-    // field for a few cycles before we can remove it within Bug 1289785.
-    if (aEntry.owner_b64) {
-      aEntry.triggeringPricipal_b64 = aEntry.owner_b64;
-      delete aEntry.owner_b64;
-    }
-
     // Before introducing the concept of principalToInherit we only had
     // a triggeringPrincipal within every entry which basically is the
     // equivalent of the new principalToInherit. To avoid compatibility
     // issues, we first check if the entry has entries for
     // triggeringPrincipal_base64 and principalToInherit_base64. If not
     // we fall back to using the principalToInherit (which is stored
     // as triggeringPrincipal_b64) as the triggeringPrincipal and
     // the principalToInherit.
--- a/security/sandbox/linux/Sandbox.cpp
+++ b/security/sandbox/linux/Sandbox.cpp
@@ -8,16 +8,17 @@
 
 #include "LinuxCapabilities.h"
 #include "LinuxSched.h"
 #include "SandboxBrokerClient.h"
 #include "SandboxChroot.h"
 #include "SandboxFilter.h"
 #include "SandboxInternal.h"
 #include "SandboxLogging.h"
+#include "SandboxReporterClient.h"
 #include "SandboxUtil.h"
 
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/futex.h>
 #include <pthread.h>
 #include <signal.h>
@@ -68,26 +69,29 @@ MOZ_IMPORT_API void
 } // extern "C"
 #endif // MOZ_ASAN
 
 // Signal number used to enable seccomp on each thread.
 int gSeccompTsyncBroadcastSignum = 0;
 
 namespace mozilla {
 
+static bool gSandboxCrashOnError = false;
+
 // This is initialized by SandboxSetCrashFunc().
 SandboxCrashFunc gSandboxCrashFunc;
 
 #ifdef MOZ_GMP_SANDBOX
 // For media plugins, we can start the sandbox before we dlopen the
 // module, so we have to pre-open the file and simulate the sandboxed
 // open().
 static SandboxOpenedFile gMediaPluginFile;
 #endif
 
+static Maybe<SandboxReporterClient> gSandboxReporterClient;
 static UniquePtr<SandboxChroot> gChrootHelper;
 static void (*gChromiumSigSysHandler)(int, siginfo_t*, void*);
 
 // Test whether a ucontext, interpreted as the state after a syscall,
 // indicates the given error.  See also sandbox::Syscall::PutValueInUcontext.
 static bool
 ContextIsError(const ucontext_t *aContext, int aError)
 {
@@ -130,38 +134,34 @@ SigSysHandler(int nr, siginfo_t *info, v
   // which will overwrite one or more registers with the return value.
   ucontext_t savedCtx = *ctx;
 
   gChromiumSigSysHandler(nr, info, ctx);
   if (!ContextIsError(ctx, ENOSYS)) {
     return;
   }
 
-  pid_t pid = getpid();
-  unsigned long syscall_nr = SECCOMP_SYSCALL(&savedCtx);
-  unsigned long args[6];
-  args[0] = SECCOMP_PARM1(&savedCtx);
-  args[1] = SECCOMP_PARM2(&savedCtx);
-  args[2] = SECCOMP_PARM3(&savedCtx);
-  args[3] = SECCOMP_PARM4(&savedCtx);
-  args[4] = SECCOMP_PARM5(&savedCtx);
-  args[5] = SECCOMP_PARM6(&savedCtx);
+  SandboxReport report = gSandboxReporterClient->MakeReportAndSend(&savedCtx);
 
   // TODO, someday when this is enabled on MIPS: include the two extra
   // args in the error message.
-  SANDBOX_LOG_ERROR("seccomp sandbox violation: pid %d, syscall %d,"
-                    " args %d %d %d %d %d %d.  Killing process.",
-                    pid, syscall_nr,
-                    args[0], args[1], args[2], args[3], args[4], args[5]);
+  SANDBOX_LOG_ERROR("seccomp sandbox violation: pid %d, tid %d, syscall %d,"
+                    " args %d %d %d %d %d %d.%s",
+                    report.mPid, report.mTid, report.mSyscall,
+                    report.mArgs[0], report.mArgs[1], report.mArgs[2],
+                    report.mArgs[3], report.mArgs[4], report.mArgs[5],
+                    gSandboxCrashOnError ? "  Killing process." : "");
 
-  // Bug 1017393: record syscall number somewhere useful.
-  info->si_addr = reinterpret_cast<void*>(syscall_nr);
+  if (gSandboxCrashOnError) {
+    // Bug 1017393: record syscall number somewhere useful.
+    info->si_addr = reinterpret_cast<void*>(report.mSyscall);
 
-  gSandboxCrashFunc(nr, info, &savedCtx);
-  _exit(127);
+    gSandboxCrashFunc(nr, info, &savedCtx);
+    _exit(127);
+  }
 }
 
 /**
  * This function installs the SIGSYS handler.  This is slightly
  * complicated because we want to use Chromium's handler to dispatch
  * to specific trap handlers defined in the policy, but we also need
  * the full original signal context to give to Breakpad for crash
  * dumps.  So we install Chromium's handler first, then retrieve its
@@ -454,16 +454,17 @@ ApplySandboxWithTSync(sock_fprog* aFilte
   }
 }
 
 // Common code for sandbox startup.
 static void
 SetCurrentProcessSandbox(UniquePtr<sandbox::bpf_dsl::Policy> aPolicy)
 {
   MOZ_ASSERT(gSandboxCrashFunc);
+  MOZ_RELEASE_ASSERT(gSandboxReporterClient.isSome());
 
   // Note: PolicyCompiler borrows the policy and registry for its
   // lifetime, but does not take ownership of them.
   sandbox::bpf_dsl::PolicyCompiler compiler(aPolicy.get(),
                                             sandbox::Trap::Registry());
   sandbox::CodeGen::Program program = compiler.Compile();
   if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
     sandbox::bpf_dsl::DumpBPF::PrintProgram(program);
@@ -510,16 +511,31 @@ void
 SandboxEarlyInit(GeckoProcessType aType)
 {
   const SandboxInfo info = SandboxInfo::Get();
   if (info.Test(SandboxInfo::kUnexpectedThreads)) {
     return;
   }
   MOZ_RELEASE_ASSERT(IsSingleThreaded());
 
+  // Set gSandboxCrashOnError if appropriate.  This doesn't need to
+  // happen this early, but for now it's here so that I don't need to
+  // add NSPR dependencies for PR_GetEnv.
+  //
+  // This also means that users with "unexpected threads" setups won't
+  // crash even on nightly.
+#ifdef NIGHTLY_BUILD
+  gSandboxCrashOnError = true;
+#endif
+  if (const char* envVar = getenv("MOZ_SANDBOX_CRASH_ON_ERROR")) {
+    if (envVar[0]) {
+      gSandboxCrashOnError = envVar[0] != '0';
+    }
+  }
+
   // Which kinds of resource isolation (of those that need to be set
   // up at this point) can be used by this process?
   bool canChroot = false;
   bool canUnshareNet = false;
   bool canUnshareIPC = false;
 
   switch (aType) {
   case GeckoProcessType_Default:
@@ -638,16 +654,18 @@ SetContentProcessSandbox(int aBrokerFd, 
 {
   if (!SandboxInfo::Get().Test(SandboxInfo::kEnabledForContent)) {
     if (aBrokerFd >= 0) {
       close(aBrokerFd);
     }
     return false;
   }
 
+  gSandboxReporterClient.emplace(SandboxReport::ProcType::CONTENT);
+
   // This needs to live until the process exits.
   static Maybe<SandboxBrokerClient> sBroker;
   if (aBrokerFd >= 0) {
     sBroker.emplace(aBrokerFd);
   }
 
   SetCurrentProcessSandbox(GetContentSandboxPolicy(sBroker.ptrOr(nullptr),
                                                    aSyscallWhitelist));
@@ -669,16 +687,18 @@ SetContentProcessSandbox(int aBrokerFd, 
 */
 void
 SetMediaPluginSandbox(const char *aFilePath)
 {
   if (!SandboxInfo::Get().Test(SandboxInfo::kEnabledForMedia)) {
     return;
   }
 
+  gSandboxReporterClient.emplace(SandboxReport::ProcType::MEDIA_PLUGIN);
+
   MOZ_ASSERT(!gMediaPluginFile.mPath);
   if (aFilePath) {
     gMediaPluginFile.mPath = strdup(aFilePath);
     gMediaPluginFile.mFd = open(aFilePath, O_RDONLY | O_CLOEXEC);
     if (gMediaPluginFile.mFd == -1) {
       SANDBOX_LOG_ERROR("failed to open plugin file %s: %s",
                         aFilePath, strerror(errno));
       MOZ_CRASH();
--- a/security/sandbox/linux/SandboxFilter.cpp
+++ b/security/sandbox/linux/SandboxFilter.cpp
@@ -164,16 +164,17 @@ public:
 
   virtual ResultExpr EvaluateSyscall(int sysno) const override {
     switch (sysno) {
       // Timekeeping
     case __NR_clock_gettime: {
       Arg<clockid_t> clk_id(0);
       return If(clk_id == CLOCK_MONOTONIC, Allow())
 #ifdef CLOCK_MONOTONIC_COARSE
+        // Used by SandboxReporter, among other things.
         .ElseIf(clk_id == CLOCK_MONOTONIC_COARSE, Allow())
 #endif
         .ElseIf(clk_id == CLOCK_PROCESS_CPUTIME_ID, Allow())
         .ElseIf(clk_id == CLOCK_REALTIME, Allow())
 #ifdef CLOCK_REALTIME_COARSE
         .ElseIf(clk_id == CLOCK_REALTIME_COARSE, Allow())
 #endif
         .ElseIf(clk_id == CLOCK_THREAD_CPUTIME_ID, Allow())
new file mode 100644
--- /dev/null
+++ b/security/sandbox/linux/SandboxReporterClient.cpp
@@ -0,0 +1,89 @@
+/* -*- 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 "SandboxReporterClient.h"
+#include "SandboxLogging.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/PodOperations.h"
+#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+#ifdef ANDROID
+#include "sandbox/linux/system_headers/linux_ucontext.h"
+#else
+#include <ucontext.h>
+#endif
+
+namespace mozilla {
+
+SandboxReporterClient::SandboxReporterClient(SandboxReport::ProcType aProcType,
+					     int aFd)
+  : mProcType(aProcType)
+  , mFd(aFd)
+{
+  // Unfortunately, there isn't a good way to check that the fd is a
+  // socket connected to the right thing without attempting some kind
+  // of in-band handshake.  However, the crash reporter (which also
+  // uses a "magic number" fd) doesn't do any kind of checking either,
+  // so it's probably okay to skip it here.
+}
+
+SandboxReport
+SandboxReporterClient::MakeReport(const void* aContext)
+{
+  SandboxReport report;
+  const auto ctx = static_cast<const ucontext_t*>(aContext);
+
+  // Zero the entire struct; some memory safety analyses care about
+  // sending uninitialized alignment padding to another process.
+  PodZero(&report);
+
+  clock_gettime(CLOCK_MONOTONIC_COARSE, &report.mTime);
+  report.mPid = getpid();
+  report.mTid = syscall(__NR_gettid);
+  report.mProcType = mProcType;
+  report.mSyscall = SECCOMP_SYSCALL(ctx);
+  report.mArgs[0] = SECCOMP_PARM1(ctx);
+  report.mArgs[1] = SECCOMP_PARM2(ctx);
+  report.mArgs[2] = SECCOMP_PARM3(ctx);
+  report.mArgs[3] = SECCOMP_PARM4(ctx);
+  report.mArgs[4] = SECCOMP_PARM5(ctx);
+  report.mArgs[5] = SECCOMP_PARM6(ctx);
+  // Named Return Value Optimization allows the compiler to optimize
+  // out the copy here (and the one in MakeReportAndSend).
+  return report;
+}
+
+void
+SandboxReporterClient::SendReport(const SandboxReport& aReport)
+{
+  // The "common" seccomp-bpf policy allows sendmsg but not send(to),
+  // so just use sendmsg even though send would suffice for this.
+  struct iovec iov;
+  struct msghdr msg;
+
+  iov.iov_base = const_cast<void*>(static_cast<const void*>(&aReport));
+  iov.iov_len = sizeof(SandboxReport);
+  PodZero(&msg);
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+
+  const auto sent = sendmsg(mFd, &msg, MSG_NOSIGNAL);
+
+  if (sent != sizeof(SandboxReport)) {
+    MOZ_DIAGNOSTIC_ASSERT(sent == -1);
+    SANDBOX_LOG_ERROR("Failed to report rejected syscall: %s",
+		      strerror(errno));
+  }
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/security/sandbox/linux/SandboxReporterClient.h
@@ -0,0 +1,43 @@
+/* -*- 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_SandboxReporterClient_h
+#define mozilla_SandboxReporterClient_h
+
+#include "reporter/SandboxReporterCommon.h"
+
+namespace mozilla {
+
+// This class is instantiated in child processes in Sandbox.cpp to
+// send reports from the SIGSYS handler to the SandboxReporter
+// instance in the parent.
+class SandboxReporterClient {
+public:
+  // Note: this does not take ownership of the file descriptor; if
+  // it's not kSandboxReporterFileDesc (e.g., for unit testing), the
+  // caller will need to close it to avoid leaks.
+  explicit SandboxReporterClient(SandboxReport::ProcType aProcType,
+                                 int aFd = kSandboxReporterFileDesc);
+
+  // Constructs a report from a signal context (the ucontext_t* passed
+  // as void* to an sa_sigaction handler); uses the caller's pid and tid.
+  SandboxReport MakeReport(const void* aContext);
+
+  void SendReport(const SandboxReport& aReport);
+
+  SandboxReport MakeReportAndSend(const void* aContext) {
+    SandboxReport report = MakeReport(aContext);
+    SendReport(report);
+    return report;
+  }
+private:
+  SandboxReport::ProcType mProcType;
+  int mFd;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxReporterClient_h
new file mode 100644
--- /dev/null
+++ b/security/sandbox/linux/interfaces/moz.build
@@ -0,0 +1,11 @@
+# -*- Mode: python; python-indent: 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/.
+
+XPIDL_MODULE = 'sandbox'
+
+XPIDL_SOURCES += [
+    'mozISandboxReporter.idl',
+]
new file mode 100644
--- /dev/null
+++ b/security/sandbox/linux/interfaces/mozISandboxReporter.idl
@@ -0,0 +1,65 @@
+/* -*- Mode: IDL; 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 "nsISupports.idl"
+
+// A wrapper for the C++ class SandboxReport, representing one system
+// call that was rejected by policy.
+[scriptable, builtinclass, uuid(ed1e84d3-3346-42e1-b28c-e76a77f549f0)]
+interface mozISandboxReport : nsISupports
+{
+  // The timestamp relative to the time when this property is read.
+  // This is mainly meant for distinguishing recent events that might
+  // be related to an observable failure from older ones that may be
+  // unrelated, not for exact timing.
+  readonly attribute uint64_t msecAgo;
+  readonly attribute int32_t pid;
+  readonly attribute int32_t tid;
+  readonly attribute ACString procType;
+  readonly attribute uint32_t syscall;
+  // Currently numArgs is effectively a constant and indicates the
+  // maximum number of arguments possible on the platform; the actual
+  // system call may use fewer.
+  readonly attribute uint32_t numArgs;
+  // The argument values are presented as strings because JS doesn't
+  // have 64-bit integers and data would be lost on 64-bit platforms
+  // if the XPIDL type uint64_t were used.  The string may be decimal
+  // or hex (with leading "0x").
+  ACString getArg(in uint32_t aIndex);
+};
+
+// A wrapper for SandboxReporter::Snapshot, representing the most
+// recent SandboxReport events.  Index 0 is the first report in the
+// session, and so on; exposing the indices like this lets us see how
+// many reports have been received even though only a limited number
+// of them are stored.
+[scriptable, builtinclass, uuid(6e8ff6e5-05c9-42d3-853d-40523fd86a50)]
+interface mozISandboxReportArray : nsISupports
+{
+  readonly attribute uint64_t begin;
+  readonly attribute uint64_t end;
+  // (aIndex >= begin && aIndex < end) must be true.
+  mozISandboxReport getElement(in uint64_t aIndex);
+};
+
+// A wrapper for the SandboxReporter; use the component/contract IDs
+// below to access the SandboxReporter singleton.  The component
+// constructor will fail if called in a child process.
+[scriptable, builtinclass, uuid(8535bdf7-6d9e-4853-acf9-a146449c4a3b)]
+interface mozISandboxReporter : nsISupports
+{
+  mozISandboxReportArray snapshot();
+};
+
+%{ C++
+
+#define MOZ_SANDBOX_REPORTER_CID \
+{0x5118a6f9, 0x2493, 0x4f97, {0x95, 0x52, 0x62, 0x06, 0x63, 0xe0, 0x3c, 0xb3}}
+
+#define MOZ_SANDBOX_REPORTER_CONTRACTID \
+    "@mozilla.org/sandbox/syscall-reporter;1"
+
+%}
--- a/security/sandbox/linux/moz.build
+++ b/security/sandbox/linux/moz.build
@@ -62,16 +62,17 @@ SOURCES += [
     'Sandbox.cpp',
     'SandboxBrokerClient.cpp',
     'SandboxChroot.cpp',
     'SandboxFilter.cpp',
     'SandboxFilterUtil.cpp',
     'SandboxHooks.cpp',
     'SandboxInfo.cpp',
     'SandboxLogging.cpp',
+    'SandboxReporterClient.cpp',
     'SandboxUtil.cpp',
 ]
 
 # This copy of SafeSPrintf doesn't need to avoid the Chromium logging
 # dependency like the one in libxul does, but this way the behavior is
 # consistent.  See also the comment in SandboxLogging.h.
 SOURCES['../chromium/base/strings/safe_sprintf.cc'].flags += ['-DNDEBUG']
 
@@ -104,13 +105,15 @@ if CONFIG['OS_TARGET'] != 'Android':
     # Needed for clock_gettime with glibc < 2.17:
     OS_LIBS += [
         'rt',
     ]
 
 DIRS += [
     'broker',
     'glue',
+    'interfaces',
+    'reporter',
 ]
 
 TEST_DIRS += [
     'gtest',
 ]
new file mode 100644
--- /dev/null
+++ b/security/sandbox/linux/reporter/SandboxReporter.cpp
@@ -0,0 +1,295 @@
+/* -*- 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 "SandboxReporter.h"
+#include "SandboxLogging.h"
+
+#include <algorithm>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <time.h> // for clockid_t
+
+#include "mozilla/Assertions.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/StaticMutex.h"
+#include "mozilla/PodOperations.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Telemetry.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+// Distinguish architectures for the telemetry key.
+#if defined(__i386__)
+#define SANDBOX_ARCH_NAME "x86"
+#elif defined(__x86_64__)
+#define SANDBOX_ARCH_NAME "amd64"
+#else
+#error "unrecognized architecture"
+#endif
+
+namespace mozilla {
+
+StaticAutoPtr<SandboxReporter> SandboxReporter::sSingleton;
+
+SandboxReporter::SandboxReporter()
+  : mClientFd(-1)
+  , mServerFd(-1)
+  , mMutex("SandboxReporter")
+  , mBuffer(MakeUnique<SandboxReport[]>(kSandboxReporterBufferSize))
+  , mCount(0)
+{
+}
+
+bool
+SandboxReporter::Init()
+{
+  int fds[2];
+
+  if (0 != socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)) {
+    SANDBOX_LOG_ERROR("SandboxReporter: socketpair failed: %s",
+		      strerror(errno));
+    return false;
+  }
+  mClientFd = fds[0];
+  mServerFd = fds[1];
+
+  if (!PlatformThread::Create(0, this, &mThread)) {
+    SANDBOX_LOG_ERROR("SandboxReporter: thread creation failed: %s",
+                      strerror(errno));
+    close(mClientFd);
+    close(mServerFd);
+    mClientFd = mServerFd = -1;
+    return false;
+  }
+
+  return true;
+}
+
+SandboxReporter::~SandboxReporter()
+{
+  if (mServerFd < 0) {
+    return;
+  }
+  shutdown(mServerFd, SHUT_RD);
+  PlatformThread::Join(mThread);
+  close(mServerFd);
+  close(mClientFd);
+}
+
+/* static */ SandboxReporter*
+SandboxReporter::Singleton()
+{
+  static StaticMutex sMutex;
+  StaticMutexAutoLock lock(sMutex);
+
+  if (sSingleton == nullptr) {
+    sSingleton = new SandboxReporter();
+    if (!sSingleton->Init()) {
+      // If socketpair or thread creation failed, trying to continue
+      // with child process creation is unlikely to succeed; crash
+      // instead of trying to handle that case.
+      MOZ_CRASH("SandboxRepoter::Singleton: initialization failed");
+    }
+    // ClearOnShutdown must be called on the main thread and will
+    // destroy the object on the main thread.  That *should* be safe;
+    // the destructor will shut down the reporter's socket reader
+    // thread before freeing anything, IPC should already be shut down
+    // by that point (so it won't race by calling Singleton()), all
+    // non-main XPCOM threads will also be shut down, and currently
+    // the only other user is the main-thread-only Troubleshoot.jsm.
+    NS_DispatchToMainThread(NS_NewRunnableFunction([] {
+      ClearOnShutdown(&sSingleton);
+    }));
+  }
+  return sSingleton.get();
+}
+
+void
+SandboxReporter::GetClientFileDescriptorMapping(int* aSrcFd, int* aDstFd) const
+{
+  MOZ_ASSERT(mClientFd >= 0);
+  *aSrcFd = mClientFd;
+  *aDstFd = kSandboxReporterFileDesc;
+}
+
+// This function is mentioned in Histograms.json; keep that in mind if
+// it's renamed or moved to a different file.
+static void
+SubmitToTelemetry(const SandboxReport& aReport)
+{
+  nsAutoCString key;
+  // The key contains the process type, something that uniquely
+  // identifies the syscall, and in some cases arguments (see below
+  // for details).  Arbitrary formatting choice: fields in the key are
+  // separated by ':', except that (arch, syscall#) pairs are
+  // separated by '/'.
+  //
+  // Examples:
+  // * "content:x86/64"           (bug 1285768)
+  // * "content:x86_64/110"       (bug 1285768)
+  // * "gmp:madvise:8"            (bug 1303813)
+  // * "content:clock_gettime:4"  (bug 1334687)
+
+  switch (aReport.mProcType) {
+  case SandboxReport::ProcType::CONTENT:
+    key.AppendLiteral("content");
+    break;
+  case SandboxReport::ProcType::MEDIA_PLUGIN:
+    key.AppendLiteral("gmp");
+    break;
+  default:
+    MOZ_ASSERT(false);
+  }
+  key.Append(':');
+
+  switch(aReport.mSyscall) {
+    // Syscalls that are filtered by arguments in one or more of the
+    // policies in SandboxFilter.cpp should generally have those
+    // arguments included here, but don't include irrelevant
+    // information that would cause large numbers of distinct keys for
+    // the same issue -- for example, pids or pointers.  When in
+    // doubt, include arguments only if they would typically be
+    // constants (or asm immediates) in the code making the syscall.
+    //
+    // Also, keep in mind that this is opt-out data collection and
+    // privacy is critical.  While it's unlikely that information in
+    // the register values alone could personally identify a user
+    // (see also crash reports, where register contents are public),
+    // and the guidelines in the previous paragraph should rule out
+    // any value that's capable of holding PII, please be careful.
+    //
+    // When making changes here, please consult with a data steward
+    // (https://wiki.mozilla.org/Firefox/Data_Collection) and ask for
+    // a review if you are unsure about anything.
+
+    // This macro includes one argument as a decimal number; it should
+    // be enough for most cases.
+#define ARG_DECIMAL(name, idx)           \
+    case __NR_##name:                    \
+      key.AppendLiteral(#name ":");      \
+      key.AppendInt(aReport.mArgs[idx]); \
+      break
+
+    // This may be more convenient if the argument is a set of bit flags.
+#define ARG_HEX(name, idx)                    \
+    case __NR_##name:                         \
+      key.AppendLiteral(#name ":0x");         \
+      key.AppendInt(aReport.mArgs[idx], 16);  \
+      break
+
+    // clockid_t is annoying: there are a small set of fixed timers,
+    // but it can also encode a pid/tid (or a fd for a hardware clock
+    // device); in this case the value is negative.
+#define ARG_CLOCKID(name, idx)                              \
+    case __NR_##name:                                       \
+      key.AppendLiteral(#name ":");                         \
+      if (static_cast<clockid_t>(aReport.mArgs[idx]) < 0) { \
+        key.AppendLiteral("dynamic");                       \
+      } else {                                              \
+        key.AppendInt(aReport.mArgs[idx]);                  \
+      }                                                     \
+      break
+
+    // The syscalls handled specially:
+
+    ARG_HEX(clone, 0); // flags
+    ARG_DECIMAL(prctl, 0); // option
+    ARG_DECIMAL(madvise, 2); // advice
+    ARG_CLOCKID(clock_gettime, 0); // clk_id
+
+#ifdef __NR_socketcall
+    ARG_DECIMAL(socketcall, 0); // call
+#endif
+#ifdef __NR_ipc
+    ARG_DECIMAL(ipc, 0); // call
+#endif
+
+#undef ARG_DECIMAL
+#undef ARG_HEX
+#undef ARG_CLOCKID
+
+  default:
+    // Otherwise just use the number, with the arch name to disambiguate.
+    key.Append(SANDBOX_ARCH_NAME "/");
+    key.AppendInt(aReport.mSyscall);
+  }
+
+  Telemetry::Accumulate(Telemetry::SANDBOX_REJECTED_SYSCALLS, key);
+}
+
+void
+SandboxReporter::AddOne(const SandboxReport& aReport)
+{
+  SubmitToTelemetry(aReport);
+
+  MutexAutoLock lock(mMutex);
+  mBuffer[mCount % kSandboxReporterBufferSize] = aReport;
+  ++mCount;
+}
+
+void
+SandboxReporter::ThreadMain(void)
+{
+  for (;;) {
+    SandboxReport rep;
+    struct iovec iov;
+    struct msghdr msg;
+
+    iov.iov_base = &rep;
+    iov.iov_len = sizeof(rep);
+    PodZero(&msg);
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+
+    const auto recvd = recvmsg(mServerFd, &msg, 0);
+    if (recvd < 0) {
+      if (errno == EINTR) {
+	continue;
+      }
+      SANDBOX_LOG_ERROR("SandboxReporter: recvmsg: %s", strerror(errno));
+    }
+    if (recvd <= 0) {
+      break;
+    }
+
+    if (static_cast<size_t>(recvd) < sizeof(rep)) {
+      SANDBOX_LOG_ERROR("SandboxReporter: packet too short (%d < %d)",
+			recvd, sizeof(rep));
+      continue;
+    }
+    if (msg.msg_flags & MSG_TRUNC) {
+      SANDBOX_LOG_ERROR("SandboxReporter: packet too long");
+      continue;
+    }
+
+    AddOne(rep);
+  }
+}
+
+SandboxReporter::Snapshot
+SandboxReporter::GetSnapshot()
+{
+  Snapshot snapshot;
+  MutexAutoLock lock(mMutex);
+
+  const uint64_t bufSize = static_cast<uint64_t>(kSandboxReporterBufferSize);
+  const uint64_t start = std::max(mCount, bufSize) - bufSize;
+  snapshot.mOffset = start;
+  snapshot.mReports.Clear();
+  snapshot.mReports.SetCapacity(mCount - start);
+  for (size_t i = start; i < mCount; ++i) {
+    const SandboxReport* rep = &mBuffer[i % kSandboxReporterBufferSize];
+    MOZ_ASSERT(rep->IsValid());
+    snapshot.mReports.AppendElement(*rep);
+  }
+  // Named Return Value Optimization would apply here, but C++11
+  // doesn't require it; so, instead of possibly copying the entire
+  // array contents, invoke the move constructor and copy at most a
+  // few words.
+  return Move(snapshot);
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/security/sandbox/linux/reporter/SandboxReporter.h
@@ -0,0 +1,87 @@
+/* -*- 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_SandboxReporter_h
+#define mozilla_SandboxReporter_h
+
+#include "SandboxReporterCommon.h"
+
+#include "base/platform_thread.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Types.h"
+#include "mozilla/UniquePtr.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+
+// This object collects the SandboxReport messages from all of the
+// child processes, submits them to Telemetry, and maintains a ring
+// buffer of the last kSandboxReporterBufferSize reports.
+class SandboxReporter final
+  : public PlatformThread::Delegate
+{
+public:
+  // For normal use, don't construct this directly; use the
+  // Singleton() method.
+  //
+  // For unit testing, use this constructor followed by the Init
+  // method; the object isn't usable unless Init returns true.
+  explicit SandboxReporter();
+  ~SandboxReporter();
+
+  // See above; this method is not thread-safe.
+  bool Init();
+
+  // Used in GeckoChildProcessHost to connect the child process's
+  // client to this report collector.
+  void GetClientFileDescriptorMapping(int* aSrcFd, int* aDstFd) const;
+
+  // A snapshot of the report ring buffer; element 0 of `mReports` is
+  // the `mOffset`th report to be received, and so on.
+  struct Snapshot {
+    // The buffer has to fit in memory, but the total number of
+    // reports received in the session can increase without bound and
+    // could potentially overflow a uint32_t, so this is 64-bit.
+    // (It's exposed to JS as a 53-bit int, effectively, but that
+    // should also be large enough.)
+    uint64_t mOffset;
+    nsTArray<SandboxReport> mReports;
+  };
+
+  // Read the ring buffer contents; this method is thread-safe.
+  Snapshot GetSnapshot();
+
+  // Gets or creates the singleton report collector.  Crashes if
+  // initialization fails (if a socketpair and/or thread can't be
+  // created, there was almost certainly about to be a crash anyway).
+  // Thread-safe as long as the pointer isn't used during/after XPCOM
+  // shutdown.
+  static SandboxReporter* Singleton();
+private:
+  // These are constant over the life of the object:
+  int mClientFd;
+  int mServerFd;
+  PlatformThreadHandle mThread;
+
+  Mutex mMutex;
+  // These are protected by mMutex:
+  UniquePtr<SandboxReport[]> mBuffer;
+  uint64_t mCount;
+
+  static StaticAutoPtr<SandboxReporter> sSingleton;
+
+  void ThreadMain(void) override;
+  void AddOne(const SandboxReport& aReport);
+};
+
+// This is a constant so the % operations can be optimized.  This is
+// exposed in the header so that unit tests can see it.
+static const size_t kSandboxReporterBufferSize = 32;
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxReporter_h
new file mode 100644
--- /dev/null
+++ b/security/sandbox/linux/reporter/SandboxReporterCommon.h
@@ -0,0 +1,62 @@
+/* -*- 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_SandboxReporterCommon_h
+#define mozilla_SandboxReporterCommon_h
+
+#include "mozilla/IntegerTypeTraits.h"
+#include "mozilla/Types.h"
+
+#include <sys/types.h>
+
+// Note: this is also used in libmozsandbox, so dependencies on
+// symbols from libxul probably won't work.
+
+namespace mozilla {
+static const size_t kSandboxSyscallArguments = 6;
+// fds 0-2: stdio; fd 3: IPC; fd 4: crash reporter.  (The IPC child
+// process launching code will check that we don't try to use the same
+// fd twice.)
+static const int kSandboxReporterFileDesc = 5;
+
+// This struct represents a system call that was rejected by a
+// seccomp-bpf policy.
+struct SandboxReport {
+  // In the future this may include finer distinctions than
+  // GeckoProcessType -- e.g., whether a content process can load
+  // file:/// URLs, or if it's reserved for content with certain
+  // user-granted permissions.
+  enum class ProcType : uint8_t {
+    CONTENT,
+    MEDIA_PLUGIN,
+  };
+
+  // The syscall number and arguments are usually `unsigned long`, but
+  // that causes ambiguous overload errors with nsACString::AppendInt.
+  using ULong = UnsignedStdintTypeForSize<sizeof(unsigned long)>::Type;
+
+  // This time uses CLOCK_MONOTONIC_COARSE.  Displaying or reporting
+  // it should usually be done relative to the current value of that
+  // clock (or the time at some other event of interest, like a
+  // subsequent crash).
+  struct timespec mTime;
+
+  // The pid/tid values, like every other field in this struct, aren't
+  // authenticated and a compromised process could send anything, so
+  // use the values with caution.
+  pid_t mPid;
+  pid_t mTid;
+  ProcType mProcType;
+  ULong mSyscall;
+  ULong mArgs[kSandboxSyscallArguments];
+
+  SandboxReport() : mPid(0) { }
+  bool IsValid() const { return mPid > 0; }
+};
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxReporterCommon_h
new file mode 100644
--- /dev/null
+++ b/security/sandbox/linux/reporter/SandboxReporterWrappers.cpp
@@ -0,0 +1,220 @@
+/* -*- 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 "mozISandboxReporter.h"
+#include "SandboxReporter.h"
+
+#include <time.h>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/ModuleUtils.h"
+#include "nsCOMPtr.h"
+#include "nsPrintfCString.h"
+#include "nsTArray.h"
+#include "nsXULAppAPI.h"
+
+namespace mozilla {
+
+class SandboxReportWrapper final : public mozISandboxReport
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_MOZISANDBOXREPORT
+
+  explicit SandboxReportWrapper(const SandboxReport& aReport)
+  : mReport(aReport)
+  { }
+
+private:
+  ~SandboxReportWrapper() { }
+  SandboxReport mReport;
+};
+
+NS_IMPL_ISUPPORTS(SandboxReportWrapper, mozISandboxReport)
+
+/* readonly attribute uint64_t msecAgo; */
+NS_IMETHODIMP SandboxReportWrapper::GetMsecAgo(uint64_t* aMsec)
+{
+  struct timespec then = mReport.mTime, now = { 0, 0 };
+  clock_gettime(CLOCK_MONOTONIC_COARSE, &now);
+
+  const uint64_t now_msec =
+    uint64_t(now.tv_sec) * 1000 + now.tv_nsec / 1000000;
+  const uint64_t then_msec =
+    uint64_t(then.tv_sec) * 1000 + then.tv_nsec / 1000000;
+  MOZ_DIAGNOSTIC_ASSERT(now_msec >= then_msec);
+  if (now_msec >= then_msec) {
+    *aMsec = now_msec - then_msec;
+  } else {
+    *aMsec = 0;
+  }
+  return NS_OK;
+}
+
+/* readonly attribute int32_t pid; */
+NS_IMETHODIMP SandboxReportWrapper::GetPid(int32_t *aPid)
+{
+  *aPid = mReport.mPid;
+  return NS_OK;
+}
+
+/* readonly attribute int32_t tid; */
+NS_IMETHODIMP SandboxReportWrapper::GetTid(int32_t *aTid)
+{
+  *aTid = mReport.mTid;
+  return NS_OK;
+}
+
+/* readonly attribute ACString procType; */
+NS_IMETHODIMP SandboxReportWrapper::GetProcType(nsACString& aProcType)
+{
+  switch (mReport.mProcType) {
+  case SandboxReport::ProcType::CONTENT:
+    aProcType.AssignLiteral("content");
+    return NS_OK;
+  case SandboxReport::ProcType::MEDIA_PLUGIN:
+    aProcType.AssignLiteral("mediaPlugin");
+    return NS_OK;
+  default:
+    MOZ_ASSERT(false);
+    return NS_ERROR_UNEXPECTED;
+  }
+}
+
+/* readonly attribute uint32_t syscall; */
+NS_IMETHODIMP SandboxReportWrapper::GetSyscall(uint32_t *aSyscall)
+{
+  *aSyscall = static_cast<uint32_t>(mReport.mSyscall);
+  MOZ_ASSERT(static_cast<SandboxReport::ULong>(*aSyscall) == mReport.mSyscall);
+  return NS_OK;
+}
+
+/* readonly attribute uint32_t numArgs; */
+NS_IMETHODIMP SandboxReportWrapper::GetNumArgs(uint32_t *aNumArgs)
+{
+  *aNumArgs = static_cast<uint32_t>(kSandboxSyscallArguments);
+  return NS_OK;
+}
+
+/* ACString getArg (in uint32_t aIndex); */
+NS_IMETHODIMP SandboxReportWrapper::GetArg(uint32_t aIndex,
+					   nsACString& aRetval)
+{
+  if (aIndex >= kSandboxSyscallArguments) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  const auto arg = mReport.mArgs[aIndex];
+  nsAutoCString str;
+  // Use decimal for smaller numbers (more likely ints) and hex for
+  // larger (more likely pointers).  This cutoff is arbitrary.
+  if (arg >= 1000000) {
+    str.AppendLiteral("0x");
+    str.AppendInt(arg, 16);
+  } else {
+    str.AppendInt(arg, 10);
+  }
+  aRetval = str;
+  return NS_OK;
+}
+
+class SandboxReportArray final : public mozISandboxReportArray
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_MOZISANDBOXREPORTARRAY
+
+  explicit SandboxReportArray(SandboxReporter::Snapshot&& aSnap)
+  : mOffset(aSnap.mOffset)
+  , mArray(Move(aSnap.mReports))
+  { }
+
+private:
+  ~SandboxReportArray() { }
+  uint64_t mOffset;
+  nsTArray<SandboxReport> mArray;
+};
+
+NS_IMPL_ISUPPORTS(SandboxReportArray, mozISandboxReportArray)
+
+/* readonly attribute uint64_t begin; */
+NS_IMETHODIMP SandboxReportArray::GetBegin(uint64_t *aBegin)
+{
+  *aBegin = mOffset;
+  return NS_OK;
+}
+
+/* readonly attribute uint64_t end; */
+NS_IMETHODIMP SandboxReportArray::GetEnd(uint64_t *aEnd)
+{
+  *aEnd = mOffset + mArray.Length();
+  return NS_OK;
+}
+
+/* mozISandboxReport getElement (in uint64_t aIndex); */
+NS_IMETHODIMP SandboxReportArray::GetElement(uint64_t aIndex, mozISandboxReport ** aRetval)
+{
+  uint64_t relIndex = aIndex - mOffset;
+  if (relIndex >= mArray.Length()) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  nsCOMPtr<mozISandboxReport> wrapper =
+    new SandboxReportWrapper(mArray[relIndex]);
+  wrapper.forget(aRetval);
+  return NS_OK;
+}
+
+class SandboxReporterWrapper final : public mozISandboxReporter
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_MOZISANDBOXREPORTER
+
+  SandboxReporterWrapper() { }
+
+private:
+  ~SandboxReporterWrapper() { }
+};
+
+NS_IMPL_ISUPPORTS(SandboxReporterWrapper, mozISandboxReporter)
+
+/* mozISandboxReportArray snapshot(); */
+NS_IMETHODIMP SandboxReporterWrapper::Snapshot(mozISandboxReportArray** aRetval)
+{
+  if (!XRE_IsParentProcess()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  nsCOMPtr<mozISandboxReportArray> wrapper =
+    new SandboxReportArray(SandboxReporter::Singleton()->GetSnapshot());
+  wrapper.forget(aRetval);
+  return NS_OK;
+}
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(SandboxReporterWrapper)
+
+NS_DEFINE_NAMED_CID(MOZ_SANDBOX_REPORTER_CID);
+
+static const mozilla::Module::CIDEntry kSandboxReporterCIDs[] = {
+  { &kMOZ_SANDBOX_REPORTER_CID, false, nullptr,
+    SandboxReporterWrapperConstructor },
+  { nullptr }
+};
+
+static const mozilla::Module::ContractIDEntry kSandboxReporterContracts[] = {
+  { MOZ_SANDBOX_REPORTER_CONTRACTID, &kMOZ_SANDBOX_REPORTER_CID },
+  { nullptr }
+};
+
+static const mozilla::Module kSandboxReporterModule = {
+  mozilla::Module::kVersion,
+  kSandboxReporterCIDs,
+  kSandboxReporterContracts
+};
+
+NSMODULE_DEFN(SandboxReporterModule) = &kSandboxReporterModule;
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/security/sandbox/linux/reporter/moz.build
@@ -0,0 +1,30 @@
+# -*- Mode: python; python-indent: 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/.
+
+EXPORTS.mozilla += [
+    'SandboxReporter.h',
+    'SandboxReporterCommon.h',
+]
+
+SOURCES += [
+    'SandboxReporter.cpp',
+    'SandboxReporterWrappers.cpp',
+]
+
+LOCAL_INCLUDES += [
+    '/security/sandbox/linux', # SandboxLogging.h
+]
+
+# Need this for base::PlatformThread
+include('/ipc/chromium/chromium-config.mozbuild')
+
+# Need this for safe_sprintf.h used by SandboxLogging.h,
+# but it has to be after ipc/chromium/src.
+LOCAL_INCLUDES += [
+    '/security/sandbox/chromium',
+]
+
+FINAL_LIBRARY = 'xul'
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -10548,16 +10548,26 @@
  "SANDBOX_CONTENT_ENABLED": {
     "alert_emails": ["gcp@mozilla.com"],
     "bug_numbers": [1098428],
     "expires_in_version": "55",
     "kind": "boolean",
     "cpp_guard": "XP_LINUX",
     "description": "Whether the sandbox is enabled for the content process"
   },
+  "SANDBOX_REJECTED_SYSCALLS": {
+    "alert_emails": ["jld@mozilla.com", "gcp@mozilla.com"],
+    "bug_numbers": [1286865],
+    "expires_in_version": "never",
+    "releaseChannelCollection": "opt-out",
+    "kind": "count",
+    "keyed": true,
+    "cpp_guard": "XP_LINUX",
+    "description": "System calls blocked by a seccomp-bpf sandbox policy; limited to syscalls where we would crash on Nightly.  The key is generally the architecture and syscall ID but in some cases we include non-personally-identifying information from the syscall arguments; see the function SubmitToTelemetry in security/sandbox/linux/reporter/SandboxReporter.cpp for details."
+  },
   "SYNC_WORKER_OPERATION": {
     "alert_emails": ["amarchesini@mozilla.com", "khuey@mozilla.com" ],
     "bug_numbers": [1267904],
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 5000,
     "n_buckets": 20,
     "keyed": true,
--- a/toolkit/content/aboutSupport.js
+++ b/toolkit/content/aboutSupport.js
@@ -582,21 +582,46 @@ var snapshotFormatters = {
     let strings = stringBundle();
     let tbody = $("sandbox-tbody");
     for (let key in data) {
       // Simplify the display a little in the common case.
       if (key === "hasPrivilegedUserNamespaces" &&
           data[key] === data["hasUserNamespaces"]) {
         continue;
       }
+      if (key === "syscallLog") {
+	// Not in this table.
+	continue;
+      }
       tbody.appendChild($.new("tr", [
         $.new("th", strings.GetStringFromName(key), "column"),
-        $.new("td", data[key])
+        $.new("td", data[key]),
       ]));
     }
+
+    let syscallBody = $("sandbox-syscalls-tbody");
+    let argsHead = $("sandbox-syscalls-argshead");
+    for (let syscall of data.syscallLog) {
+      if (argsHead.colSpan < syscall.args.length) {
+	argsHead.colSpan = syscall.args.length;
+      }
+      let cells = [
+	$.new("td", syscall.index, "integer"),
+	$.new("td", syscall.msecAgo / 1000),
+	$.new("td", syscall.pid, "integer"),
+	$.new("td", syscall.tid, "integer"),
+	$.new("td", strings.GetStringFromName("sandboxProcType." +
+					      syscall.procType)),
+	$.new("td", syscall.syscall, "integer"),
+      ];
+      for (let arg of syscall.args) {
+	cells.push($.new("td", arg, "integer"));
+      }
+      syscallBody.appendChild($.new("tr", cells));
+    }
   },
 };
 
 var $ = document.getElementById.bind(document);
 
 $.new = function $_new(tag, textContentOrChildren, className, attributes) {
   let elt = document.createElement(tag);
   if (className)
--- a/toolkit/content/aboutSupport.xhtml
+++ b/toolkit/content/aboutSupport.xhtml
@@ -543,15 +543,48 @@
       <h2 class="major-section" id="sandbox">
 	&aboutSupport.sandboxTitle;
       </h2>
 
       <table>
 	<tbody id="sandbox-tbody">
 	</tbody>
       </table>
+
+#if defined(XP_LINUX)
+      <h4>&aboutSupport.sandboxSyscallLogTitle;</h4>
+      <table>
+	<thead>
+	  <tr>
+	    <th>
+	      &aboutSupport.sandboxSyscallIndex;
+	    </th>
+	    <th>
+	      &aboutSupport.sandboxSyscallAge;
+	    </th>
+	    <th>
+	      &aboutSupport.sandboxSyscallPID;
+	    </th>
+	    <th>
+	      &aboutSupport.sandboxSyscallTID;
+	    </th>
+	    <th>
+	      &aboutSupport.sandboxSyscallProcType;
+	    </th>
+	    <th>
+	      &aboutSupport.sandboxSyscallNumber;
+	    </th>
+	    <th id="sandbox-syscalls-argshead">
+	      &aboutSupport.sandboxSyscallArgs;
+	    </th>
+	    </tr>
+	</thead>
+	<tbody id="sandbox-syscalls-tbody">
+	</tbody>
+      </table>
+#endif
 #endif
 
     </div>
 
   </body>
 
 </html>
--- a/toolkit/locales/en-US/chrome/global/aboutSupport.dtd
+++ b/toolkit/locales/en-US/chrome/global/aboutSupport.dtd
@@ -105,16 +105,24 @@ variant of aboutSupport.showDir.label. -
 
 <!ENTITY aboutSupport.installationHistoryTitle "Installation History">
 <!ENTITY aboutSupport.updateHistoryTitle "Update History">
 
 <!ENTITY aboutSupport.copyTextToClipboard.label "Copy text to clipboard">
 <!ENTITY aboutSupport.copyRawDataToClipboard.label "Copy raw data to clipboard">
 
 <!ENTITY aboutSupport.sandboxTitle "Sandbox">
+<!ENTITY aboutSupport.sandboxSyscallLogTitle "Rejected System Calls">
+<!ENTITY aboutSupport.sandboxSyscallIndex "#">
+<!ENTITY aboutSupport.sandboxSyscallAge "Seconds Ago">
+<!ENTITY aboutSupport.sandboxSyscallPID "PID">
+<!ENTITY aboutSupport.sandboxSyscallTID "TID">
+<!ENTITY aboutSupport.sandboxSyscallProcType "Process Type">
+<!ENTITY aboutSupport.sandboxSyscallNumber "Syscall">
+<!ENTITY aboutSupport.sandboxSyscallArgs "Arguments">
 
 <!ENTITY aboutSupport.safeModeTitle "Try Safe Mode">
 <!ENTITY aboutSupport.restartInSafeMode.label "Restart with Add-ons Disabled…">
 
 <!ENTITY aboutSupport.graphicsFeaturesTitle "Features">
 <!ENTITY aboutSupport.graphicsDiagnosticsTitle "Diagnostics">
 <!ENTITY aboutSupport.graphicsFailureLogTitle "Failure Log">
 <!ENTITY aboutSupport.graphicsGPU1Title "GPU #1">
--- a/toolkit/locales/en-US/chrome/global/aboutSupport.properties
+++ b/toolkit/locales/en-US/chrome/global/aboutSupport.properties
@@ -99,16 +99,18 @@ loadedLibVersions = Version in use
 
 hasSeccompBPF = Seccomp-BPF (System Call Filtering)
 hasSeccompTSync = Seccomp Thread Synchronization
 hasUserNamespaces = User Namespaces
 hasPrivilegedUserNamespaces = User Namespaces for privileged processes
 canSandboxContent = Content Process Sandboxing
 canSandboxMedia = Media Plugin Sandboxing
 contentSandboxLevel = Content Process Sandbox Level
+sandboxProcType.content = content
+sandboxProcType.mediaPlugin = media plugin
 
 # LOCALIZATION NOTE %1$S and %2$S will be replaced with the number of remote and the total number
 # of windows, respectively, while %3$S will be replaced with one of the status strings below,
 # which contains a description of the multi-process preference and status.
 # Note: multiProcessStatus.3 doesn't exist because status=3 was deprecated.
 multiProcessWindows = %1$S/%2$S (%3$S)
 multiProcessStatus.0 = Enabled by user
 multiProcessStatus.1 = Enabled by default
--- a/toolkit/modules/Troubleshoot.jsm
+++ b/toolkit/modules/Troubleshoot.jsm
@@ -570,16 +570,31 @@ if (AppConstants.MOZ_SANDBOX) {
 
       let sysInfo = Cc["@mozilla.org/system-info;1"].
                     getService(Ci.nsIPropertyBag2);
       for (let key of keys) {
         if (sysInfo.hasKey(key)) {
           data[key] = sysInfo.getPropertyAsBool(key);
         }
       }
+
+      let reporter = Cc["@mozilla.org/sandbox/syscall-reporter;1"].
+                     getService(Ci.mozISandboxReporter);
+      const snapshot = reporter.snapshot();
+      let syscalls = [];
+      for (let index = snapshot.begin; index < snapshot.end; ++index) {
+        let report = snapshot.getElement(index);
+        let { msecAgo, pid, tid, procType, syscall } = report;
+        let args = []
+        for (let i = 0; i < report.numArgs; ++i) {
+          args.push(report.getArg(i));
+        }
+        syscalls.push({ index, msecAgo, pid, tid, procType, syscall, args });
+      }
+      data.syscallLog = syscalls;
     }
 
     if (AppConstants.MOZ_CONTENT_SANDBOX) {
       data.contentSandboxLevel =
         Services.prefs.getIntPref("security.sandbox.content.level");
     }
 
     done(data);
--- a/toolkit/modules/tests/browser/browser_Troubleshoot.js
+++ b/toolkit/modules/tests/browser/browser_Troubleshoot.js
@@ -482,16 +482,52 @@ const SNAPSHOT_SCHEMA = {
         canSandboxMedia: {
           required: false,
           type: "boolean"
         },
         contentSandboxLevel: {
           required: AppConstants.MOZ_CONTENT_SANDBOX,
           type: "number"
         },
+	syscallLog: {
+	  required: AppConstants.platform == "linux",
+	  type: "array",
+	  items: {
+	    type: "object",
+	    properties: {
+	      index: {
+		required: true,
+		type: "number",
+	      },
+	      pid: {
+		required: true,
+		type: "number",
+	      },
+	      tid: {
+		required: true,
+		type: "number",
+	      },
+	      procType: {
+		required: true,
+		type: "string",
+	      },
+	      syscall: {
+		required: true,
+		type: "number",
+	      },
+	      args: {
+		required: true,
+		type: "array",
+		items: {
+		  type: "string",
+		},
+	      },
+	    },
+	  },
+	},
       },
     },
   },
 };
 
 /**
  * Throws an Error if obj doesn't conform to schema.  That way you get a nice
  * error message and a stack to help you figure out what went wrong, which you
--- a/toolkit/themes/shared/aboutSupport.css
+++ b/toolkit/themes/shared/aboutSupport.css
@@ -64,16 +64,21 @@ th.column {
   width: 0px;
 }
 
 td {
   text-align: start;
   border-color: var(--in-content-table-border-dark-color);
 }
 
+td.integer {
+  text-align: end;
+  font-family: monospace;
+}
+
 .prefs-table {
   width: 100%;
   table-layout: fixed;
 }
 
 .pref-name {
   width: 70%;
   white-space: nowrap;