summaryrefslogtreecommitdiffstats
path: root/security/sandbox/linux/reporter/SandboxReporter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'security/sandbox/linux/reporter/SandboxReporter.cpp')
-rw-r--r--security/sandbox/linux/reporter/SandboxReporter.cpp299
1 files changed, 299 insertions, 0 deletions
diff --git a/security/sandbox/linux/reporter/SandboxReporter.cpp b/security/sandbox/linux/reporter/SandboxReporter.cpp
new file mode 100644
index 0000000000..a7c71cd5c9
--- /dev/null
+++ b/security/sandbox/linux/reporter/SandboxReporter.cpp
@@ -0,0 +1,299 @@
+/* -*- 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 "GeckoProfiler.h"
+#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"
+#elif defined(__arm__)
+# define SANDBOX_ARCH_NAME "arm"
+#elif defined(__aarch64__)
+# define SANDBOX_ARCH_NAME "arm64"
+#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 | SOCK_CLOEXEC, 0, fds)) {
+ SANDBOX_LOG_ERRNO("SandboxReporter: socketpair failed");
+ return false;
+ }
+ mClientFd = fds[0];
+ mServerFd = fds[1];
+
+ if (!PlatformThread::Create(0, this, &mThread)) {
+ SANDBOX_LOG_ERRNO("SandboxReporter: thread creation failed");
+ 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 MOZ_UNANNOTATED;
+ 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.sys.mjs.
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "SandboxReporter::Singleton", [] { 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::FILE:
+ key.AppendLiteral("file");
+ break;
+ case SandboxReport::ProcType::MEDIA_PLUGIN:
+ key.AppendLiteral("gmp");
+ break;
+ case SandboxReport::ProcType::RDD:
+ key.AppendLiteral("rdd");
+ break;
+ case SandboxReport::ProcType::SOCKET_PROCESS:
+ key.AppendLiteral("socket");
+ break;
+ case SandboxReport::ProcType::UTILITY:
+ key.AppendLiteral("utility");
+ 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_HEX(ioctl, 1); // request
+ ARG_DECIMAL(fcntl, 1); // cmd
+ 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) {
+ // Create a nsThread wrapper for the current platform thread, and register it
+ // with the thread manager.
+ (void)NS_GetCurrentThread();
+
+ PlatformThread::SetName("SandboxReporter");
+ AUTO_PROFILER_REGISTER_THREAD("SandboxReporter");
+
+ 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_ERRNO("SandboxReporter: recvmsg");
+ }
+ if (recvd <= 0) {
+ break;
+ }
+
+ if (static_cast<size_t>(recvd) < sizeof(rep)) {
+ SANDBOX_LOG("SandboxReporter: packet too short (%d < %d)", recvd,
+ sizeof(rep));
+ continue;
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ SANDBOX_LOG("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);
+ }
+ return snapshot;
+}
+
+} // namespace mozilla