Bug 1270018 - NS_APP_CONTENT_PROCESS_TEMP_DIR should only return the sandbox writeable temp. r=bobowen, r=bsmedberg
authorHaik Aftandilian <haftandilian@mozilla.com>
Tue, 05 Jul 2016 14:48:25 -0700
changeset 304163 5136dcccfe4a1fed96f55e1d65102adc44123a61
parent 304162 2763206148aa397d21a6e2649125870e2aab2207
child 304164 90e2eea25b34af97d380094143f8c4bd421dc223
push id30414
push usercbook@mozilla.com
push dateFri, 08 Jul 2016 09:59:01 +0000
treeherdermozilla-central@45682df2d2d4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbobowen, bsmedberg
bugs1270018
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1270018 - NS_APP_CONTENT_PROCESS_TEMP_DIR should only return the sandbox writeable temp. r=bobowen, r=bsmedberg MozReview-Commit-ID: 5L9zCR4ExWD
toolkit/xre/nsAppRunner.cpp
toolkit/xre/nsXREDirProvider.cpp
toolkit/xre/nsXREDirProvider.h
xpcom/io/nsAppDirectoryServiceDefs.h
--- 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();
 }
@@ -721,61 +734,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) {
@@ -1051,26 +1179,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___