Bug 1386404 - Enable content-process specific tmpdir on Linux. r=jld
MozReview-Commit-ID: 6Hijq0to9MG
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1108,24 +1108,22 @@ pref("security.sandbox.content.level", 3
// This setting may not be required anymore once we decide to permanently
// enable the content sandbox.
pref("security.sandbox.content.level", 3);
pref("security.sandbox.content.write_path_whitelist", "");
pref("security.sandbox.content.read_path_whitelist", "");
pref("security.sandbox.content.syscall_whitelist", "");
#endif
-#if defined(XP_MACOSX) || defined(XP_WIN)
#if defined(MOZ_SANDBOX) && defined(MOZ_CONTENT_SANDBOX)
// ID (a UUID when set by gecko) that is used to form the name of a
// sandbox-writable temporary directory to be used by content processes
// when a temporary writable file is required in a level 1 sandbox.
pref("security.sandbox.content.tempDirSuffix", "");
#endif
-#endif
#if defined(MOZ_SANDBOX)
// This pref determines if messages relevant to sandbox violations are
// logged.
#if defined(XP_WIN) || defined(XP_MACOSX)
pref("security.sandbox.logging.enabled", false);
#else
pref("security.sandbox.logging.enabled", true);
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -20,20 +20,18 @@
#include "MainThreadUtils.h"
#include "mozilla/Sprintf.h"
#include "prenv.h"
#include "nsXPCOMPrivate.h"
#if defined(MOZ_CONTENT_SANDBOX)
#include "mozilla/SandboxSettings.h"
-#if defined(XP_MACOSX)
#include "nsAppDirectoryServiceDefs.h"
#endif
-#endif
#include "nsExceptionHandler.h"
#include "nsDirectoryServiceDefs.h"
#include "nsIFile.h"
#include "nsPrintfCString.h"
#include "mozilla/ClearOnShutdown.h"
@@ -275,16 +273,26 @@ GeckoChildProcessHost::PrepareLaunch()
#if defined(MOZ_SANDBOX)
// For other process types we can't rely on them being launched on main
// thread and they may not have access to prefs in the child process, so allow
// them to turn on logging via an environment variable.
mEnableSandboxLogging = mEnableSandboxLogging
|| !!PR_GetEnv("MOZ_SANDBOX_LOGGING");
#endif
+#elif defined(XP_LINUX)
+ // Get and remember the path to the per-content-process tmpdir
+ if (ShouldHaveDirectoryService()) {
+ nsCOMPtr<nsIFile> contentTempDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_APP_CONTENT_PROCESS_TEMP_DIR,
+ getter_AddRefs(contentTempDir));
+ if (NS_SUCCEEDED(rv)) {
+ contentTempDir->GetNativePath(mTmpDirName);
+ }
+ }
#endif
}
#ifdef XP_WIN
void GeckoChildProcessHost::InitWindowsGroupID()
{
// On Win7+, pass the application user model to the child, so it can
// register with it. This insures windows created by the container
@@ -501,16 +509,32 @@ GeckoChildProcessHost::PerformAsyncLaunc
// `RUST_LOG_CHILD` is meant for logging child processes only.
nsAutoCString childRustLog(PR_GetEnv("RUST_LOG_CHILD"));
if (!childRustLog.IsEmpty()) {
mLaunchOptions->env_map[ENVIRONMENT_LITERAL("RUST_LOG")]
= ENVIRONMENT_STRING(childRustLog);
}
+#if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX)
+ if (!mTmpDirName.IsEmpty()) {
+ // Point a bunch of things that might want to write from content to our
+ // shiny new content-process specific tmpdir
+ mLaunchOptions->env_map[ENVIRONMENT_LITERAL("TMPDIR")] =
+ ENVIRONMENT_STRING(mTmpDirName);
+ mLaunchOptions->env_map[ENVIRONMENT_LITERAL("XDG_CACHE_HOME")] =
+ ENVIRONMENT_STRING(mTmpDirName);
+ mLaunchOptions->env_map[ENVIRONMENT_LITERAL("XDG_CACHE_DIR")] =
+ ENVIRONMENT_STRING(mTmpDirName);
+ // Partial fix for bug 1380051 (not persistent - should be)
+ mLaunchOptions->env_map[ENVIRONMENT_LITERAL("MESA_GLSL_CACHE_DIR")] =
+ ENVIRONMENT_STRING(mTmpDirName);
+ }
+#endif
+
return PerformAsyncLaunchInternal(aExtraOpts);
}
bool
GeckoChildProcessHost::RunPerformAsyncLaunch(std::vector<std::string> aExtraOpts)
{
InitializeChannel();
--- a/ipc/glue/GeckoChildProcessHost.h
+++ b/ipc/glue/GeckoChildProcessHost.h
@@ -188,16 +188,21 @@ private:
// channel, there's a small window of time in which *we* might still
// be the channel listener, and receive messages. That's bad
// because we have no idea what to do with those messages. So queue
// them here until we hand off the eventual listener.
//
// FIXME/cjones: this strongly indicates bad design. Shame on us.
std::queue<IPC::Message> mQueue;
+ // Set this up before we're called from a different thread.
+#if defined(OS_LINUX)
+ nsCString mTmpDirName;
+#endif
+
static uint32_t sNextUniqueID;
static bool sRunSelfAsContentProc;
#if defined(MOZ_WIDGET_ANDROID)
void LaunchAndroidService(const char* type,
const std::vector<std::string>& argv,
const base::file_handle_mapping_vector& fds_to_remap,
--- a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
+++ b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
@@ -183,35 +183,16 @@ SandboxBrokerPolicyFactory::SandboxBroke
{
// Policy entries that are the same in every process go here, and
// are cached over the lifetime of the factory.
#if defined(MOZ_CONTENT_SANDBOX)
SandboxBroker::Policy* policy = new SandboxBroker::Policy;
policy->AddDir(rdwrcr, "/dev/shm");
// Write permssions
//
- // Add write permissions on the temporary directory. This can come
- // from various environment variables (TMPDIR,TMP,TEMP,...) so
- // make sure to use the full logic.
- nsCOMPtr<nsIFile> tmpDir;
- nsresult rv = GetSpecialSystemDirectory(OS_TemporaryDirectory,
- getter_AddRefs(tmpDir));
-
- if (NS_SUCCEEDED(rv)) {
- nsAutoCString tmpPath;
- rv = tmpDir->GetNativePath(tmpPath);
- if (NS_SUCCEEDED(rv)) {
- policy->AddDir(rdwrcr, tmpPath.get());
- }
- }
- // If the above fails at any point, fall back to a very good guess.
- if (NS_FAILED(rv)) {
- policy->AddDir(rdwrcr, "/tmp");
- }
-
// Bug 1308851: NVIDIA proprietary driver when using WebGL
policy->AddFilePrefix(rdwr, "/dev", "nvidia");
// Bug 1312678: radeonsi/Intel with DRI when using WebGL
policy->AddDir(rdwr, "/dev/dri");
#ifdef MOZ_ALSA
// Bug 1309098: ALSA support
@@ -303,17 +284,18 @@ SandboxBrokerPolicyFactory::SandboxBroke
// access to.
mozilla::Array<const char*, 3> extraConfDirs = {
".config", // Fallback if XDG_CONFIG_PATH isn't set
".themes",
".fonts",
};
nsCOMPtr<nsIFile> homeDir;
- rv = GetSpecialSystemDirectory(Unix_HomeDirectory, getter_AddRefs(homeDir));
+ nsresult rv = GetSpecialSystemDirectory(Unix_HomeDirectory,
+ getter_AddRefs(homeDir));
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIFile> confDir;
for (const auto& dir : extraConfDirs) {
rv = homeDir->Clone(getter_AddRefs(confDir));
if (NS_SUCCEEDED(rv)) {
rv = confDir->AppendNative(nsDependentCString(dir));
if (NS_SUCCEEDED(rv)) {
@@ -458,22 +440,34 @@ SandboxBrokerPolicyFactory::GetContentPo
policy->AddPath(rdonly, nsPrintfCString("/proc/%d/statm", aPid).get());
policy->AddPath(rdonly, nsPrintfCString("/proc/%d/smaps", aPid).get());
// Bug 1384804, notably comment 15
// Used by libnuma, included by x265/ffmpeg, who falls back
// to get_mempolicy if this fails
policy->AddPath(rdonly, nsPrintfCString("/proc/%d/status", aPid).get());
+ // Add write permissions on the content process specific temporary dir.
+ nsCOMPtr<nsIFile> tmpDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_APP_CONTENT_PROCESS_TEMP_DIR,
+ getter_AddRefs(tmpDir));
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString tmpPath;
+ rv = tmpDir->GetNativePath(tmpPath);
+ if (NS_SUCCEEDED(rv)) {
+ policy->AddDir(rdwrcr, tmpPath.get());
+ }
+ }
+
// userContent.css and the extensions dir sit in the profile, which is
// normally blocked and we can't get the profile dir earlier in startup,
// so this must happen here.
nsCOMPtr<nsIFile> profileDir;
- nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
- getter_AddRefs(profileDir));
+ rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+ getter_AddRefs(profileDir));
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIFile> workDir;
rv = profileDir->Clone(getter_AddRefs(workDir));
if (NS_SUCCEEDED(rv)) {
rv = workDir->AppendNative(NS_LITERAL_CSTRING("chrome"));
if (NS_SUCCEEDED(rv)) {
rv = workDir->AppendNative(NS_LITERAL_CSTRING("userContent.css"));
if (NS_SUCCEEDED(rv)) {
--- a/security/sandbox/test/browser_content_sandbox_fs.js
+++ b/security/sandbox/test/browser_content_sandbox_fs.js
@@ -158,38 +158,33 @@ function minHomeReadSandboxLevel(level)
//
// Checks that sandboxing is enabled and at the appropriate level
// setting before triggering tests that do the file I/O.
//
// Tests attempting to write to a file in the home directory from the
// content process--expected to fail.
//
// Tests attempting to write to a file in the content temp directory
-// from the content process--expected to succeed. On Mac and Windows,
-// use "ContentTmpD", but on Linux use "TmpD" until Linux uses the
-// content temp dir key.
+// from the content process--expected to succeed. Uses "ContentTmpD".
//
// Tests reading various files and directories from file and web
// content processes.
//
add_task(async function() {
// This test is only relevant in e10s
if (!gMultiProcessBrowser) {
ok(false, "e10s is enabled");
info("e10s is not enabled, exiting");
return;
}
let level = 0;
let prefExists = true;
// Read the security.sandbox.content.level pref.
- // If the pref isn't set and we're running on Linux on !isNightly(),
- // exit without failing. The Linux content sandbox is only enabled
- // on Nightly at this time.
// eslint-disable-next-line mozilla/use-default-preference-values
try {
level = Services.prefs.getIntPref("security.sandbox.content.level");
} catch (e) {
prefExists = false;
}
ok(prefExists, "pref security.sandbox.content.level exists");
@@ -371,30 +366,25 @@ async function testFileAccess() {
browser: webBrowser, // browser to run test in
file: fontFile, // nsIFile object
minLevel: minHomeReadSandboxLevel(), // min level to enable test
func: readFile, // the test function to use
});
}
}
- // The Linux test runners create the temporary profile in the same
- // system temp dir we give write access to, so this gives a false
- // positive.
let profileDir = GetProfileDir();
- if (!isLinux()) {
- tests.push({
- desc: "profile dir", // description
- ok: false, // expected to succeed?
- browser: webBrowser, // browser to run test in
- file: profileDir, // nsIFile object
- minLevel: minProfileReadSandboxLevel(), // min level to enable test
- func: readDir,
- });
- }
+ tests.push({
+ desc: "profile dir", // description
+ ok: false, // expected to succeed?
+ browser: webBrowser, // browser to run test in
+ file: profileDir, // nsIFile object
+ minLevel: minProfileReadSandboxLevel(), // min level to enable test
+ func: readDir,
+ });
if (fileContentProcessEnabled) {
tests.push({
desc: "profile dir",
ok: true,
browser: fileBrowser,
file: profileDir,
minLevel: 0,
func: readDir,
@@ -646,28 +636,24 @@ async function testFileAccess() {
func: readDir,
});
} else {
ok(false, `${chromeDir.path} is valid dir`);
}
let cookiesFile = GetProfileEntry("cookies.sqlite");
if (cookiesFile.exists() && !cookiesFile.isDirectory()) {
- // On Linux, the temporary profile used for tests is in the system
- // temp dir which content has read access to, so this test fails.
- if (!isLinux()) {
- tests.push({
- desc: "cookies file",
- ok: false,
- browser: webBrowser,
- file: cookiesFile,
- minLevel: minProfileReadSandboxLevel(),
- func: readFile,
- });
- }
+ tests.push({
+ desc: "cookies file",
+ ok: false,
+ browser: webBrowser,
+ file: cookiesFile,
+ minLevel: minProfileReadSandboxLevel(),
+ func: readFile,
+ });
if (fileContentProcessEnabled) {
tests.push({
desc: "cookies file",
ok: true,
browser: fileBrowser,
file: cookiesFile,
minLevel: 0,
func: readFile,
--- a/security/sandbox/test/browser_content_sandbox_utils.js
+++ b/security/sandbox/test/browser_content_sandbox_utils.js
@@ -36,20 +36,16 @@ function fileInHomeDir() {
homeFile.appendRelativePath(uuid());
Assert.ok(!homeFile.exists(), homeFile.path + " does not exist");
return (homeFile);
}
// Returns a file object for a new file in the content temp dir (.../<UUID>).
function fileInTempDir() {
let contentTempKey = "ContentTmpD";
- if (Services.appinfo.OS == "Linux") {
- // Linux builds don't use the content-specific temp key
- contentTempKey = "TmpD";
- }
// get the content temp dir, make sure it exists
let ctmp = Services.dirsvc.get(contentTempKey, Ci.nsIFile);
Assert.ok(ctmp.exists(), "Content temp dir exists");
Assert.ok(ctmp.isDirectory(), "Content temp dir is a directory");
// build a file object for a new file in content temp
let tempFile = ctmp.clone();
--- a/toolkit/xre/nsXREDirProvider.cpp
+++ b/toolkit/xre/nsXREDirProvider.cpp
@@ -61,36 +61,34 @@
#include <ctype.h>
#endif
#ifdef XP_IOS
#include "UIKitDirProvider.h"
#endif
#if defined(MOZ_CONTENT_SANDBOX)
#include "mozilla/SandboxSettings.h"
-#if (defined(XP_WIN) || defined(XP_MACOSX))
#include "nsIUUIDGenerator.h"
#include "mozilla/Unused.h"
#if defined(XP_WIN)
#include "WinUtils.h"
#endif
#endif
-#endif
#if defined(XP_MACOSX)
#define APP_REGISTRY_NAME "Application Registry"
#elif defined(XP_WIN)
#define APP_REGISTRY_NAME "registry.dat"
#else
#define APP_REGISTRY_NAME "appreg"
#endif
#define PREF_OVERRIDE_DIRNAME "preferences"
-#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+#if defined(MOZ_CONTENT_SANDBOX)
static already_AddRefed<nsIFile> GetContentProcessSandboxTempDir();
static nsresult DeleteDirIfExists(nsIFile *dir);
static bool IsContentSandboxDisabled();
static const char* GetContentProcessTempBaseDirKey();
static already_AddRefed<nsIFile> CreateContentProcessSandboxTempDir();
#endif
nsXREDirProvider* gDirServiceProvider = nullptr;
@@ -494,17 +492,17 @@ nsXREDirProvider::GetFile(const char* aP
}
else if (!strcmp(aProperty, XRE_ADDON_APP_DIR)) {
nsCOMPtr<nsIDirectoryServiceProvider> dirsvc(do_GetService("@mozilla.org/file/directory_service;1", &rv));
if (NS_FAILED(rv))
return rv;
bool unused;
rv = dirsvc->GetFile("XCurProcD", &unused, getter_AddRefs(file));
}
-#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+#if defined(MOZ_CONTENT_SANDBOX)
else if (!strcmp(aProperty, NS_APP_CONTENT_PROCESS_TEMP_DIR)) {
if (!mContentTempDir && NS_FAILED((rv = LoadContentProcessTempDir()))) {
return rv;
}
rv = mContentTempDir->Clone(getter_AddRefs(file));
}
#endif // defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
else if (NS_SUCCEEDED(GetProfileStartupDir(getter_AddRefs(file)))) {
@@ -654,17 +652,17 @@ nsXREDirProvider::GetFiles(const char* a
rv = NS_NewUnionEnumerator(aResult, appEnum, xreEnum);
if (NS_FAILED(rv))
return rv;
return NS_SUCCESS_AGGREGATE_RESULT;
}
-#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+#if defined(MOZ_CONTENT_SANDBOX)
static const char*
GetContentProcessTempBaseDirKey()
{
#if defined(XP_WIN)
return NS_WIN_LOW_INTEGRITY_TEMP_BASE;
#else
return NS_OS_TEMP_DIR;
@@ -776,21 +774,26 @@ CreateContentProcessSandboxTempDir()
nsID uuid;
rv = uuidgen->GenerateUUIDInPlace(&uuid);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
char uuidChars[NSID_LENGTH];
uuid.ToProvidedString(uuidChars);
- tempDirSuffix.AssignASCII(uuidChars);
+ tempDirSuffix.AssignASCII(uuidChars, NSID_LENGTH);
+#ifdef XP_UNIX
+ // Braces in a path are somewhat annoying to deal with
+ // and pretty alien on Unix
+ tempDirSuffix.StripChars(u"{}");
+#endif
// Save the pref
- rv = Preferences::SetCString("security.sandbox.content.tempDirSuffix",
- uuidChars);
+ rv = Preferences::SetString("security.sandbox.content.tempDirSuffix",
+ tempDirSuffix);
if (NS_WARN_IF(NS_FAILED(rv))) {
// If we fail to save the pref we don't want to create the temp dir,
// because we won't be able to clean it up later.
return nullptr;
}
nsCOMPtr<nsIPrefService> prefsvc = Preferences::GetService();
if (!prefsvc || NS_FAILED((rv = prefsvc->SavePrefFile(nullptr)))) {
@@ -837,18 +840,17 @@ DeleteDirIfExists(nsIFile* dir)
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND &&
rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
return rv;
}
}
return NS_OK;
}
-#endif // (defined(XP_WIN) || defined(XP_MACOSX)) &&
- // defined(MOZ_CONTENT_SANDBOX)
+#endif // defined(MOZ_CONTENT_SANDBOX)
static const char *const kAppendPrefDir[] = { "defaults", "preferences", nullptr };
#ifdef DEBUG_bsmedberg
static void
DumpFileArray(const char *key,
nsCOMArray<nsIFile> dirs)
{
@@ -1062,17 +1064,17 @@ nsXREDirProvider::DoStartup()
}
mozilla::Telemetry::Accumulate(mozilla::Telemetry::NUMBER_OF_PROFILES,
count);
}
obsSvc->NotifyObservers(nullptr, "profile-initial-state", nullptr);
-#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+#if defined(MOZ_CONTENT_SANDBOX)
// Makes sure the content temp dir has been loaded if it hasn't been
// already. In the parent this ensures it has been created before we attempt
// to start any content processes.
if (!mContentTempDir) {
mozilla::Unused << NS_WARN_IF(NS_FAILED(LoadContentProcessTempDir()));
}
#endif
}
@@ -1102,17 +1104,17 @@ nsXREDirProvider::DoShutdown()
obsSvc->NotifyObservers(nullptr, "profile-before-change", kShutdownPersist);
obsSvc->NotifyObservers(nullptr, "profile-before-change-qm", kShutdownPersist);
obsSvc->NotifyObservers(nullptr, "profile-before-change-telemetry", kShutdownPersist);
}
mProfileNotified = false;
}
-#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+#if defined(MOZ_CONTENT_SANDBOX)
if (XRE_IsParentProcess()) {
Unused << DeleteDirIfExists(mContentProcessSandboxTempDir);
}
#endif
}
#ifdef XP_WIN
static nsresult
--- a/toolkit/xre/nsXREDirProvider.h
+++ b/toolkit/xre/nsXREDirProvider.h
@@ -120,17 +120,17 @@ protected:
static nsresult AppendSysUserExtensionPath(nsIFile* aFile);
static nsresult AppendSysUserExtensionsDevPath(nsIFile* aFile);
// Internal helper that splits a path into components using the '/' and '\\'
// delimiters.
static inline nsresult AppendProfileString(nsIFile* aFile, const char* aPath);
-#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+#if defined(MOZ_CONTENT_SANDBOX)
// Load the temp directory for sandboxed content processes
nsresult LoadContentProcessTempDir();
#endif
void Append(nsIFile* aDirectory);
nsCOMPtr<nsIDirectoryServiceProvider> mAppProvider;
// On OSX, mGREDir points to .app/Contents/Resources
@@ -138,16 +138,16 @@ protected:
// On OSX, mGREBinDir points to .app/Contents/MacOS
nsCOMPtr<nsIFile> mGREBinDir;
// On OSX, mXULAppDir points to .app/Contents/Resources/browser
nsCOMPtr<nsIFile> mXULAppDir;
nsCOMPtr<nsIFile> mProfileDir;
nsCOMPtr<nsIFile> mProfileLocalDir;
bool mProfileNotified;
bool mPrefsInitialized = false;
-#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+#if defined(MOZ_CONTENT_SANDBOX)
nsCOMPtr<nsIFile> mContentTempDir;
nsCOMPtr<nsIFile> mContentProcessSandboxTempDir;
#endif
nsCOMArray<nsIFile> mAppBundleDirectories;
};
#endif
--- a/xpcom/io/nsAppDirectoryServiceDefs.h
+++ b/xpcom/io/nsAppDirectoryServiceDefs.h
@@ -71,17 +71,17 @@
#define NS_APP_CACHE_PARENT_DIR "cachePDir"
#define NS_APP_INSTALL_CLEANUP_DIR "XPIClnupD" //location of xpicleanup.dat xpicleanup.exe
#define NS_APP_INDEXEDDB_PARENT_DIR "indexedDBPDir"
#define NS_APP_PERMISSION_PARENT_DIR "permissionDBPDir"
-#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+#if defined(MOZ_CONTENT_SANDBOX)
//
// NS_APP_CONTENT_PROCESS_TEMP_DIR refers to a directory that is read and
// write accessible from a sandboxed content process. The key may be used in
// either process, but the directory is intended to be used for short-lived
// files that need to be saved to the filesystem by the content process and
// don't need to survive browser restarts. The directory is reset on startup.
// The key is only valid when MOZ_CONTENT_SANDBOX is defined. When
// MOZ_CONTENT_SANDBOX is defined, the directory the key refers to differs