Bug 1270018 - NS_APP_CONTENT_PROCESS_TEMP_DIR should only return the sandbox writeable temp; r=bobowen r=bsmedberg
MozReview-Commit-ID: 5L9zCR4ExWD
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -605,153 +605,16 @@ ProcessDDE(nsINativeAppSupport* aNative,
* @return true in all environments
*/
static bool
CanShowProfileManager()
{
return true;
}
-#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
-static already_AddRefed<nsIFile>
-GetAndCleanTempDir()
-{
- // Get the directory within which we'll place the
- // sandbox-writable temp directory
- nsCOMPtr<nsIFile> tempDir;
- nsresult rv = NS_GetSpecialDirectory(NS_APP_CONTENT_PROCESS_TEMP_DIR,
- getter_AddRefs(tempDir));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return nullptr;
- }
-
- // If the NS_APP_CONTENT_PROCESS_TEMP_DIR is the real temp directory, don't
- // attempt to delete it.
- nsCOMPtr<nsIFile> realTempDir;
- rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(realTempDir));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return nullptr;
- }
- bool isRealTemp;
- rv = tempDir->Equals(realTempDir, &isRealTemp);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return nullptr;
- }
- if (isRealTemp) {
- return tempDir.forget();
- }
-
- // Don't return an error if the directory doesn't exist.
- // Windows Remove() returns NS_ERROR_FILE_NOT_FOUND while
- // OS X returns NS_ERROR_FILE_TARGET_DOES_NOT_EXIST.
- rv = tempDir->Remove(/* aRecursive */ true);
- if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND &&
- rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
- NS_WARNING("Failed to delete temp directory.");
- return nullptr;
- }
-
- return tempDir.forget();
-}
-
-static void
-SetUpSandboxEnvironment()
-{
- // Setup a sandbox-writable temp directory. i.e., a directory
- // that is writable by a sandboxed content process. This
- // only applies when e10s is enabled, depending on the platform
- // and setting of security.sandbox.content.level.
- if (!BrowserTabsRemoteAutostart()) {
- return;
- }
-
-#if defined(XP_WIN)
- // For Windows, the temp dir only makes sense for Vista and later
- // with a sandbox pref level >= 1
- if (!IsVistaOrLater() ||
- (Preferences::GetInt("security.sandbox.content.level") < 1)) {
- return;
- }
-#endif
-
-#if defined(XP_MACOSX)
- // For OSX, we just require sandbox pref level >= 1.
- if (Preferences::GetInt("security.sandbox.content.level") < 1) {
- return;
- }
-#endif
-
- // Get (and create if blank) temp directory suffix pref.
- nsresult rv;
- nsAdoptingString tempDirSuffix =
- Preferences::GetString("security.sandbox.content.tempDirSuffix");
- if (tempDirSuffix.IsEmpty()) {
- nsCOMPtr<nsIUUIDGenerator> uuidgen =
- do_GetService("@mozilla.org/uuid-generator;1", &rv);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return;
- }
-
- nsID uuid;
- rv = uuidgen->GenerateUUIDInPlace(&uuid);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return;
- }
-
- char uuidChars[NSID_LENGTH];
- uuid.ToProvidedString(uuidChars);
- tempDirSuffix.AssignASCII(uuidChars);
-
- // Save the pref to be picked up later.
- rv = Preferences::SetCString("security.sandbox.content.tempDirSuffix",
- uuidChars);
- 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;
- }
-
- nsCOMPtr<nsIPrefService> prefsvc = Preferences::GetService();
- if (!prefsvc || NS_FAILED(prefsvc->SavePrefFile(nullptr))) {
- // Again, if we fail to save the pref file we might not be able to clean
- // up the temp directory, so don't create one.
- NS_WARNING("Failed to save pref file, cannot create temp dir.");
- return;
- }
- }
-
- // Get (and clean up if still there) the sandbox-writable temp directory.
- nsCOMPtr<nsIFile> tempDir = GetAndCleanTempDir();
- if (!tempDir) {
- NS_WARNING("Failed to get or clean sandboxed temp directory.");
- return;
- }
-
- rv = tempDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return;
- }
-}
-
-static void
-CleanUpSandboxEnvironment()
-{
-#if defined(XP_WIN)
- // We can't have created the temp directory before Vista.
- if (!IsVistaOrLater()) {
- return;
- }
-#endif
-
- // Get and remove the sandbox-writable temp directory.
- // This function already warns if the deletion fails.
- nsCOMPtr<nsIFile> tempDir = GetAndCleanTempDir();
-}
-#endif
-
bool gSafeMode = false;
/**
* The nsXULAppInfo object implements nsIFactory so that it can be its own
* singleton.
*/
class nsXULAppInfo : public nsIXULAppInfo,
public nsIObserver,
@@ -4377,32 +4240,25 @@ XREMain::XRE_mainRun()
}
#endif /* MOZ_INSTRUMENT_EVENT_LOOP */
#if defined(MOZ_SANDBOX) && defined(XP_LINUX) && !defined(ANDROID)
// If we're on Linux, we now have information about the OS capabilities
// available to us.
SandboxInfo::SubmitTelemetry();
#endif
-#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
- SetUpSandboxEnvironment();
-#endif
{
rv = appStartup->Run();
if (NS_FAILED(rv)) {
NS_ERROR("failed to run appstartup");
gLogConsoleErrors = true;
}
}
-#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
- CleanUpSandboxEnvironment();
-#endif
-
return rv;
}
#if MOZ_WIDGET_GTK == 2
void XRE_GlibInit()
{
static bool ran_once = false;
--- a/toolkit/xre/nsXREDirProvider.cpp
+++ b/toolkit/xre/nsXREDirProvider.cpp
@@ -55,26 +55,39 @@
#endif
#ifdef XP_UNIX
#include <ctype.h>
#endif
#ifdef XP_IOS
#include "UIKitDirProvider.h"
#endif
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+#include "nsIUUIDGenerator.h"
+#include "mozilla/unused.h"
+#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)
+static already_AddRefed<nsIFile> GetContentProcessSandboxTempDir();
+static nsresult DeleteDirIfExists(nsIFile *dir);
+static bool IsContentSandboxDisabled();
+static const char* GetContentProcessTempBaseDirKey();
+static already_AddRefed<nsIFile> CreateContentProcessSandboxTempDir();
+#endif
+
static already_AddRefed<nsIFile>
CloneAndAppend(nsIFile* aFile, const char* name)
{
nsCOMPtr<nsIFile> file;
aFile->Clone(getter_AddRefs(file));
file->AppendNative(nsDependentCString(name));
return file.forget();
}
@@ -718,61 +731,176 @@ GetContentProcessTempBaseDirKey()
{
#if defined(XP_WIN)
return NS_WIN_LOW_INTEGRITY_TEMP_BASE;
#else
return NS_OS_TEMP_DIR;
#endif
}
+//
+// Sets mContentTempDir so that it refers to the appropriate temp dir.
+// If the sandbox is enabled, NS_APP_CONTENT_PROCESS_TEMP_DIR, otherwise
+// NS_OS_TEMP_DIR is used.
+//
nsresult
nsXREDirProvider::LoadContentProcessTempDir()
{
+ mContentTempDir = GetContentProcessSandboxTempDir();
+ if (mContentTempDir) {
+ return NS_OK;
+ } else {
+ return NS_GetSpecialDirectory(NS_OS_TEMP_DIR,
+ getter_AddRefs(mContentTempDir));
+ }
+}
+
+static bool
+IsContentSandboxDisabled()
+{
+ if (!BrowserTabsRemoteAutostart()) {
+ return false;
+ }
#if defined(XP_WIN)
const bool isSandboxDisabled = !mozilla::IsVistaOrLater() ||
(Preferences::GetInt("security.sandbox.content.level") < 1);
#elif defined(XP_MACOSX)
const bool isSandboxDisabled =
Preferences::GetInt("security.sandbox.content.level") < 1;
#endif
+ return isSandboxDisabled;
+}
- if (isSandboxDisabled) {
- // Just use the normal temp directory if sandboxing is turned off
- return NS_GetSpecialDirectory(NS_OS_TEMP_DIR,
- getter_AddRefs(mContentTempDir));
+//
+// If a content process sandbox temp dir is to be used, returns an nsIFile
+// for the directory. Returns null if the content sandbox is disabled or
+// an error occurs.
+//
+static already_AddRefed<nsIFile>
+GetContentProcessSandboxTempDir()
+{
+ if (IsContentSandboxDisabled()) {
+ return nullptr;
}
nsCOMPtr<nsIFile> localFile;
nsresult rv = NS_GetSpecialDirectory(GetContentProcessTempBaseDirKey(),
getter_AddRefs(localFile));
if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
+ return nullptr;
}
nsAutoString tempDirSuffix;
rv = Preferences::GetString("security.sandbox.content.tempDirSuffix",
&tempDirSuffix);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (tempDirSuffix.IsEmpty()) {
- return NS_ERROR_NOT_AVAILABLE;
+ if (NS_WARN_IF(NS_FAILED(rv)) || tempDirSuffix.IsEmpty()) {
+ return nullptr;
}
rv = localFile->Append(NS_LITERAL_STRING("Temp-") + tempDirSuffix);
if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
+ return nullptr;
+ }
+
+ return localFile.forget();
+}
+
+//
+// Create a temporary directory for use from sandboxed content processes.
+// Only called in the parent. The path is derived from a UUID stored in a
+// pref which is available to content processes. Returns null if the
+// content sandbox is disabled or if an error occurs.
+//
+static already_AddRefed<nsIFile>
+CreateContentProcessSandboxTempDir()
+{
+ if (IsContentSandboxDisabled()) {
+ return nullptr;
}
- localFile.swap(mContentTempDir);
+ // Get (and create if blank) temp directory suffix pref.
+ nsresult rv;
+ nsAdoptingString tempDirSuffix =
+ Preferences::GetString("security.sandbox.content.tempDirSuffix");
+ if (tempDirSuffix.IsEmpty()) {
+ nsCOMPtr<nsIUUIDGenerator> uuidgen =
+ do_GetService("@mozilla.org/uuid-generator;1", &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+
+ 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);
+
+ // Save the pref
+ rv = Preferences::SetCString("security.sandbox.content.tempDirSuffix",
+ uuidChars);
+ 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)))) {
+ // Again, if we fail to save the pref file we might not be able to clean
+ // up the temp directory, so don't create one.
+ NS_WARNING("Failed to save pref file, cannot create temp dir.");
+ return nullptr;
+ }
+ }
+
+ nsCOMPtr<nsIFile> sandboxTempDir = GetContentProcessSandboxTempDir();
+ if (!sandboxTempDir) {
+ NS_WARNING("Failed to determine sandbox temp dir path.");
+ return nullptr;
+ }
+
+ // Remove the directory. It may exist due to a previous crash.
+ if (NS_FAILED(DeleteDirIfExists(sandboxTempDir))) {
+ NS_WARNING("Failed to reset sandbox temp dir.");
+ return nullptr;
+ }
+
+ // Create the directory
+ rv = sandboxTempDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to create sandbox temp dir.");
+ return nullptr;
+ }
+
+ return sandboxTempDir.forget();
+}
+
+static nsresult
+DeleteDirIfExists(nsIFile* dir)
+{
+ if (dir) {
+ // Don't return an error if the directory doesn't exist.
+ // Windows Remove() returns NS_ERROR_FILE_NOT_FOUND while
+ // OS X returns NS_ERROR_FILE_TARGET_DOES_NOT_EXIST.
+ nsresult rv = dir->Remove(/* aRecursive */ true);
+ 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(MOZ_CONTENT_SANDBOX)
+#endif // (defined(XP_WIN) || defined(XP_MACOSX)) &&
+ // defined(MOZ_CONTENT_SANDBOX)
void
nsXREDirProvider::LoadExtensionBundleDirectories()
{
if (!mozilla::Preferences::GetBool("extensions.defaultProviders.enabled", true))
return;
if (mProfileDir) {
@@ -1048,26 +1176,40 @@ nsXREDirProvider::DoStartup()
if (safeModeNecessary)
mode = 3;
else
mode = 2;
}
mozilla::Telemetry::Accumulate(mozilla::Telemetry::SAFE_MODE_USAGE, mode);
obsSvc->NotifyObservers(nullptr, "profile-initial-state", nullptr);
+
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+ // The parent is responsible for creating the sandbox temp dir
+ if (XRE_IsParentProcess()) {
+ mContentProcessSandboxTempDir = CreateContentProcessSandboxTempDir();
+ mContentTempDir = mContentProcessSandboxTempDir;
+ }
+#endif
}
return NS_OK;
}
void
nsXREDirProvider::DoShutdown()
{
PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
if (mProfileNotified) {
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+ if (XRE_IsParentProcess()) {
+ Unused << DeleteDirIfExists(mContentProcessSandboxTempDir);
+ }
+#endif
+
nsCOMPtr<nsIObserverService> obsSvc =
mozilla::services::GetObserverService();
NS_ASSERTION(obsSvc, "No observer service?");
if (obsSvc) {
static const char16_t kShutdownPersist[] = MOZ_UTF16("shutdown-persist");
obsSvc->NotifyObservers(nullptr, "profile-change-net-teardown", kShutdownPersist);
obsSvc->NotifyObservers(nullptr, "profile-change-teardown", kShutdownPersist);
--- a/toolkit/xre/nsXREDirProvider.h
+++ b/toolkit/xre/nsXREDirProvider.h
@@ -143,15 +143,16 @@ protected:
nsCOMPtr<nsIFile> mGREBinDir;
// On OSX, mXULAppDir points to .app/Contents/Resources/browser
nsCOMPtr<nsIFile> mXULAppDir;
nsCOMPtr<nsIFile> mProfileDir;
nsCOMPtr<nsIFile> mProfileLocalDir;
bool mProfileNotified;
#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
nsCOMPtr<nsIFile> mContentTempDir;
+ nsCOMPtr<nsIFile> mContentProcessSandboxTempDir;
#endif
nsCOMArray<nsIFile> mAppBundleDirectories;
nsCOMArray<nsIFile> mExtensionDirectories;
nsCOMArray<nsIFile> mThemeDirectories;
};
#endif
--- a/xpcom/io/nsAppDirectoryServiceDefs.h
+++ b/xpcom/io/nsAppDirectoryServiceDefs.h
@@ -80,12 +80,36 @@
#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)
+//
+// 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
+// depending on whether or not content sandboxing is enabled.
+//
+// When MOZ_CONTENT_SANDBOX is defined and sandboxing is enabled (versus
+// manually disabled via prefs), the content process replaces NS_OS_TEMP_DIR
+// with NS_APP_CONTENT_PROCESS_TEMP_DIR so that legacy code in content
+// attempting to write to NS_OS_TEMP_DIR will write to
+// NS_APP_CONTENT_PROCESS_TEMP_DIR instead. When MOZ_CONTENT_SANDBOX is
+// defined but sandboxing is disabled, NS_APP_CONTENT_PROCESS_TEMP_DIR
+// falls back to NS_OS_TEMP_DIR in both content and chrome processes.
+//
+// New code should avoid writing to the filesystem from the content process
+// and should instead proxy through the parent process whenever possible.
+//
+// At present, all sandboxed content processes use the same directory for
+// NS_APP_CONTENT_PROCESS_TEMP_DIR, but that should not be relied upon.
+//
#define NS_APP_CONTENT_PROCESS_TEMP_DIR "ContentTmpD"
#endif // (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
#endif // nsAppDirectoryServiceDefs_h___