Backed out 4 changesets (bug 1386404) for mass failures, e.g. in browser-chrome's dom/tests/browser/browser_xhr_sandbox.js. r=backout on a CLOSED TREE
authorSebastian Hengst <archaeopteryx@coole-files.de>
Mon, 30 Oct 2017 19:10:01 +0100
changeset 389070 27a4ccb808ea959aba8837a13ef89bcfffd19598
parent 389069 da6b544b9891b2998db542fc881e5a9e133dd554
child 389071 265a168b4e3739f3bec3b06b1ad8bfc457f87165
push id32777
push userarchaeopteryx@coole-files.de
push dateMon, 30 Oct 2017 22:44:45 +0000
treeherdermozilla-central@dd0f265a1300 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1386404
milestone58.0a1
backs out36556e1a5ac7629336c789f006be524030b45765
b136f90dc49f8c34b44246d8e3e4916bc5c5c24a
4600c2d575f9fdd3168942edde1bc01d2874c460
c2c40e4d9815fb3ab65543071a2d891dcd142bc9
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
Backed out 4 changesets (bug 1386404) for mass failures, e.g. in browser-chrome's dom/tests/browser/browser_xhr_sandbox.js. r=backout on a CLOSED TREE Backed out changeset 36556e1a5ac7 (bug 1386404) Backed out changeset b136f90dc49f (bug 1386404) Backed out changeset 4600c2d575f9 (bug 1386404) Backed out changeset c2c40e4d9815 (bug 1386404)
browser/app/profile/firefox.js
ipc/glue/GeckoChildProcessHost.cpp
ipc/glue/GeckoChildProcessHost.h
security/sandbox/linux/broker/SandboxBroker.cpp
security/sandbox/linux/broker/SandboxBroker.h
security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
security/sandbox/test/browser_content_sandbox_fs.js
security/sandbox/test/browser_content_sandbox_utils.js
toolkit/xre/nsXREDirProvider.cpp
toolkit/xre/nsXREDirProvider.h
xpcom/io/nsAppDirectoryServiceDefs.h
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1111,22 +1111,24 @@ 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,30 +20,31 @@
 
 #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"
 #include "mozilla/ipc/BrowserProcessSubThread.h"
 #include "mozilla/Omnijar.h"
 #include "mozilla/Telemetry.h"
-#include "mozilla/Maybe.h"
 #include "ProtocolUtils.h"
 #include <sys/stat.h>
 
 #ifdef XP_WIN
 #include "nsIWinTaskbar.h"
 #include <stdlib.h>
 #define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
 
@@ -431,23 +432,25 @@ GeckoChildProcessHost::SetAlreadyDead()
   }
 
   mChildProcessHandle = 0;
 }
 
 int32_t GeckoChildProcessHost::mChildCounter = 0;
 
 void
-GeckoChildProcessHost::GetChildLogName(const char* origLogName,
+GeckoChildProcessHost::SetChildLogName(const char* varName, const char* origLogName,
                                        nsACString &buffer)
 {
   // We currently have no portable way to launch child with environment
   // different than parent.  So temporarily change NSPR_LOG_FILE so child
   // inherits value we want it to have. (NSPR only looks at NSPR_LOG_FILE at
   // startup, so it's 'safe' to play with the parent's environment this way.)
+  buffer.Assign(varName);
+
 #ifdef XP_WIN
   // On Windows we must expand relative paths because sandboxing rules
   // bound only to full paths.  fopen fowards to NtCreateFile which checks
   // the path against the sanboxing rules as passed to fopen (left relative).
   char absPath[MAX_PATH + 2];
   if (_fullpath(absPath, origLogName, sizeof(absPath))) {
 #ifdef MOZ_SANDBOX
     // We need to make sure the child log name doesn't contain any junction
@@ -464,108 +467,85 @@ GeckoChildProcessHost::GetChildLogName(c
 #endif
   {
     buffer.Append(origLogName);
   }
 
   // Append child-specific postfix to name
   buffer.AppendLiteral(".child-");
   buffer.AppendInt(mChildCounter);
+
+  // Passing temporary to PR_SetEnv is ok here if we keep the temporary
+  // for the time we launch the sub-process.  It's copied to the new
+  // environment.
+  PR_SetEnv(buffer.BeginReading());
 }
 
-class AutoSetAndRestoreEnvVarForChildProcess {
-public:
-  AutoSetAndRestoreEnvVarForChildProcess(const char* envVar,
-                                         const char* newVal) {
-    const char* origVal = PR_GetEnv(envVar);
-    mSetString.Assign(envVar);
-    mSetString.Append('=');
-    mRestoreString.Assign(mSetString);
-
-    mSetString.Append(newVal);
-    mRestoreString.Append(origVal);
-
-    // Passing to PR_SetEnv is ok here if we keep the the storage alive
-    // for the time we launch the sub-process.  It's copied to the new
-    // environment by PR_DuplicateEnvironment()
-    PR_SetEnv(mSetString.get());
-  }
-  // Delegate helper
-  AutoSetAndRestoreEnvVarForChildProcess(const char* envVar,
-                                         nsCString& newVal)
-    : AutoSetAndRestoreEnvVarForChildProcess(envVar, newVal.get()) {}
-  ~AutoSetAndRestoreEnvVarForChildProcess() {
-    PR_SetEnv(mRestoreString.get());
-  }
-private:
-  nsAutoCString mSetString;
-  nsAutoCString mRestoreString;
-};
-
 bool
 GeckoChildProcessHost::PerformAsyncLaunch(std::vector<std::string> aExtraOpts)
 {
 #ifdef MOZ_GECKO_PROFILER
   AutoSetProfilerEnvVarsForChildProcess profilerEnvironment;
 #endif
+
+  const char* origNSPRLogName = PR_GetEnv("NSPR_LOG_FILE");
+  const char* origMozLogName = PR_GetEnv("MOZ_LOG_FILE");
+  const char* origRustLog = PR_GetEnv("RUST_LOG");
+  const char* childRustLog = PR_GetEnv("RUST_LOG_CHILD");
+
   // - Note: this code is not called re-entrantly, nor are restoreOrig*LogName
   //   or mChildCounter touched by any other thread, so this is safe.
   ++mChildCounter;
 
   // Must keep these on the same stack where from we call PerformAsyncLaunchInternal
   // so that PR_DuplicateEnvironment() still sees a valid memory.
-  Maybe<AutoSetAndRestoreEnvVarForChildProcess> nsprLogDir;
-  Maybe<AutoSetAndRestoreEnvVarForChildProcess> mozLogDir;
-  Maybe<AutoSetAndRestoreEnvVarForChildProcess> rustLogDir;
-
-  const char* origNSPRLogName = PR_GetEnv("NSPR_LOG_FILE");
-  const char* origMozLogName = PR_GetEnv("MOZ_LOG_FILE");
+  nsAutoCString nsprLogName;
+  nsAutoCString mozLogName;
+  nsAutoCString rustLog;
 
   if (origNSPRLogName) {
-    nsAutoCString nsprLogName;
-    GetChildLogName(origNSPRLogName, nsprLogName);
-    nsprLogDir.emplace("NSPR_LOG_FILE", nsprLogName);
+    if (mRestoreOrigNSPRLogName.IsEmpty()) {
+      mRestoreOrigNSPRLogName.AssignLiteral("NSPR_LOG_FILE=");
+      mRestoreOrigNSPRLogName.Append(origNSPRLogName);
+    }
+    SetChildLogName("NSPR_LOG_FILE=", origNSPRLogName, nsprLogName);
   }
   if (origMozLogName) {
-    nsAutoCString mozLogName;
-    GetChildLogName(origMozLogName, mozLogName);
-    mozLogDir.emplace("MOZ_LOG_FILE", mozLogName);
+    if (mRestoreOrigMozLogName.IsEmpty()) {
+      mRestoreOrigMozLogName.AssignLiteral("MOZ_LOG_FILE=");
+      mRestoreOrigMozLogName.Append(origMozLogName);
+    }
+    SetChildLogName("MOZ_LOG_FILE=", origMozLogName, mozLogName);
   }
 
   // `RUST_LOG_CHILD` is meant for logging child processes only.
-  const char* childRustLog = PR_GetEnv("RUST_LOG_CHILD");
   if (childRustLog) {
-    rustLogDir.emplace("RUST_LOG", childRustLog);
+    if (mRestoreOrigRustLog.IsEmpty()) {
+      mRestoreOrigRustLog.AssignLiteral("RUST_LOG=");
+      mRestoreOrigRustLog.Append(origRustLog);
+    }
+    rustLog.AssignLiteral("RUST_LOG=");
+    rustLog.Append(childRustLog);
+    PR_SetEnv(rustLog.get());
   }
 
-#if defined(MOZ_CONTENT_SANDBOX)
-  Maybe<AutoSetAndRestoreEnvVarForChildProcess> tmpDir;
-  Maybe<AutoSetAndRestoreEnvVarForChildProcess> xdgCacheHome;
-  Maybe<AutoSetAndRestoreEnvVarForChildProcess> xdgCacheDir;
-  Maybe<AutoSetAndRestoreEnvVarForChildProcess> mesaCacheDir;
+  bool retval = PerformAsyncLaunchInternal(aExtraOpts);
 
-  nsAutoCString tmpDirName;
-  nsCOMPtr<nsIFile> mContentTempDir;
-  nsresult rv = NS_GetSpecialDirectory(NS_APP_CONTENT_PROCESS_TEMP_DIR,
-                                       getter_AddRefs(mContentTempDir));
-  if (NS_SUCCEEDED(rv)) {
-    rv = mContentTempDir->GetNativePath(tmpDirName);
-    if (NS_SUCCEEDED(rv)) {
-      // Point a bunch of things that might want to write from content to our
-      // shiny new content-process specific tmpdir
-      tmpDir.emplace("TMPDIR", tmpDirName);
-      xdgCacheHome.emplace("XDG_CACHE_HOME", tmpDirName);
-      xdgCacheDir.emplace("XDG_CACHE_DIR", tmpDirName);
-      // Partial fix for bug 1380051 (not persistent - should be)
-      mesaCacheDir.emplace("MESA_GLSL_CACHE_DIR", tmpDirName);
-    }
+  // Revert to original value
+  if (origNSPRLogName) {
+    PR_SetEnv(mRestoreOrigNSPRLogName.get());
   }
-#endif
+  if (origMozLogName) {
+    PR_SetEnv(mRestoreOrigMozLogName.get());
+  }
+  if (origRustLog) {
+    PR_SetEnv(mRestoreOrigRustLog.get());
+  }
 
-  return PerformAsyncLaunchInternal(aExtraOpts);
+  return retval;
 }
 
 bool
 GeckoChildProcessHost::RunPerformAsyncLaunch(std::vector<std::string> aExtraOpts)
 {
   InitializeChannel();
 
   bool ok = PerformAsyncLaunch(aExtraOpts);
--- a/ipc/glue/GeckoChildProcessHost.h
+++ b/ipc/glue/GeckoChildProcessHost.h
@@ -175,17 +175,18 @@ private:
     Self,
     PluginContainer
   };
 
   static BinaryPathType GetPathToBinary(FilePath& exePath, GeckoProcessType processType);
 
   // The buffer is passed to preserve its lifetime until we are done
   // with launching the sub-process.
-  void GetChildLogName(const char* origLogName, nsACString &buffer);
+  void SetChildLogName(const char* varName, const char* origLogName,
+                       nsACString &buffer);
 
   // In between launching the subprocess and handing off its IPC
   // 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.
--- a/security/sandbox/linux/broker/SandboxBroker.cpp
+++ b/security/sandbox/linux/broker/SandboxBroker.cpp
@@ -23,19 +23,16 @@
 
 #include "base/string_util.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Move.h"
 #include "mozilla/NullPtr.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/ipc/FileDescriptor.h"
-#include "nsDirectoryServiceDefs.h"
-#include "nsAppDirectoryServiceDefs.h"
-#include "SpecialSystemDirectory.h"
 #include "sandbox/linux/system_headers/linux_syscalls.h"
 
 namespace mozilla {
 
 // This constructor signals failure by setting mFileDesc and aClientFd to -1.
 SandboxBroker::SandboxBroker(UniquePtr<const Policy> aPolicy, int aChildPid,
                              int& aClientFd)
   : mChildPid(aChildPid), mPolicy(Move(aPolicy))
@@ -513,44 +510,16 @@ SandboxBroker::ConvertToRealPath(char* a
       // Size changed, but guaranteed to be 0 terminated
       aPathLen = strlen(aPath);
     }
     // ValidatePath will handle failure to translate
   }
   return aPathLen;
 }
 
-size_t
-SandboxBroker::RemapTempDirs(char* aPath, size_t aBufSize, size_t aPathLen)
-{
-  nsAutoCString path(aPath);
-  static const nsLiteralCString tempPrefix(NS_LITERAL_CSTRING("/tmp"));
-
-  if (StringBeginsWith(path, tempPrefix)) {
-    size_t prefixLen = tempPrefix.Length();
-    const nsDependentCSubstring cutPath =
-      Substring(path, prefixLen, path.Length() - prefixLen);
-    // Only now try to get the content process temp 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)) {
-        tmpPath.Append(cutPath);
-        base::strlcpy(aPath, tmpPath.get(), aBufSize);
-        return strlen(aPath);
-      }
-    }
-  }
-
-  return aPathLen;
-}
-
 nsCString
 SandboxBroker::ReverseSymlinks(const nsACString& aPath)
 {
   // Revert any symlinks we previously resolved.
   int32_t cutLength = aPath.Length();
   nsCString cutPath(Substring(aPath, 0, cutLength));
 
   for (;;) {
@@ -704,29 +673,24 @@ SandboxBroker::ThreadMain(void)
       // First string is guaranteed to be 0-terminated.
       pathLen = first_len;
 
       // Look up the first pathname but first translate relative paths.
       pathLen = ConvertToRealPath(pathBuf, sizeof(pathBuf), pathLen);
       perms = mPolicy->Lookup(nsDependentCString(pathBuf, pathLen));
 
       // We don't have read permissions on the requested dir.
+      // Did we arrive from a symlink in a path that is not writable?
+      // Then try to figure out the original path and see if that is readable.
       if (!(perms & MAY_READ)) {
-          // Was it a tempdir that we can remap?
-          pathLen = RemapTempDirs(pathBuf, sizeof(pathBuf), pathLen);
-          perms = mPolicy->Lookup(nsDependentCString(pathBuf, pathLen));
-          if (!(perms & MAY_READ)) {
-            // Did we arrive from a symlink in a path that is not writable?
-            // Then try to figure out the original path and see if that is
-            // readable. Work on the original path, this reverses
-            // ConvertToRealPath above.
-            int symlinkPerms = SymlinkPermissions(recvBuf, first_len);
-            if (symlinkPerms > 0) {
-              perms = symlinkPerms;
-            }
+          // Work on the original path,
+          // this reverses ConvertToRealPath above.
+          int symlinkPerms = SymlinkPermissions(recvBuf, first_len);
+          if (symlinkPerms > 0) {
+            perms = symlinkPerms;
           }
       }
 
       // Same for the second path.
       pathLen2 = strnlen(pathBuf2, kMaxPathLen);
       if (pathLen2 > 0) {
         // Force 0 termination.
         pathBuf2[pathLen2] = '\0';
--- a/security/sandbox/linux/broker/SandboxBroker.h
+++ b/security/sandbox/linux/broker/SandboxBroker.h
@@ -138,18 +138,16 @@ class SandboxBroker final
 
   SandboxBroker(UniquePtr<const Policy> aPolicy, int aChildPid,
                 int& aClientFd);
   void ThreadMain(void) override;
   void AuditPermissive(int aOp, int aFlags, int aPerms, const char* aPath);
   void AuditDenial(int aOp, int aFlags, int aPerms, const char* aPath);
   // Remap relative paths to absolute paths.
   size_t ConvertToRealPath(char* aPath, size_t aBufSize, size_t aPathLen);
-  // Remap references to /tmp and friends to the content process tempdir
-  size_t RemapTempDirs(char* aPath, size_t aBufSize, size_t aPathLen);
   nsCString ReverseSymlinks(const nsACString& aPath);
   // Retrieves permissions for the path the original symlink sits in.
   int SymlinkPermissions(const char* aPath, const size_t aPathLen);
   // In SandboxBrokerRealPath.cpp
   char* SymlinkPath(const Policy* aPolicy, const char* __restrict aPath,
                     char* __restrict aResolved, int* aPermission);
 
   // Holding a UniquePtr should disallow copying, but to make that explicit:
--- a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
+++ b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
@@ -181,16 +181,35 @@ 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
@@ -226,16 +245,18 @@ SandboxBrokerPolicyFactory::SandboxBroke
   policy->AddDir(rdonly, "/usr/lib32");
   policy->AddDir(rdonly, "/usr/lib64");
   policy->AddDir(rdonly, "/etc");
 #ifdef MOZ_PULSEAUDIO
   policy->AddPath(rdonly, "/var/lib/dbus/machine-id");
 #endif
   policy->AddDir(rdonly, "/usr/share");
   policy->AddDir(rdonly, "/usr/local/share");
+  policy->AddDir(rdonly, "/usr/tmp");
+  policy->AddDir(rdonly, "/var/tmp");
   // Various places where fonts reside
   policy->AddDir(rdonly, "/usr/X11R6/lib/X11/fonts");
   policy->AddDir(rdonly, "/nix/store");
   policy->AddDir(rdonly, "/run/host/fonts");
   policy->AddDir(rdonly, "/run/host/user-fonts");
 
   AddMesaSysfsPaths(policy);
   AddLdconfigPaths(policy);
@@ -264,18 +285,17 @@ SandboxBrokerPolicyFactory::SandboxBroke
   // access to.
   mozilla::Array<const char*, 3> extraConfDirs = {
     ".config",   // Fallback if XDG_CONFIG_PATH isn't set
     ".themes",
     ".fonts",
   };
 
   nsCOMPtr<nsIFile> homeDir;
-  nsresult rv = GetSpecialSystemDirectory(Unix_HomeDirectory,
-                                          getter_AddRefs(homeDir));
+  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)) {
@@ -420,44 +440,35 @@ 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;
-  rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
-                              getter_AddRefs(profileDir));
+  nsresult 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)) {
-          nsAutoCString tmpPath;
-          rv = workDir->GetNativePath(tmpPath);
+          rv = workDir->AppendNative(NS_LITERAL_CSTRING("userContent.css"));
           if (NS_SUCCEEDED(rv)) {
-            policy->AddDir(rdonly, tmpPath.get());
+            nsAutoCString tmpPath;
+            rv = workDir->GetNativePath(tmpPath);
+            if (NS_SUCCEEDED(rv)) {
+              policy->AddPath(rdonly, tmpPath.get());
+            }
           }
         }
       }
       rv = profileDir->Clone(getter_AddRefs(workDir));
       if (NS_SUCCEEDED(rv)) {
         rv = workDir->AppendNative(NS_LITERAL_CSTRING("extensions"));
         if (NS_SUCCEEDED(rv)) {
           nsAutoCString tmpPath;
--- a/security/sandbox/test/browser_content_sandbox_fs.js
+++ b/security/sandbox/test/browser_content_sandbox_fs.js
@@ -148,33 +148,38 @@ 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. Uses "ContentTmpD".
+// 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.
 //
 // 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 = prefs.getIntPref("security.sandbox.content.level");
   } catch (e) {
     prefExists = false;
   }
 
   ok(prefExists, "pref security.sandbox.content.level exists");
@@ -358,23 +363,25 @@ async function testFileAccess() {
       });
     }
   }
 
   // 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();
-  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
-  });
+  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
+    });
+  }
   if (fileContentProcessEnabled) {
     tests.push({
       desc:     "profile dir",
       ok:       true,
       browser:  fileBrowser,
       file:     profileDir,
       minLevel: 0,
     });
@@ -549,23 +556,27 @@ async function testFileAccess() {
       minLevel: 0,
     });
   } else {
     ok(false, `${chromeDir.path} is valid dir`);
   }
 
   let cookiesFile = GetProfileEntry("cookies.sqlite");
   if (cookiesFile.exists() && !cookiesFile.isDirectory()) {
-    tests.push({
-      desc:     "cookies file",
-      ok:       false,
-      browser:  webBrowser,
-      file:     cookiesFile,
-      minLevel: minProfileReadSandboxLevel(),
-    });
+    // 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(),
+      });
+    }
     if (fileContentProcessEnabled) {
       tests.push({
         desc:     "cookies file",
         ok:       true,
         browser:  fileBrowser,
         file:     cookiesFile,
         minLevel: 0,
       });
--- a/security/sandbox/test/browser_content_sandbox_utils.js
+++ b/security/sandbox/test/browser_content_sandbox_utils.js
@@ -37,16 +37,20 @@ 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,34 +61,36 @@
 #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(MOZ_CONTENT_SANDBOX)
+#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
 
 nsXREDirProvider* gDirServiceProvider = nullptr;
@@ -492,17 +494,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(MOZ_CONTENT_SANDBOX)
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && 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)))) {
@@ -652,17 +654,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(MOZ_CONTENT_SANDBOX)
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && 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;
@@ -774,26 +776,21 @@ 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, NSID_LENGTH);
-#ifdef XP_UNIX
-    // Braces in a path are somewhat annoying to deal with
-    // and pretty alien on Unix
-    tempDirSuffix.StripChars(u"{}");
-#endif
+    tempDirSuffix.AssignASCII(uuidChars);
 
     // Save the pref
-    rv = Preferences::SetString("security.sandbox.content.tempDirSuffix",
-                                tempDirSuffix);
+    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)))) {
@@ -840,17 +837,18 @@ 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(MOZ_CONTENT_SANDBOX)
+#endif // (defined(XP_WIN) || defined(XP_MACOSX)) &&
+  // defined(MOZ_CONTENT_SANDBOX)
 
 static const char *const kAppendPrefDir[] = { "defaults", "preferences", nullptr };
 
 #ifdef DEBUG_bsmedberg
 static void
 DumpFileArray(const char *key,
               nsCOMArray<nsIFile> dirs)
 {
@@ -1077,17 +1075,17 @@ nsXREDirProvider::DoStartup()
       }
 
       mozilla::Telemetry::Accumulate(mozilla::Telemetry::NUMBER_OF_PROFILES,
                                      count);
     }
 
     obsSvc->NotifyObservers(nullptr, "profile-initial-state", nullptr);
 
-#if defined(MOZ_CONTENT_SANDBOX)
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && 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
   }
@@ -1117,17 +1115,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(MOZ_CONTENT_SANDBOX)
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && 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(MOZ_CONTENT_SANDBOX)
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && 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(MOZ_CONTENT_SANDBOX)
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && 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
@@ -76,17 +76,17 @@
 #define NS_APP_SEARCH_50_FILE                   "SrchF"
 
 #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(MOZ_CONTENT_SANDBOX)
+#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