SandboxReporter.cpp (9580B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "SandboxReporter.h" 8 #include "SandboxLogging.h" 9 10 #include <algorithm> 11 #include <errno.h> 12 #include <sys/socket.h> 13 #include <sys/types.h> 14 #include <time.h> // for clockid_t 15 16 #include "GeckoProfiler.h" 17 #include "mozilla/Assertions.h" 18 #include "mozilla/ClearOnShutdown.h" 19 #include "mozilla/StaticMutex.h" 20 #include "mozilla/PodOperations.h" 21 #include "nsThreadUtils.h" 22 #include "mozilla/glean/SecuritySandboxMetrics.h" 23 #include "sandbox/linux/system_headers/linux_syscalls.h" 24 25 // Distinguish architectures for the telemetry key. 26 #if defined(__i386__) 27 # define SANDBOX_ARCH_NAME "x86" 28 #elif defined(__x86_64__) 29 # define SANDBOX_ARCH_NAME "amd64" 30 #elif defined(__arm__) 31 # define SANDBOX_ARCH_NAME "arm" 32 #elif defined(__aarch64__) 33 # define SANDBOX_ARCH_NAME "arm64" 34 #else 35 # error "unrecognized architecture" 36 #endif 37 38 namespace mozilla { 39 40 StaticAutoPtr<SandboxReporter> SandboxReporter::sSingleton; 41 42 SandboxReporter::SandboxReporter() 43 : mClientFd(-1), 44 mServerFd(-1), 45 mMutex("SandboxReporter"), 46 mBuffer(MakeUnique<SandboxReport[]>(kSandboxReporterBufferSize)), 47 mCount(0) {} 48 49 bool SandboxReporter::Init() { 50 int fds[2]; 51 52 if (0 != socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, fds)) { 53 SANDBOX_LOG_ERRNO("SandboxReporter: socketpair failed"); 54 return false; 55 } 56 mClientFd = fds[0]; 57 mServerFd = fds[1]; 58 59 if (!PlatformThread::Create(0, this, &mThread)) { 60 SANDBOX_LOG_ERRNO("SandboxReporter: thread creation failed"); 61 close(mClientFd); 62 close(mServerFd); 63 mClientFd = mServerFd = -1; 64 return false; 65 } 66 67 return true; 68 } 69 70 SandboxReporter::~SandboxReporter() { 71 if (mServerFd < 0) { 72 return; 73 } 74 shutdown(mServerFd, SHUT_RD); 75 PlatformThread::Join(mThread); 76 close(mServerFd); 77 close(mClientFd); 78 } 79 80 /* static */ 81 SandboxReporter* SandboxReporter::Singleton() { 82 static StaticMutex sMutex MOZ_UNANNOTATED; 83 StaticMutexAutoLock lock(sMutex); 84 85 if (sSingleton == nullptr) { 86 sSingleton = new SandboxReporter(); 87 if (!sSingleton->Init()) { 88 // If socketpair or thread creation failed, trying to continue 89 // with child process creation is unlikely to succeed; crash 90 // instead of trying to handle that case. 91 MOZ_CRASH("SandboxRepoter::Singleton: initialization failed"); 92 } 93 // ClearOnShutdown must be called on the main thread and will 94 // destroy the object on the main thread. That *should* be safe; 95 // the destructor will shut down the reporter's socket reader 96 // thread before freeing anything, IPC should already be shut down 97 // by that point (so it won't race by calling Singleton()), all 98 // non-main XPCOM threads will also be shut down, and currently 99 // the only other user is the main-thread-only Troubleshoot.sys.mjs. 100 NS_DispatchToMainThread(NS_NewRunnableFunction( 101 "SandboxReporter::Singleton", [] { ClearOnShutdown(&sSingleton); })); 102 } 103 return sSingleton.get(); 104 } 105 106 int SandboxReporter::GetClientFileDescriptor() const { 107 MOZ_ASSERT(mClientFd >= 0); 108 return mClientFd; 109 } 110 111 // This function is mentioned in Histograms.json; keep that in mind if 112 // it's renamed or moved to a different file. 113 static void SubmitToTelemetry(const SandboxReport& aReport) { 114 nsAutoCString key; 115 // The key contains the process type, something that uniquely 116 // identifies the syscall, and in some cases arguments (see below 117 // for details). Arbitrary formatting choice: fields in the key are 118 // separated by ':', except that (arch, syscall#) pairs are 119 // separated by '/'. 120 // 121 // Examples: 122 // * "content:x86/64" (bug 1285768) 123 // * "content:x86_64/110" (bug 1285768) 124 // * "gmp:madvise:8" (bug 1303813) 125 // * "content:clock_gettime:4" (bug 1334687) 126 127 switch (aReport.mProcType) { 128 case SandboxReport::ProcType::CONTENT: 129 key.AppendLiteral("content"); 130 break; 131 case SandboxReport::ProcType::FILE: 132 key.AppendLiteral("file"); 133 break; 134 case SandboxReport::ProcType::MEDIA_PLUGIN: 135 key.AppendLiteral("gmp"); 136 break; 137 case SandboxReport::ProcType::RDD: 138 key.AppendLiteral("rdd"); 139 break; 140 case SandboxReport::ProcType::SOCKET_PROCESS: 141 key.AppendLiteral("socket"); 142 break; 143 case SandboxReport::ProcType::UTILITY: 144 key.AppendLiteral("utility"); 145 break; 146 default: 147 MOZ_ASSERT(false); 148 } 149 key.Append(':'); 150 151 switch (aReport.mSyscall) { 152 // Syscalls that are filtered by arguments in one or more of the 153 // policies in SandboxFilter.cpp should generally have those 154 // arguments included here, but don't include irrelevant 155 // information that would cause large numbers of distinct keys for 156 // the same issue -- for example, pids or pointers. When in 157 // doubt, include arguments only if they would typically be 158 // constants (or asm immediates) in the code making the syscall. 159 // 160 // Also, keep in mind that this is opt-out data collection and 161 // privacy is critical. While it's unlikely that information in 162 // the register values alone could personally identify a user 163 // (see also crash reports, where register contents are public), 164 // and the guidelines in the previous paragraph should rule out 165 // any value that's capable of holding PII, please be careful. 166 // 167 // When making changes here, please consult with a data steward 168 // (https://wiki.mozilla.org/Firefox/Data_Collection) and ask for 169 // a review if you are unsure about anything. 170 171 // This macro includes one argument as a decimal number; it should 172 // be enough for most cases. 173 #define ARG_DECIMAL(name, idx) \ 174 case __NR_##name: \ 175 key.AppendLiteral(#name ":"); \ 176 key.AppendInt(aReport.mArgs[idx]); \ 177 break 178 179 // This may be more convenient if the argument is a set of bit flags. 180 #define ARG_HEX(name, idx) \ 181 case __NR_##name: \ 182 key.AppendLiteral(#name ":0x"); \ 183 key.AppendInt(aReport.mArgs[idx], 16); \ 184 break 185 186 // clockid_t is annoying: there are a small set of fixed timers, 187 // but it can also encode a pid/tid (or a fd for a hardware clock 188 // device); in this case the value is negative. 189 #define ARG_CLOCKID(name, idx) \ 190 case __NR_##name: \ 191 key.AppendLiteral(#name ":"); \ 192 if (static_cast<clockid_t>(aReport.mArgs[idx]) < 0) { \ 193 key.AppendLiteral("dynamic"); \ 194 } else { \ 195 key.AppendInt(aReport.mArgs[idx]); \ 196 } \ 197 break 198 199 // The syscalls handled specially: 200 201 ARG_HEX(clone, 0); // flags 202 ARG_DECIMAL(prctl, 0); // option 203 ARG_HEX(ioctl, 1); // request 204 ARG_DECIMAL(fcntl, 1); // cmd 205 ARG_DECIMAL(madvise, 2); // advice 206 ARG_CLOCKID(clock_gettime, 0); // clk_id 207 208 #ifdef __NR_socketcall 209 ARG_DECIMAL(socketcall, 0); // call 210 #endif 211 #ifdef __NR_ipc 212 ARG_DECIMAL(ipc, 0); // call 213 #endif 214 215 #undef ARG_DECIMAL 216 #undef ARG_HEX 217 #undef ARG_CLOCKID 218 219 default: 220 // Otherwise just use the number, with the arch name to disambiguate. 221 key.Append(SANDBOX_ARCH_NAME "/"); 222 key.AppendInt(aReport.mSyscall); 223 } 224 225 glean::sandbox::rejected_syscalls.Get(key).Add(1); 226 } 227 228 void SandboxReporter::AddOne(const SandboxReport& aReport) { 229 SubmitToTelemetry(aReport); 230 231 MutexAutoLock lock(mMutex); 232 mBuffer[mCount % kSandboxReporterBufferSize] = aReport; 233 ++mCount; 234 } 235 236 void SandboxReporter::ThreadMain(void) { 237 // Create a nsThread wrapper for the current platform thread, and register it 238 // with the thread manager. 239 (void)NS_GetCurrentThread(); 240 241 PlatformThread::SetName("SandboxReporter"); 242 AUTO_PROFILER_REGISTER_THREAD("SandboxReporter"); 243 244 for (;;) { 245 SandboxReport rep; 246 struct iovec iov; 247 struct msghdr msg; 248 249 iov.iov_base = &rep; 250 iov.iov_len = sizeof(rep); 251 PodZero(&msg); 252 msg.msg_iov = &iov; 253 msg.msg_iovlen = 1; 254 255 const auto recvd = recvmsg(mServerFd, &msg, 0); 256 if (recvd < 0) { 257 if (errno == EINTR) { 258 continue; 259 } 260 SANDBOX_LOG_ERRNO("SandboxReporter: recvmsg"); 261 } 262 if (recvd <= 0) { 263 break; 264 } 265 266 if (static_cast<size_t>(recvd) < sizeof(rep)) { 267 SANDBOX_LOG("SandboxReporter: packet too short (%d < %d)", recvd, 268 sizeof(rep)); 269 continue; 270 } 271 if (msg.msg_flags & MSG_TRUNC) { 272 SANDBOX_LOG("SandboxReporter: packet too long"); 273 continue; 274 } 275 276 AddOne(rep); 277 } 278 } 279 280 SandboxReporter::Snapshot SandboxReporter::GetSnapshot() { 281 Snapshot snapshot; 282 MutexAutoLock lock(mMutex); 283 284 const uint64_t bufSize = static_cast<uint64_t>(kSandboxReporterBufferSize); 285 const uint64_t start = std::max(mCount, bufSize) - bufSize; 286 snapshot.mOffset = start; 287 snapshot.mReports.Clear(); 288 snapshot.mReports.SetCapacity(mCount - start); 289 for (size_t i = start; i < mCount; ++i) { 290 const SandboxReport* rep = &mBuffer[i % kSandboxReporterBufferSize]; 291 MOZ_ASSERT(rep->IsValid()); 292 snapshot.mReports.AppendElement(*rep); 293 } 294 return snapshot; 295 } 296 297 } // namespace mozilla