Bug 1286865 - Step 4: Report rejected syscall info in Telemetry. r=gcp r?francois draft
authorJed Davis <jld@mozilla.com>
Mon, 13 Feb 2017 20:04:34 -0700
changeset 483315 6ff5e2eeb5af713795633eee7016a92c14602fe6
parent 483312 9a6ab34652a9569efe08393b5e1e689d8fe9e406
child 545617 6261ca880baf9d5f5c4b551422cb2af3b73fd450
push id45286
push userbmo:jld@mozilla.com
push dateTue, 14 Feb 2017 04:22:58 +0000
reviewersgcp, francois
bugs1286865
milestone54.0a1
Bug 1286865 - Step 4: Report rejected syscall info in Telemetry. r=gcp r?francois MozReview-Commit-ID: 7R755WT1Ftu
security/sandbox/linux/reporter/SandboxReporter.cpp
toolkit/components/telemetry/Histograms.json
--- a/security/sandbox/linux/reporter/SandboxReporter.cpp
+++ b/security/sandbox/linux/reporter/SandboxReporter.cpp
@@ -6,22 +6,34 @@
 
 #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)
@@ -98,20 +110,122 @@ SandboxReporter::Singleton()
 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.
+
+    // 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)
 {
-  // TODO: send a copy to Telemetry
+  SubmitToTelemetry(aReport);
+
   MutexAutoLock lock(mMutex);
   mBuffer[mCount % kSandboxReporterBufferSize] = aReport;
   ++mCount;
 }
 
 void
 SandboxReporter::ThreadMain(void)
 {
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -10414,16 +10414,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,