Bug 1289718 - Clean up stat/stat64 wrapper. Deal with non-default TMPDIR. r=jld draft
authorGian-Carlo Pascutto <gcp@mozilla.com>
Thu, 06 Oct 2016 13:25:13 +0200
changeset 421529 6380b554313a1ff5bcc80fa95be6d9b2891ed167
parent 421528 ad740950d3c2df009c13fab53a91358d4d86ed44
child 533116 4d45ed404cdb1c673c7e90803c34b32a79363ed2
push id31540
push userbmo:gpascutto@mozilla.com
push dateThu, 06 Oct 2016 11:25:45 +0000
reviewersjld
bugs1289718
milestone52.0a1
Bug 1289718 - Clean up stat/stat64 wrapper. Deal with non-default TMPDIR. r=jld MozReview-Commit-ID: DW63be9qd3z
security/sandbox/linux/SandboxBrokerClient.cpp
security/sandbox/linux/SandboxBrokerClient.h
security/sandbox/linux/SandboxFilter.cpp
security/sandbox/linux/broker/SandboxBroker.cpp
security/sandbox/linux/broker/SandboxBrokerCommon.h
security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
security/sandbox/linux/broker/SandboxBrokerUtils.h
security/sandbox/linux/gtest/TestBroker.cpp
toolkit/crashreporter/nsExceptionHandler.cpp
--- a/security/sandbox/linux/SandboxBrokerClient.cpp
+++ b/security/sandbox/linux/SandboxBrokerClient.cpp
@@ -15,17 +15,16 @@
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 #include "mozilla/Assertions.h"
 #include "mozilla/NullPtr.h"
 #include "base/strings/safe_sprintf.h"
-#include "sandbox/linux/system_headers/linux_syscalls.h"
 
 namespace mozilla {
 
 SandboxBrokerClient::SandboxBrokerClient(int aFd)
 : mFileDesc(aFd)
 {
 }
 
@@ -170,42 +169,26 @@ SandboxBrokerClient::Open(const char* aP
 int
 SandboxBrokerClient::Access(const char* aPath, int aMode)
 {
   Request req = { SANDBOX_FILE_ACCESS, aMode, 0 };
   return DoCall(&req, aPath, nullptr, nullptr, false);
 }
 
 int
-SandboxBrokerClient::Stat(const char* aPath, struct stat* aStat)
+SandboxBrokerClient::Stat(const char* aPath, statstruct* aStat)
 {
-  // This is actually stat64 on 32-bit Linux, so adjust the
-  // struct to have the right expected size.
-#if defined(__NR_stat64)
-  Request req = { SANDBOX_FILE_STAT, 0, sizeof(struct stat64) };
-#elif defined(__NR_stat)
-  Request req = { SANDBOX_FILE_STAT, 0, sizeof(struct stat) };
-#else
-#error Missing include.
-#endif
+  Request req = { SANDBOX_FILE_STAT, 0, sizeof(statstruct) };
   return DoCall(&req, aPath, nullptr, (void*)aStat, false);
 }
 
 int
-SandboxBrokerClient::LStat(const char* aPath, struct stat* aStat)
+SandboxBrokerClient::LStat(const char* aPath, statstruct* aStat)
 {
-  // This is actually stat64 on 32-bit Linux, so adjust the
-  // struct to have the right expected size.
-#if defined(__NR_stat64)
-  Request req = { SANDBOX_FILE_STAT, O_NOFOLLOW, sizeof(struct stat64) };
-#elif defined(__NR_stat)
-  Request req = { SANDBOX_FILE_STAT, O_NOFOLLOW, sizeof(struct stat) };
-#else
-#error Missing include.
-#endif
+  Request req = { SANDBOX_FILE_STAT, O_NOFOLLOW, sizeof(statstruct) };
   return DoCall(&req, aPath, nullptr, (void*)aStat, false);
 }
 
 int
 SandboxBrokerClient::Chmod(const char* aPath, int aMode)
 {
   Request req = {SANDBOX_FILE_CHMOD, aMode, 0};
   return DoCall(&req, aPath, nullptr, nullptr, false);
--- a/security/sandbox/linux/SandboxBrokerClient.h
+++ b/security/sandbox/linux/SandboxBrokerClient.h
@@ -3,16 +3,17 @@
 /* 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 http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_SandboxBrokerClient_h
 #define mozilla_SandboxBrokerClient_h
 
 #include "broker/SandboxBrokerCommon.h"
+#include "broker/SandboxBrokerUtils.h"
 
 #include "mozilla/Attributes.h"
 
 // This is the client for the sandbox broker described in
 // broker/SandboxBroker.h; its constructor takes the file descriptor
 // returned by SandboxBroker::Create, passed to the child over IPC.
 //
 // The operations exposed here can be called from any thread and in
@@ -26,18 +27,18 @@ namespace mozilla {
 
 class SandboxBrokerClient final : private SandboxBrokerCommon {
  public:
   explicit SandboxBrokerClient(int aFd);
   ~SandboxBrokerClient();
 
   int Open(const char* aPath, int aFlags);
   int Access(const char* aPath, int aMode);
-  int Stat(const char* aPath, struct stat* aStat);
-  int LStat(const char* aPath, struct stat* aStat);
+  int Stat(const char* aPath, statstruct* aStat);
+  int LStat(const char* aPath, statstruct* aStat);
   int Chmod(const char* aPath, int aMode);
   int Link(const char* aPath, const char* aPath2);
   int Mkdir(const char* aPath, int aMode);
   int Symlink(const char* aOldPath, const char* aNewPath);
   int Rename(const char* aOldPath, const char* aNewPath);
   int Unlink(const char* aPath);
   int Rmdir(const char* aPath);
   int Readlink(const char* aPath, void* aBuf, size_t aBufSize);
--- a/security/sandbox/linux/SandboxFilter.cpp
+++ b/security/sandbox/linux/SandboxFilter.cpp
@@ -378,32 +378,32 @@ class ContentSandboxPolicy : public Sand
       return BlockedSyscallTrap(aArgs, nullptr);
     }
     return broker->Access(path, mode);
   }
 
   static intptr_t StatTrap(ArgsRef aArgs, void* aux) {
     auto broker = static_cast<SandboxBrokerClient*>(aux);
     auto path = reinterpret_cast<const char*>(aArgs.args[0]);
-    auto buf = reinterpret_cast<struct stat*>(aArgs.args[1]);
+    auto buf = reinterpret_cast<statstruct*>(aArgs.args[1]);
     return broker->Stat(path, buf);
   }
 
   static intptr_t LStatTrap(ArgsRef aArgs, void* aux) {
     auto broker = static_cast<SandboxBrokerClient*>(aux);
     auto path = reinterpret_cast<const char*>(aArgs.args[0]);
-    auto buf = reinterpret_cast<struct stat*>(aArgs.args[1]);
+    auto buf = reinterpret_cast<statstruct*>(aArgs.args[1]);
     return broker->LStat(path, buf);
   }
 
   static intptr_t StatAtTrap(ArgsRef aArgs, void* aux) {
     auto broker = static_cast<SandboxBrokerClient*>(aux);
     auto fd = static_cast<int>(aArgs.args[0]);
     auto path = reinterpret_cast<const char*>(aArgs.args[1]);
-    auto buf = reinterpret_cast<struct stat*>(aArgs.args[2]);
+    auto buf = reinterpret_cast<statstruct*>(aArgs.args[2]);
     auto flags = static_cast<int>(aArgs.args[3]);
     if (fd != AT_FDCWD && path[0] != '/') {
       SANDBOX_LOG_ERROR("unsupported fd-relative fstatat(%d, \"%s\", %p, %d)",
                         fd, path, buf, flags);
       return BlockedSyscallTrap(aArgs, nullptr);
     }
     if ((flags & ~AT_SYMLINK_NOFOLLOW) != 0) {
       SANDBOX_LOG_ERROR("unsupported flags %d in fstatat(%d, \"%s\", %p, %d)",
--- a/security/sandbox/linux/broker/SandboxBroker.cpp
+++ b/security/sandbox/linux/broker/SandboxBroker.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 http://mozilla.org/MPL/2.0/. */
 
 #include "SandboxBroker.h"
 #include "SandboxInfo.h"
 #include "SandboxLogging.h"
+#include "SandboxBrokerUtils.h"
 
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -360,27 +361,20 @@ AllowOpen(int aReqFlags, int aPerms)
     needed |= SandboxBroker::MAY_CREATE;
   }
   return (aPerms & needed) == needed;
 }
 
 static int
 DoStat(const char* aPath, void* aBuff, int aFlags)
 {
-#if defined(__NR_stat64)
  if (aFlags & O_NOFOLLOW) {
-    return lstat64(aPath, (struct stat64*)aBuff);
+    return lstatsyscall(aPath, (statstruct*)aBuff);
   }
-  return stat64(aPath, (struct stat64*)aBuff);
-#else
-  if (aFlags & O_NOFOLLOW) {
-    return lstat(aPath, (struct stat*)aBuff);
-  }
-  return stat(aPath, (struct stat*)aBuff);
-#endif
+  return statsyscall(aPath, (statstruct*)aBuff);
 }
 
 static int
 DoLink(const char* aPath, const char* aPath2,
        SandboxBrokerCommon::Operation aOper)
 {
   if (aOper == SandboxBrokerCommon::Operation::SANDBOX_FILE_LINK) {
     return link(aPath, aPath2);
--- a/security/sandbox/linux/broker/SandboxBrokerCommon.h
+++ b/security/sandbox/linux/broker/SandboxBrokerCommon.h
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_SandboxBrokerTypes_h
-#define mozilla_SandboxBrokerTypes_h
+#ifndef mozilla_SandboxBrokerCommon_h
+#define mozilla_SandboxBrokerCommon_h
 
 #include <sys/types.h>
 
 struct iovec;
 
 // This file defines the protocol between the filesystem broker,
 // described in SandboxBroker.h, and its client, described in
 // ../SandboxBrokerClient.h; and it defines some utility functions
@@ -64,9 +64,9 @@ public:
   static ssize_t RecvWithFd(int aFd, const iovec* aIO, size_t aNumIO,
                             int* aPassedFdPtr);
   static ssize_t SendWithFd(int aFd, const iovec* aIO, size_t aNumIO,
                             int aPassedFd);
 };
 
 } // namespace mozilla
 
-#endif // mozilla_SandboxBrokerTypes_h
+#endif // mozilla_SandboxBrokerCommon_h
--- a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
+++ b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
@@ -8,16 +8,17 @@
 #include "SandboxInfo.h"
 
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Preferences.h"
 #include "nsPrintfCString.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
+#include "SpecialSystemDirectory.h"
 
 #ifdef ANDROID
 #include "cutils/properties.h"
 #endif
 
 namespace mozilla {
 
 /* static */ bool
@@ -113,17 +114,33 @@ SandboxBrokerPolicyFactory::SandboxBroke
   policy->AddPath(rdonly, "/data/local/tmp/profiler.options",
                   SandboxBroker::Policy::AddAlways); // bug 1029337
 
   mCommonContentPolicy.reset(policy);
 #elif defined(MOZ_CONTENT_SANDBOX)
   SandboxBroker::Policy* policy = new SandboxBroker::Policy;
   policy->AddDir(rdonly, "/");
   policy->AddDir(rdwrcr, "/dev/shm");
-  policy->AddDir(rdwrcr, "/tmp");
+  // 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");
+  }
   mCommonContentPolicy.reset(policy);
 #endif
 }
 
 #ifdef MOZ_CONTENT_SANDBOX
 UniquePtr<SandboxBroker::Policy>
 SandboxBrokerPolicyFactory::GetContentPolicy(int aPid)
 {
new file mode 100644
--- /dev/null
+++ b/security/sandbox/linux/broker/SandboxBrokerUtils.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 http://mozilla.org/MPL/2.0/. */
+#ifndef mozilla_SandboxBrokerUtils_h
+#define mozilla_SandboxBrokerUtils_h
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+// On 32-bit Linux, stat calls are translated by libc into stat64
+// calls. We'll intercept those and handle them in the stat functions
+// but must be sure to use the right structure layout.
+
+#if defined(__NR_stat64)
+typedef struct stat64 statstruct;
+#define statsyscall stat64
+#define lstatsyscall lstat64
+#elif defined(__NR_stat)
+typedef struct stat statstruct;
+#define statsyscall stat
+#define lstatsyscall lstat
+#else
+#error Missing stat syscall include.
+#endif
+
+#endif // mozilla_SandboxBrokerUtils_h
--- a/security/sandbox/linux/gtest/TestBroker.cpp
+++ b/security/sandbox/linux/gtest/TestBroker.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 http://mozilla.org/MPL/2.0/. */
 
 #include "gtest/gtest.h"
 
 #include "broker/SandboxBroker.h"
+#include "broker/SandboxBrokerUtils.h"
 #include "SandboxBrokerClient.h"
 
 #include <errno.h>
 #include <fcntl.h>
 #include <pthread.h>
 #include <stdlib.h>
 #include <sched.h>
 #include <semaphore.h>
@@ -49,20 +50,20 @@ class SandboxBrokerTest : public ::testi
 
 protected:
   int Open(const char* aPath, int aFlags) {
     return mClient->Open(aPath, aFlags);
   }
   int Access(const char* aPath, int aMode) {
     return mClient->Access(aPath, aMode);
   }
-  int Stat(const char* aPath, struct stat* aStat) {
+  int Stat(const char* aPath, statstruct* aStat) {
     return mClient->Stat(aPath, aStat);
   }
-  int LStat(const char* aPath, struct stat* aStat) {
+  int LStat(const char* aPath, statstruct* aStat) {
     return mClient->LStat(aPath, aStat);
   }
   int Chmod(const char* aPath, int aMode) {
     return mClient->Chmod(aPath, aMode);
   }
   int Link(const char* aPath, const char* bPath) {
     return mClient->Link(aPath, bPath);
   }
@@ -204,33 +205,33 @@ TEST_F(SandboxBrokerTest, Access)
   EXPECT_EQ(0, Access("/proc/self", F_OK));
   EXPECT_EQ(-EACCES, Access("/proc/self", R_OK));
 
   EXPECT_EQ(-EACCES, Access("/proc/self/stat", F_OK));
 }
 
 TEST_F(SandboxBrokerTest, Stat)
 {
-  struct stat brokeredStat, realStat;
-  ASSERT_EQ(0, stat("/dev/null", &realStat)) << "Shouldn't ever fail!";
+  statstruct realStat, brokeredStat;
+  ASSERT_EQ(0, statsyscall("/dev/null", &realStat)) << "Shouldn't ever fail!";
   EXPECT_EQ(0, Stat("/dev/null", &brokeredStat));
   EXPECT_EQ(realStat.st_ino, brokeredStat.st_ino);
   EXPECT_EQ(realStat.st_rdev, brokeredStat.st_rdev);
 
   EXPECT_EQ(-ENOENT, Stat("/var/empty/qwertyuiop", &brokeredStat));
   EXPECT_EQ(-EACCES, Stat("/dev", &brokeredStat));
 
   EXPECT_EQ(0, Stat("/proc/self", &brokeredStat));
   EXPECT_TRUE(S_ISDIR(brokeredStat.st_mode));
 }
 
 TEST_F(SandboxBrokerTest, LStat)
 {
-  struct stat brokeredStat, realStat;
-  ASSERT_EQ(0, lstat("/dev/null", &realStat));
+  statstruct realStat, brokeredStat;
+  ASSERT_EQ(0, lstatsyscall("/dev/null", &realStat));
   EXPECT_EQ(0, LStat("/dev/null", &brokeredStat));
   EXPECT_EQ(realStat.st_ino, brokeredStat.st_ino);
   EXPECT_EQ(realStat.st_rdev, brokeredStat.st_rdev);
 
   EXPECT_EQ(-ENOENT, LStat("/var/empty/qwertyuiop", &brokeredStat));
   EXPECT_EQ(-EACCES, LStat("/dev", &brokeredStat));
 
   EXPECT_EQ(0, LStat("/proc/self", &brokeredStat));
@@ -255,22 +256,22 @@ TEST_F(SandboxBrokerTest, Chmod)
   ASSERT_GE(fd, 0) << "Opening /tmp/blublu for writing failed.";
   close(fd);
   // Set read only. SandboxBroker enforces 0600 mode flags.
   ASSERT_EQ(0, Chmod("/tmp/blublu", S_IRUSR));
   // SandboxBroker doesn't use real access(), it just checks against
   // the policy. So it can't see the change in permisions here.
   // This won't work:
   // EXPECT_EQ(-EACCES, Access("/tmp/blublu", W_OK));
-  struct stat realStat;
-  EXPECT_EQ(0, stat("/tmp/blublu", &realStat));
+  statstruct realStat;
+  EXPECT_EQ(0, statsyscall("/tmp/blublu", &realStat));
   EXPECT_EQ((mode_t)S_IRUSR, realStat.st_mode & 0777);
 
   ASSERT_EQ(0, Chmod("/tmp/blublu", S_IRUSR | S_IWUSR));
-  EXPECT_EQ(0, stat("/tmp/blublu", &realStat));
+  EXPECT_EQ(0, statsyscall("/tmp/blublu", &realStat));
   EXPECT_EQ((mode_t)(S_IRUSR | S_IWUSR), realStat.st_mode & 0777);
   EXPECT_EQ(0, unlink("/tmp/blublu"));
 
   PrePostTestCleanup();
 }
 
 TEST_F(SandboxBrokerTest, Link)
 {
@@ -293,18 +294,18 @@ TEST_F(SandboxBrokerTest, Symlink)
 {
   PrePostTestCleanup();
 
   int fd = Open("/tmp/blublu", O_WRONLY | O_CREAT);
   ASSERT_GE(fd, 0) << "Opening /tmp/blublu for writing failed.";
   close(fd);
   ASSERT_EQ(0, Symlink("/tmp/blublu", "/tmp/blublublu"));
   EXPECT_EQ(0, Access("/tmp/blublublu", F_OK));
-  struct stat aStat;
-  ASSERT_EQ(0, lstat("/tmp/blublublu", &aStat));
+  statstruct aStat;
+  ASSERT_EQ(0, lstatsyscall("/tmp/blublublu", &aStat));
   EXPECT_EQ((mode_t)S_IFLNK, aStat.st_mode & S_IFMT);
   // Not whitelisted target path
   EXPECT_EQ(-EACCES, Symlink("/tmp/blublu", "/tmp/nope"));
   EXPECT_EQ(0, unlink("/tmp/blublublu"));
   EXPECT_EQ(0, unlink("/tmp/blublu"));
 
   PrePostTestCleanup();
 }
@@ -416,23 +417,23 @@ void SandboxBrokerTest::MultiThreadOpenW
 }
 
 TEST_F(SandboxBrokerTest, MultiThreadStat) {
   RunOnManyThreads<SandboxBrokerTest,
                    &SandboxBrokerTest::MultiThreadStatWorker>();
 }
 void SandboxBrokerTest::MultiThreadStatWorker() {
   static const int kNumLoops = 7500;
-  struct stat nullStat, zeroStat, selfStat;
+  statstruct nullStat, zeroStat, selfStat;
   dev_t realNullDev, realZeroDev;
   ino_t realSelfInode;
 
-  ASSERT_EQ(0, stat("/dev/null", &nullStat)) << "Shouldn't ever fail!";
-  ASSERT_EQ(0, stat("/dev/zero", &zeroStat)) << "Shouldn't ever fail!";
-  ASSERT_EQ(0, lstat("/proc/self", &selfStat)) << "Shouldn't ever fail!";
+  ASSERT_EQ(0, statsyscall("/dev/null", &nullStat)) << "Shouldn't ever fail!";
+  ASSERT_EQ(0, statsyscall("/dev/zero", &zeroStat)) << "Shouldn't ever fail!";
+  ASSERT_EQ(0, lstatsyscall("/proc/self", &selfStat)) << "Shouldn't ever fail!";
   ASSERT_TRUE(S_ISLNK(selfStat.st_mode)) << "Shouldn't ever fail!";
   realNullDev = nullStat.st_rdev;
   realZeroDev = zeroStat.st_rdev;
   realSelfInode = selfStat.st_ino;
   for (int i = 1; i <= kNumLoops; ++i) {
     ASSERT_EQ(0, Stat("/dev/null", &nullStat))
       << "Loop " << i << "/" << kNumLoops;
     ASSERT_EQ(0, Stat("/dev/zero", &zeroStat))
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -1179,22 +1179,24 @@ bool MinidumpCallback(
 #endif
   }
 #endif // XP_MACOSX
 #endif // XP_UNIX
 
   return returnValue;
 }
 
-#if defined(XP_MACOSX) || defined(__ANDROID__)
+#if defined(XP_MACOSX) || defined(__ANDROID__) || defined(XP_LINUX)
 static size_t
 EnsureTrailingSlash(XP_CHAR* aBuf, size_t aBufLen)
 {
   size_t len = XP_STRLEN(aBuf);
-  if ((len + 2) < aBufLen && aBuf[len - 1] != XP_PATH_SEPARATOR_CHAR) {
+  if ((len + 1) < aBufLen
+      && len > 0
+      && aBuf[len - 1] != XP_PATH_SEPARATOR_CHAR) {
     aBuf[len] = XP_PATH_SEPARATOR_CHAR;
     ++len;
     aBuf[len] = 0;
   }
   return len;
 }
 #endif
 
@@ -1257,21 +1259,24 @@ BuildTempPath(char* aBuf, size_t aBufLen
   return EnsureTrailingSlash(aBuf, aBufLen);
 }
 
 #elif defined(XP_UNIX)
 
 static size_t
 BuildTempPath(char* aBuf, size_t aBufLen)
 {
-  // we assume it's always /tmp on unix systems
-  NS_NAMED_LITERAL_CSTRING(tmpPath, "/tmp/");
+  const char *tempenv = PR_GetEnv("TMPDIR");
+  const char *tmpPath = "/tmp/";
+  if (!tempenv) {
+    tempenv = tmpPath;
+  }
   int size = (int)aBufLen;
-  Concat(aBuf, tmpPath.get(), &size);
-  return tmpPath.Length();
+  Concat(aBuf, tempenv, &size);
+  return EnsureTrailingSlash(aBuf, aBufLen);
 }
 
 #else
 #error "Implement this for your platform"
 #endif
 
 template <typename CharT, size_t N>
 static size_t