Bug 1884378 - Add Sandbox error profiler markers r=padenot,fluent-reviewers,profiler-reviewers,bolsson,florian

Differential Revision: https://phabricator.services.mozilla.com/D204048
This commit is contained in:
Alexandre Lissy
2024-06-14 10:17:37 +00:00
parent 5aebb49661
commit 4953ba5461
10 changed files with 172 additions and 21 deletions

View File

@@ -11,10 +11,12 @@
#include "SandboxChrootProto.h" #include "SandboxChrootProto.h"
#include "SandboxFilter.h" #include "SandboxFilter.h"
#include "SandboxInternal.h" #include "SandboxInternal.h"
#include "SandboxLogging.h"
#include "SandboxOpenedFiles.h" #include "SandboxOpenedFiles.h"
#include "SandboxReporterClient.h" #include "SandboxReporterClient.h"
#include "SandboxProfilerChild.h"
#include "SandboxLogging.h"
#include <dirent.h> #include <dirent.h>
#ifdef NIGHTLY_BUILD #ifdef NIGHTLY_BUILD
# include "dlfcn.h" # include "dlfcn.h"
@@ -574,6 +576,33 @@ static void SandboxLateInit() {
} }
RunGlibcLazyInitializers(); RunGlibcLazyInitializers();
// This will run on main thread before it is in a signal-handler context, to
// make sure rprofiler pointers are properly initialized (and send a marker
// with a stack if the profiler is already running) on the main thread for
// later use (read-only) on other threads.
//
// If profiler is already started (e.g., MOZ_PROFILER_STARTUP=1) the following
// will be instantiated, but if the profiler is not yet started, then it is a
// no-op and rely on "profiler-started" observer from
// RegisterProfilerObserversForSandboxProfiler:
//
// This will create:
// - pointers to uprofiler to make use of the profiler
// - a SandboxProfiler
// - a MPSCQueue
// - a std::thread
//
// So that later usage of uprofiler under SIGSYS context can:
// - safely (i.e., no alloc etc.) take a stack
// - copy it over to the queue
// - thread polling from the queue in a more favorable context will be able
// to do what is required to finish sending to the profiler
// If the profiler is not running those are no-op
SandboxProfiler::Create();
const void* top = CallerPC();
SandboxProfiler::ReportInit(top);
} }
// Common code for sandbox startup. // Common code for sandbox startup.
@@ -814,4 +843,8 @@ bool SetSandboxCrashOnError(bool aValue) {
return oldValue; return oldValue;
} }
void DestroySandboxProfiler() { SandboxProfiler::Shutdown(); }
void CreateSandboxProfiler() { SandboxProfiler::Create(); }
} // namespace mozilla } // namespace mozilla

View File

@@ -71,6 +71,12 @@ MOZ_EXPORT void SetUtilitySandbox(int aBroker, ipc::SandboxingKind aKind);
// This will return current value and set the aValue we pass // This will return current value and set the aValue we pass
MOZ_EXPORT bool SetSandboxCrashOnError(bool aValue); MOZ_EXPORT bool SetSandboxCrashOnError(bool aValue);
// Call SandboxProfiler::Create to make sure SandboxProfiler exists if it should
// exists, i.e., profiler symbols were found and the profiler is running
MOZ_EXPORT void CreateSandboxProfiler();
MOZ_EXPORT void DestroySandboxProfiler();
} // namespace mozilla } // namespace mozilla
#endif // mozilla_Sandbox_h #endif // mozilla_Sandbox_h

View File

@@ -19,6 +19,7 @@
#include <unistd.h> #include <unistd.h>
#include "mozilla/Assertions.h" #include "mozilla/Assertions.h"
#include "mozilla/Atomics.h"
#include "base/strings/safe_sprintf.h" #include "base/strings/safe_sprintf.h"
namespace mozilla { namespace mozilla {
@@ -55,6 +56,17 @@ int SandboxBrokerClient::DoCall(const Request* aReq, const char* aPath,
} }
} }
if (SandboxInfo::Get().Test(SandboxInfo::kVerboseTests)) {
// Dont use SANDBOX_LOG directly to not be too spammy, just make sure the
// ReportLog() works as expected
SandboxProfiler::ReportLog(OperationDescription[aReq->mOp]);
}
const void* top = CallerPC();
SandboxProfiler::ReportRequest(top, aReq->mId,
OperationDescription[aReq->mOp], aReq->mFlags,
aPath, aPath2, getpid());
struct iovec ios[3]; struct iovec ios[3];
int respFds[2]; int respFds[2];
@@ -141,8 +153,17 @@ int SandboxBrokerClient::DoCall(const Request* aReq, const char* aPath,
return resp.mError; return resp.mError;
} }
SandboxBrokerCommon::Request MakeRequest(
const SandboxBrokerCommon::Operation aOp, const int aFlags,
const size_t aBufSize) {
static Atomic<uint64_t> reqId{0};
SandboxBrokerCommon::Request req = {aOp, aFlags, reqId, aBufSize};
reqId++;
return req;
}
int SandboxBrokerClient::Open(const char* aPath, int aFlags) { int SandboxBrokerClient::Open(const char* aPath, int aFlags) {
Request req = {SANDBOX_FILE_OPEN, aFlags, 0}; Request req = MakeRequest(SANDBOX_FILE_OPEN, aFlags, 0);
int maybeFd = DoCall(&req, aPath, nullptr, nullptr, true); int maybeFd = DoCall(&req, aPath, nullptr, nullptr, true);
if (maybeFd >= 0) { if (maybeFd >= 0) {
// NSPR has opinions about file flags. Fix O_CLOEXEC. // NSPR has opinions about file flags. Fix O_CLOEXEC.
@@ -154,7 +175,7 @@ int SandboxBrokerClient::Open(const char* aPath, int aFlags) {
} }
int SandboxBrokerClient::Access(const char* aPath, int aMode) { int SandboxBrokerClient::Access(const char* aPath, int aMode) {
Request req = {SANDBOX_FILE_ACCESS, aMode, 0}; Request req = MakeRequest(SANDBOX_FILE_ACCESS, aMode, 0);
return DoCall(&req, aPath, nullptr, nullptr, false); return DoCall(&req, aPath, nullptr, nullptr, false);
} }
@@ -163,7 +184,7 @@ int SandboxBrokerClient::Stat(const char* aPath, statstruct* aStat) {
return -EFAULT; return -EFAULT;
} }
Request req = {SANDBOX_FILE_STAT, 0, sizeof(statstruct)}; Request req = MakeRequest(SANDBOX_FILE_STAT, 0, sizeof(statstruct));
return DoCall(&req, aPath, nullptr, (void*)aStat, false); return DoCall(&req, aPath, nullptr, (void*)aStat, false);
} }
@@ -172,48 +193,48 @@ int SandboxBrokerClient::LStat(const char* aPath, statstruct* aStat) {
return -EFAULT; return -EFAULT;
} }
Request req = {SANDBOX_FILE_STAT, O_NOFOLLOW, sizeof(statstruct)}; Request req = MakeRequest(SANDBOX_FILE_STAT, O_NOFOLLOW, sizeof(statstruct));
return DoCall(&req, aPath, nullptr, (void*)aStat, false); return DoCall(&req, aPath, nullptr, (void*)aStat, false);
} }
int SandboxBrokerClient::Chmod(const char* aPath, int aMode) { int SandboxBrokerClient::Chmod(const char* aPath, int aMode) {
Request req = {SANDBOX_FILE_CHMOD, aMode, 0}; Request req = MakeRequest(SANDBOX_FILE_CHMOD, aMode, 0);
return DoCall(&req, aPath, nullptr, nullptr, false); return DoCall(&req, aPath, nullptr, nullptr, false);
} }
int SandboxBrokerClient::Link(const char* aOldPath, const char* aNewPath) { int SandboxBrokerClient::Link(const char* aOldPath, const char* aNewPath) {
Request req = {SANDBOX_FILE_LINK, 0, 0}; Request req = MakeRequest(SANDBOX_FILE_LINK, 0, 0);
return DoCall(&req, aOldPath, aNewPath, nullptr, false); return DoCall(&req, aOldPath, aNewPath, nullptr, false);
} }
int SandboxBrokerClient::Symlink(const char* aOldPath, const char* aNewPath) { int SandboxBrokerClient::Symlink(const char* aOldPath, const char* aNewPath) {
Request req = {SANDBOX_FILE_SYMLINK, 0, 0}; Request req = MakeRequest(SANDBOX_FILE_SYMLINK, 0, 0);
return DoCall(&req, aOldPath, aNewPath, nullptr, false); return DoCall(&req, aOldPath, aNewPath, nullptr, false);
} }
int SandboxBrokerClient::Rename(const char* aOldPath, const char* aNewPath) { int SandboxBrokerClient::Rename(const char* aOldPath, const char* aNewPath) {
Request req = {SANDBOX_FILE_RENAME, 0, 0}; Request req = MakeRequest(SANDBOX_FILE_RENAME, 0, 0);
return DoCall(&req, aOldPath, aNewPath, nullptr, false); return DoCall(&req, aOldPath, aNewPath, nullptr, false);
} }
int SandboxBrokerClient::Mkdir(const char* aPath, int aMode) { int SandboxBrokerClient::Mkdir(const char* aPath, int aMode) {
Request req = {SANDBOX_FILE_MKDIR, aMode, 0}; Request req = MakeRequest(SANDBOX_FILE_MKDIR, aMode, 0);
return DoCall(&req, aPath, nullptr, nullptr, false); return DoCall(&req, aPath, nullptr, nullptr, false);
} }
int SandboxBrokerClient::Unlink(const char* aPath) { int SandboxBrokerClient::Unlink(const char* aPath) {
Request req = {SANDBOX_FILE_UNLINK, 0, 0}; Request req = MakeRequest(SANDBOX_FILE_UNLINK, 0, 0);
return DoCall(&req, aPath, nullptr, nullptr, false); return DoCall(&req, aPath, nullptr, nullptr, false);
} }
int SandboxBrokerClient::Rmdir(const char* aPath) { int SandboxBrokerClient::Rmdir(const char* aPath) {
Request req = {SANDBOX_FILE_RMDIR, 0, 0}; Request req = MakeRequest(SANDBOX_FILE_RMDIR, 0, 0);
return DoCall(&req, aPath, nullptr, nullptr, false); return DoCall(&req, aPath, nullptr, nullptr, false);
} }
int SandboxBrokerClient::Readlink(const char* aPath, void* aBuff, int SandboxBrokerClient::Readlink(const char* aPath, void* aBuff,
size_t aSize) { size_t aSize) {
Request req = {SANDBOX_FILE_READLINK, 0, aSize}; Request req = MakeRequest(SANDBOX_FILE_READLINK, 0, aSize);
return DoCall(&req, aPath, nullptr, aBuff, false); return DoCall(&req, aPath, nullptr, aBuff, false);
} }
@@ -250,7 +271,7 @@ int SandboxBrokerClient::Connect(const sockaddr_un* aAddr, size_t aLen,
memcpy(tmpBuf, path + 1, bufLen - 1); memcpy(tmpBuf, path + 1, bufLen - 1);
tmpBuf[bufLen - 1] = '\0'; tmpBuf[bufLen - 1] = '\0';
const Request req = {SANDBOX_SOCKET_CONNECT_ABSTRACT, aType, 0}; const Request req = MakeRequest(SANDBOX_SOCKET_CONNECT_ABSTRACT, aType, 0);
return DoCall(&req, tmpBuf, nullptr, nullptr, true); return DoCall(&req, tmpBuf, nullptr, nullptr, true);
} }
@@ -267,7 +288,7 @@ int SandboxBrokerClient::Connect(const sockaddr_un* aAddr, size_t aLen,
return -ENETUNREACH; return -ENETUNREACH;
} }
const Request req = {SANDBOX_SOCKET_CONNECT, aType, 0}; const Request req = MakeRequest(SANDBOX_SOCKET_CONNECT, aType, 0);
return DoCall(&req, path, nullptr, nullptr, true); return DoCall(&req, path, nullptr, nullptr, true);
} }

View File

@@ -33,6 +33,8 @@
#include <errno.h> #include <errno.h>
#include "SandboxProfiler.h"
namespace mozilla { namespace mozilla {
// Logs the formatted string (marked with "error" severity, if supported). // Logs the formatted string (marked with "error" severity, if supported).
void SandboxLogError(const char* aMessage); void SandboxLogError(const char* aMessage);
@@ -60,6 +62,7 @@ ssize_t GetLibcErrorName(char* aBuf, size_t aSize, int aErr);
char _sandboxLogBuf[SANDBOX_LOG_LEN]; \ char _sandboxLogBuf[SANDBOX_LOG_LEN]; \
::base::strings::SafeSPrintf(_sandboxLogBuf, fmt, ##args); \ ::base::strings::SafeSPrintf(_sandboxLogBuf, fmt, ##args); \
::mozilla::SandboxLogError(_sandboxLogBuf); \ ::mozilla::SandboxLogError(_sandboxLogBuf); \
::mozilla::SandboxProfiler::ReportLog(_sandboxLogBuf); \
} while (0) } while (0)
#define SANDBOX_LOG_WITH_ERROR(errnum, fmt, args...) \ #define SANDBOX_LOG_WITH_ERROR(errnum, fmt, args...) \
@@ -73,6 +76,7 @@ ssize_t GetLibcErrorName(char* aBuf, size_t aSize, int aErr);
errnum); \ errnum); \
} \ } \
::mozilla::SandboxLogError(_sandboxLogBuf); \ ::mozilla::SandboxLogError(_sandboxLogBuf); \
::mozilla::SandboxProfiler::ReportLog(_sandboxLogBuf); \
} while (0) } while (0)
#define SANDBOX_LOG_ERRNO(fmt, args...) \ #define SANDBOX_LOG_ERRNO(fmt, args...) \

View File

@@ -0,0 +1,62 @@
/* 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 https://mozilla.org/MPL/2.0/. */
#ifndef SANDBOX_PROFILER_STARTUP_H
#define SANDBOX_PROFILER_STARTUP_H
#include "mozilla/Services.h"
#include "nsCOMPtr.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsISupportsImpl.h"
/*
* This code is here to help bring up SandboxProfiler whenever the profiler
* is started by the user. We cannot have that code live within
* SandboxProfiler.cpp itself because we rely on libxul facilities, while the
* sandbox code lives within libmozsandbox.
*/
namespace {
class ProfilerStartupObserverForSandboxProfiler final : public nsIObserver {
~ProfilerStartupObserverForSandboxProfiler() = default;
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
};
NS_IMPL_ISUPPORTS(ProfilerStartupObserverForSandboxProfiler, nsIObserver)
NS_IMETHODIMP ProfilerStartupObserverForSandboxProfiler::Observe(
nsISupports* aSubject, const char* aTopic, const char16_t* aData) {
if (strcmp(aTopic, "profiler-started") == 0) {
mozilla::CreateSandboxProfiler();
return NS_OK;
}
if (strcmp(aTopic, "profiler-stopped") == 0) {
mozilla::DestroySandboxProfiler();
return NS_OK;
}
return NS_OK;
}
} // anonymous namespace
inline void RegisterProfilerObserversForSandboxProfiler() {
nsCOMPtr<nsIObserverService> obsServ(mozilla::services::GetObserverService());
MOZ_ASSERT(!!obsServ);
if (!obsServ) {
return;
}
nsCOMPtr<nsIObserver> obs(new ProfilerStartupObserverForSandboxProfiler());
obsServ->AddObserver(obs, "profiler-started", false);
obsServ->AddObserver(obs, "profiler-stopped", false);
}
#endif // SANDBOX_PROFILER_STARTUP_H

View File

@@ -6,7 +6,10 @@
#include "SandboxBroker.h" #include "SandboxBroker.h"
#include "SandboxInfo.h" #include "SandboxInfo.h"
#include "SandboxProfilerParent.h"
#include "SandboxLogging.h" #include "SandboxLogging.h"
#include "SandboxBrokerUtils.h" #include "SandboxBrokerUtils.h"
#include <dirent.h> #include <dirent.h>
@@ -41,6 +44,12 @@ namespace mozilla {
// Default/fallback temporary directory // Default/fallback temporary directory
static const nsLiteralCString tempDirPrefix("/tmp"); static const nsLiteralCString tempDirPrefix("/tmp");
// kernel level limit defined at
// https://elixir.bootlin.com/linux/latest/source/include/linux/sched.h#L301
// used at
// https://elixir.bootlin.com/linux/latest/source/include/linux/sched.h#L1087
static const int kThreadNameMaxSize = 16;
// This constructor signals failure by setting mFileDesc and aClientFd to -1. // This constructor signals failure by setting mFileDesc and aClientFd to -1.
SandboxBroker::SandboxBroker(UniquePtr<const Policy> aPolicy, int aChildPid, SandboxBroker::SandboxBroker(UniquePtr<const Policy> aPolicy, int aChildPid,
int& aClientFd) int& aClientFd)
@@ -656,7 +665,10 @@ void SandboxBroker::ThreadMain(void) {
// with the thread manager. // with the thread manager.
(void)NS_GetCurrentThread(); (void)NS_GetCurrentThread();
char threadName[16]; char threadName[kThreadNameMaxSize];
// mChildPid can be max 7 digits because of the previous string size,
// and 'FSBroker' is 8 bytes. The maximum thread size is 16 with the null byte
// included. That leaves us 7 digits.
SprintfLiteral(threadName, "FSBroker%d", mChildPid); SprintfLiteral(threadName, "FSBroker%d", mChildPid);
PlatformThread::SetName(threadName); PlatformThread::SetName(threadName);
@@ -1075,8 +1087,8 @@ void SandboxBroker::ThreadMain(void) {
} }
} }
void SandboxBroker::AuditPermissive(int aOp, int aFlags, int aPerms, void SandboxBroker::AuditPermissive(int aOp, int aFlags, uint64_t aId,
const char* aPath) { int aPerms, const char* aPath) {
MOZ_RELEASE_ASSERT(SandboxInfo::Get().Test(SandboxInfo::kPermissive)); MOZ_RELEASE_ASSERT(SandboxInfo::Get().Test(SandboxInfo::kPermissive));
struct stat statBuf; struct stat statBuf;
@@ -1090,15 +1102,21 @@ void SandboxBroker::AuditPermissive(int aOp, int aFlags, int aPerms,
"SandboxBroker: would have denied op=%s rflags=%o perms=%d path=%s for " "SandboxBroker: would have denied op=%s rflags=%o perms=%d path=%s for "
"pid=%d permissive=1; real status", "pid=%d permissive=1; real status",
OperationDescription[aOp], aFlags, aPerms, aPath, mChildPid); OperationDescription[aOp], aFlags, aPerms, aPath, mChildPid);
SandboxProfiler::ReportAudit("SandboxBroker::AuditPermissive",
OperationDescription[aOp], aFlags, aId, aPerms,
aPath, mChildPid);
} }
void SandboxBroker::AuditDenial(int aOp, int aFlags, int aPerms, void SandboxBroker::AuditDenial(int aOp, int aFlags, uint64_t aId, int aPerms,
const char* aPath) { const char* aPath) {
if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) { if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
SANDBOX_LOG( SANDBOX_LOG(
"SandboxBroker: denied op=%s rflags=%o perms=%d path=%s for pid=%d", "SandboxBroker: denied op=%s rflags=%o perms=%d path=%s for pid=%d",
OperationDescription[aOp], aFlags, aPerms, aPath, mChildPid); OperationDescription[aOp], aFlags, aPerms, aPath, mChildPid);
} }
SandboxProfiler::ReportAudit("SandboxBroker::AuditDenial",
OperationDescription[aOp], aFlags, aId, aPerms,
aPath, mChildPid);
} }
} // namespace mozilla } // namespace mozilla

View File

@@ -154,8 +154,10 @@ class SandboxBroker final : private SandboxBrokerCommon,
SandboxBroker(UniquePtr<const Policy> aPolicy, int aChildPid, int& aClientFd); SandboxBroker(UniquePtr<const Policy> aPolicy, int aChildPid, int& aClientFd);
void ThreadMain(void) override; void ThreadMain(void) override;
void AuditPermissive(int aOp, int aFlags, int aPerms, const char* aPath); void AuditPermissive(int aOp, int aFlags, uint64_t aId, int aPerms,
void AuditDenial(int aOp, int aFlags, int aPerms, const char* aPath); const char* aPath);
void AuditDenial(int aOp, int aFlags, uint64_t aId, int aPerms,
const char* aPath);
// Remap relative paths to absolute paths. // Remap relative paths to absolute paths.
size_t ConvertRelativePath(char* aPath, size_t aBufSize, size_t aPathLen); size_t ConvertRelativePath(char* aPath, size_t aBufSize, size_t aPathLen);
size_t RealPath(char* aPath, size_t aBufSize, size_t aPathLen); size_t RealPath(char* aPath, size_t aBufSize, size_t aPathLen);

View File

@@ -8,6 +8,7 @@
#define mozilla_SandboxBrokerCommon_h #define mozilla_SandboxBrokerCommon_h
#include <sys/types.h> #include <sys/types.h>
#include <stdint.h>
struct iovec; struct iovec;
@@ -48,6 +49,8 @@ class SandboxBrokerCommon {
// For open, flags; for access, "mode"; for stat, O_NOFOLLOW for lstat. // For open, flags; for access, "mode"; for stat, O_NOFOLLOW for lstat.
// For connect, the socket type. // For connect, the socket type.
int mFlags; int mFlags;
// ID to match child/parent requests in profiler
uint64_t mId;
// Size of return value buffer, if any // Size of return value buffer, if any
size_t mBufSize; size_t mBufSize;
// The rest of the packet is the pathname. // The rest of the packet is the pathname.

View File

@@ -10,6 +10,7 @@
// to be able to isolate sandbox/chromium from ipc/chromium. // to be able to isolate sandbox/chromium from ipc/chromium.
#include "SandboxInternal.h" #include "SandboxInternal.h"
#include "SandboxLogging.h" #include "SandboxLogging.h"
#include <unistd.h> #include <unistd.h>

View File

@@ -24,6 +24,7 @@ EXPORTS.mozilla += [
"Sandbox.h", "Sandbox.h",
"SandboxInfo.h", "SandboxInfo.h",
"SandboxProfiler.h", "SandboxProfiler.h",
"SandboxProfilerObserver.h",
] ]
UNIFIED_SOURCES += [ UNIFIED_SOURCES += [