Bug 1051633: Make sure magic file descriptors in the content process will not be taken for other uses. r=khuey
☠☠ backed out by 14639e630d21 ☠ ☠
authorCervantes Yu <cyu@mozilla.com>
Fri, 15 Aug 2014 17:19:05 +0800
changeset 206882 1e25cd3e82197a4c8a683d63506449d531ede741
parent 206881 d5ee070ddad371ba323990193fd08156e0fb9347
child 206883 8d16fdda5b35bb315d3b614656bc615b6cb242a0
push id49537
push usercyu@mozilla.com
push dateWed, 24 Sep 2014 07:02:42 +0000
treeherdermozilla-inbound@1e25cd3e8219 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs1051633
milestone35.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 1051633: Make sure magic file descriptors in the content process will not be taken for other uses. r=khuey
b2g/app/B2GLoader.cpp
ipc/glue/ProcessUtils_linux.cpp
memory/replace/dmd/DMD.cpp
xpcom/base/nsDebugImpl.cpp
xpcom/build/nsXULAppAPI.h
--- a/b2g/app/B2GLoader.cpp
+++ b/b2g/app/B2GLoader.cpp
@@ -21,33 +21,36 @@
 #include <sys/socket.h>
 
 #include <dlfcn.h>
 
 #include "nsXPCOMPrivate.h" // for MAXPATHLEN and XPCOM_DLL
 
 #define ASSERT(x) if (!(x)) { MOZ_CRASH(); }
 
-
 // Functions being loaded by XPCOMGlue
 XRE_ProcLoaderServiceRunType XRE_ProcLoaderServiceRun;
 XRE_ProcLoaderClientInitType XRE_ProcLoaderClientInit;
 XRE_ProcLoaderPreloadType XRE_ProcLoaderPreload;
 extern XRE_CreateAppDataType XRE_CreateAppData;
 extern XRE_GetFileFromPathType XRE_GetFileFromPath;
 
 static const nsDynamicFunctionLoad kXULFuncs[] = {
   { "XRE_ProcLoaderServiceRun", (NSFuncPtr*) &XRE_ProcLoaderServiceRun },
   { "XRE_ProcLoaderClientInit", (NSFuncPtr*) &XRE_ProcLoaderClientInit },
   { "XRE_ProcLoaderPreload", (NSFuncPtr*) &XRE_ProcLoaderPreload },
   { "XRE_CreateAppData", (NSFuncPtr*) &XRE_CreateAppData },
   { "XRE_GetFileFromPath", (NSFuncPtr*) &XRE_GetFileFromPath },
   { nullptr, nullptr }
 };
 
+typedef mozilla::Vector<int> FdArray;
+static const int kReservedFileDescriptors = 5;
+static const int kBeginReserveFileDescriptor = STDERR_FILENO + 1;
+
 static int
 GetDirnameSlash(const char *aPath, char *aOutDir, int aMaxLen)
 {
   char *lastSlash = strrchr(aPath, XPCOM_FILE_PATH_SEPARATOR[0]);
   if (lastSlash == nullptr) {
     return 0;
   }
   int cpsz = lastSlash - aPath + 1; // include slash
@@ -64,17 +67,17 @@ GetXPCOMPath(const char *aProgram, char 
 {
   nsAutoArrayPtr<char> progBuf(new char[aMaxLen]);
   nsresult rv = mozilla::BinaryPath::Get(aProgram, progBuf);
   NS_ENSURE_SUCCESS(rv, false);
 
   int len = GetDirnameSlash(progBuf, aOutPath, aMaxLen);
   NS_ENSURE_TRUE(!!len, false);
 
-  NS_ENSURE_TRUE((len + sizeof(XPCOM_DLL)) < aMaxLen, false);
+  NS_ENSURE_TRUE((len + sizeof(XPCOM_DLL)) < (unsigned)aMaxLen, false);
   char *afterSlash = aOutPath + len;
   strcpy(afterSlash, XPCOM_DLL);
   return true;
 }
 
 static bool
 LoadLibxul(const char *aXPCOMPath)
 {
@@ -176,17 +179,17 @@ LoadStaticData(int argc, const char *arg
 }
 
 /**
  * Fork and run parent and child process.
  *
  * The parent is the b2g process and child for Nuwa.
  */
 static int
-RunProcesses(int argc, const char *argv[])
+RunProcesses(int argc, const char *argv[], FdArray& aReservedFds)
 {
   /*
    * The original main() of the b2g process.  It is renamed to
    * b2g_main() for the b2g loader.
    */
   int b2g_main(int argc, const char *argv[]);
 
   int ipcSockets[2] = {-1, -1};
@@ -207,42 +210,82 @@ RunProcesses(int argc, const char *argv[
   close(isChildProcess ? parentSock : childSock);
 
   if (isChildProcess) {
     /* The Nuwa process */
     /* This provides the IPC service of loading Nuwa at the process.
      * The b2g process would send a IPC message of loading Nuwa
      * as the replacement of forking and executing plugin-container.
      */
-    return XRE_ProcLoaderServiceRun(getppid(), childSock, argc, argv);
+    return XRE_ProcLoaderServiceRun(getppid(), childSock, argc, argv,
+                                    aReservedFds);
   }
 
   // The b2g process
   int childPid = pid;
-  XRE_ProcLoaderClientInit(childPid, parentSock);
+  XRE_ProcLoaderClientInit(childPid, parentSock, aReservedFds);
   return b2g_main(argc, argv);
 }
 
 /**
+ * Reserve the file descriptors that shouldn't be taken for other use for the
+ * child process.
+ */
+static void
+ReserveFileDescriptors(FdArray& aReservedFds)
+{
+  for (int i = 0; i < kReservedFileDescriptors; i++) {
+    struct stat fileState;
+    int target = kBeginReserveFileDescriptor + i;
+    if (fstat(target, &fileState) == 0) {
+      MOZ_CRASH("ProcLoader error: a magic file descriptor is occupied.");
+    }
+
+    int fd = open("/dev/null", O_RDWR);
+    if (fd == -1) {
+      MOZ_CRASH("ProcLoader error: failed to reserve a magic file descriptor.");
+    }
+
+    aReservedFds.append(target);
+
+    if (fd == target) {
+      // No need to call dup2(). We already occupy the desired file descriptor.
+      continue;
+    }
+
+    if (dup2(fd, target)) {
+      MOZ_CRASH("ProcLoader error: failed to reserve a magic file descriptor.");
+    }
+
+    close(fd);
+  }
+}
+
+/**
  * B2G Loader is responsible for loading the b2g process and the
  * Nuwa process.  It forks into the parent process, for the b2g
  * process, and the child process, for the Nuwa process.
  *
  * The loader loads libxul and performs initialization of static data
  * before forking, so relocation of libxul and static data can be
  * shared between the b2g process, the Nuwa process, and the content
  * processes.
  */
 int
 main(int argc, const char* argv[])
 {
-  const char *program = argv[0];
+  /**
+   * Reserve file descriptors before loading static data.
+   */
+  FdArray reservedFds;
+  ReserveFileDescriptors(reservedFds);
+
   /*
    * Before fork(), libxul and static data of Gecko are loaded for
    * sharing.
    */
   bool ok = LoadStaticData(argc, argv);
   if (!ok) {
     return 255;
   }
 
-  return RunProcesses(argc, argv);
+  return RunProcesses(argc, argv, reservedFds);
 }
--- a/ipc/glue/ProcessUtils_linux.cpp
+++ b/ipc/glue/ProcessUtils_linux.cpp
@@ -22,31 +22,35 @@
 #include "mozilla/ipc/PProcLoaderChild.h"
 #include "mozilla/ipc/Transport.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "mozilla/ipc/IOThreadChild.h"
 #include "mozilla/dom/ContentProcess.h"
 #include "base/file_descriptor_shuffle.h"
 #include "mozilla/BackgroundHangMonitor.h"
 #include "mozilla/DebugOnly.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/unused.h"
 #include "base/process_util.h"
+#include "base/eintr_wrapper.h"
 
 #include "prenv.h"
 
 #include "nsXULAppAPI.h" // export XRE_* functions
 
 #include "nsAppRunner.h"
 
 int content_process_main(int argc, char *argv[]);
 
-extern bool gDisableAndroidLog;
+typedef mozilla::Vector<int> FdArray;
 
 #endif /* MOZ_B2G_LOADER */
 
 namespace mozilla {
+
 namespace ipc {
 
 void SetThisProcessName(const char *aName)
 {
   prctl(PR_SET_NAME, (unsigned long)aName, 0uL, 0uL, 0uL);
 }
 
 #ifdef MOZ_B2G_LOADER
@@ -93,19 +97,28 @@ using namespace mozilla::dom;
 
 static bool sProcLoaderClientOnDeinit = false;
 static DebugOnly<bool> sProcLoaderClientInitialized = false;
 static DebugOnly<bool> sProcLoaderClientGeckoInitialized = false;
 static pid_t sProcLoaderPid = 0;
 static int sProcLoaderChannelFd = -1;
 static PProcLoaderParent *sProcLoaderParent = nullptr;
 static MessageLoop *sProcLoaderLoop = nullptr;
+static mozilla::UniquePtr<FdArray> sReservedFds;
 
 static void ProcLoaderClientDeinit();
 
+/**
+ * Some file descriptors, like the child IPC channel FD, must be opened at
+ * specific numbers. To ensure this, we pre-reserve kReservedFileDescriptors FDs
+ * starting from kBeginReserveFileDescriptor so that operations like
+ * __android_log_print() won't take these magic FDs.
+ */
+static const int kReservedFileDescriptors = 5;
+static const int kBeginReserveFileDescriptor = STDERR_FILENO + 1;
 
 class ProcLoaderParent : public PProcLoaderParent
 {
 private:
   nsAutoPtr<FileDescriptor> mChannelFd; // To keep a reference.
 
 public:
   ProcLoaderParent(FileDescriptor *aFd) : mChannelFd(aFd) {}
@@ -134,16 +147,24 @@ static void
 bool
 ProcLoaderParent::RecvLoadComplete(const int32_t &aPid,
                                    const int32_t &aCookie)
 {
   ProcLoaderClientDeinit();
   return true;
 }
 
+static void
+CloseFileDescriptors(FdArray& aFds)
+{
+  for (size_t i = 0; i < aFds.length(); i++) {
+    unused << HANDLE_EINTR(close(aFds[i]));
+  }
+}
+
 void
 ProcLoaderParent::OnChannelError()
 {
   if (sProcLoaderClientOnDeinit) {
     // Get error for closing while the channel is already error.
     return;
   }
   NS_WARNING("ProcLoaderParent is in channel error");
@@ -287,30 +308,30 @@ class ProcLoaderRunnerBase;
 
 static bool sProcLoaderServing = false;
 static ProcLoaderRunnerBase *sProcLoaderDispatchedTask = nullptr;
 
 class ProcLoaderRunnerBase
 {
 public:
   virtual int DoWork() = 0;
+  virtual ~ProcLoaderRunnerBase() {}
 };
 
 
 class ProcLoaderNoopRunner : public ProcLoaderRunnerBase {
 public:
   virtual int DoWork();
 };
 
 int
 ProcLoaderNoopRunner::DoWork() {
   return 0;
 }
 
-
 /**
  * The runner to load Nuwa at the current process.
  */
 class ProcLoaderLoadRunner : public ProcLoaderRunnerBase {
 private:
   const nsTArray<nsCString> mArgv;
   const nsTArray<nsCString> mEnv;
   const nsTArray<FDRemap> mFdsRemap;
@@ -331,32 +352,51 @@ public:
   int DoWork();
 };
 
 void
 ProcLoaderLoadRunner::ShuffleFds()
 {
   unsigned int i;
 
+  MOZ_ASSERT(mFdsRemap.Length() <= kReservedFileDescriptors);
+
   InjectiveMultimap fd_shuffle1, fd_shuffle2;
   fd_shuffle1.reserve(mFdsRemap.Length());
   fd_shuffle2.reserve(mFdsRemap.Length());
+
   for (i = 0; i < mFdsRemap.Length(); i++) {
     const FDRemap *map = &mFdsRemap[i];
     int fd = map->fd().PlatformHandle();
     int tofd = map->mapto();
 
     fd_shuffle1.push_back(InjectionArc(fd, tofd, false));
     fd_shuffle2.push_back(InjectionArc(fd, tofd, false));
+
+    // Erase from sReservedFds we will use.
+    for (int* toErase = sReservedFds->begin();
+         toErase < sReservedFds->end();
+         toErase++) {
+      if (tofd == *toErase) {
+        sReservedFds->erase(toErase);
+        break;
+      }
+    }
   }
 
   DebugOnly<bool> ok = ShuffleFileDescriptors(&fd_shuffle1);
-  MOZ_ASSERT(ok, "ShuffleFileDescriptors failed");
 
-  CloseSuperfluousFds(fd_shuffle2);
+  // Close the FDs that are reserved but not used after
+  // ShuffleFileDescriptors().
+  MOZ_ASSERT(sReservedFds);
+  CloseFileDescriptors(*sReservedFds);
+  sReservedFds = nullptr;
+
+  // Note that we don'e call ::base::CloseSuperfluousFds() here, assuming that
+  // The file descriptor inherited from the parent are also necessary for us.
 }
 
 int
 ProcLoaderLoadRunner::DoWork()
 {
   unsigned int i;
 
   ShuffleFds();
@@ -477,37 +517,39 @@ public:
  *
  * \param aPeerPid is the pid of the parent.
  * \param aFd is the file descriptor of the socket for IPC.
  *
  * See the comment near the head of this file.
  */
 static int
 ProcLoaderServiceRun(pid_t aPeerPid, int aFd,
-                     int aArgc, const char *aArgv[])
+                     int aArgc, const char *aArgv[],
+                     FdArray& aReservedFds)
 {
+  // Make a copy of aReservedFds. It will be used when we dup() the magic file
+  // descriptors when ProcLoaderChild::RecvLoad() runs.
+  sReservedFds = MakeUnique<FdArray>(mozilla::Move(aReservedFds));
+
   ScopedLogging logging;
 
   char **_argv;
   _argv = new char *[aArgc + 1];
   for (int i = 0; i < aArgc; i++) {
     _argv[i] = ::strdup(aArgv[i]);
     MOZ_ASSERT(_argv[i] != nullptr);
   }
   _argv[aArgc] = nullptr;
 
   gArgv = _argv;
   gArgc = aArgc;
 
   {
-    gDisableAndroidLog = true;
-
     nsresult rv = XRE_InitCommandLine(aArgc, _argv);
     if (NS_FAILED(rv)) {
-      gDisableAndroidLog = false;
       MOZ_CRASH();
     }
 
     FileDescriptor fd(aFd);
     close(aFd);
 
     MOZ_ASSERT(!sProcLoaderServing);
     MessageLoop loop;
@@ -525,18 +567,16 @@ ProcLoaderServiceRun(pid_t aPeerPid, int
     BackgroundHangMonitor::Prohibit();
 
     sProcLoaderServing = true;
     loop.Run();
 
     BackgroundHangMonitor::Allow();
 
     XRE_DeinitCommandLine();
-
-    gDisableAndroidLog = false;
   }
 
   MOZ_ASSERT(sProcLoaderDispatchedTask != nullptr);
   ProcLoaderRunnerBase *task = sProcLoaderDispatchedTask;
   sProcLoaderDispatchedTask = nullptr;
   int ret = task->DoWork();
   delete task;
 
@@ -550,21 +590,27 @@ ProcLoaderServiceRun(pid_t aPeerPid, int
 
 #endif /* MOZ_B2G_LOADER */
 
 } // namespace ipc
 } // namespace mozilla
 
 #ifdef MOZ_B2G_LOADER
 void
-XRE_ProcLoaderClientInit(pid_t aPeerPid, int aChannelFd)
+XRE_ProcLoaderClientInit(pid_t aPeerPid, int aChannelFd, FdArray& aReservedFds)
 {
+  // We already performed fork(). It's safe to free the "danger zone" of file
+  // descriptors .
+  mozilla::ipc::CloseFileDescriptors(aReservedFds);
+
   mozilla::ipc::ProcLoaderClientInit(aPeerPid, aChannelFd);
 }
 
 int
 XRE_ProcLoaderServiceRun(pid_t aPeerPid, int aFd,
-                         int aArgc, const char *aArgv[])
+                         int aArgc, const char *aArgv[],
+                         FdArray& aReservedFds)
 {
   return mozilla::ipc::ProcLoaderServiceRun(aPeerPid, aFd,
-                                            aArgc, aArgv);
+                                            aArgc, aArgv,
+                                            aReservedFds);
 }
 #endif /* MOZ_B2G_LOADER */
--- a/memory/replace/dmd/DMD.cpp
+++ b/memory/replace/dmd/DMD.cpp
@@ -196,17 +196,22 @@ MallocSizeOf(const void* aPtr)
 }
 
 MOZ_EXPORT void
 StatusMsg(const char* aFmt, ...)
 {
   va_list ap;
   va_start(ap, aFmt);
 #ifdef ANDROID
-  __android_log_vprint(ANDROID_LOG_INFO, "DMD", aFmt, ap);
+#ifdef MOZ_B2G_LOADER
+  // Don't call __android_log_vprint() during initialization, or the magic file
+  // descriptors will be occupied by android logcat.
+  if (gIsDMDRunning)
+#endif
+    __android_log_vprint(ANDROID_LOG_INFO, "DMD", aFmt, ap);
 #else
   // The +64 is easily enough for the "DMD[<pid>] " prefix and the NUL.
   char* fmt = (char*) InfallibleAllocPolicy::malloc_(strlen(aFmt) + 64);
   sprintf(fmt, "DMD[%d] %s", getpid(), aFmt);
   vfprintf(stderr, fmt, ap);
   InfallibleAllocPolicy::free_(fmt);
 #endif
   va_end(ap);
--- a/xpcom/base/nsDebugImpl.cpp
+++ b/xpcom/base/nsDebugImpl.cpp
@@ -97,23 +97,16 @@ Break(const char* aMsg);
 #if defined(_WIN32)
 #include <windows.h>
 #include <signal.h>
 #include <malloc.h> // for _alloca
 #elif defined(XP_UNIX)
 #include <stdlib.h>
 #endif
 
-#ifdef MOZ_B2G_LOADER
-/* Avoid calling Android logger/logd temporarily while running
- * B2GLoader to start the child process.
- */
-bool gDisableAndroidLog = false;
-#endif
-
 using namespace mozilla;
 
 static const char* sMultiprocessDescription = nullptr;
 
 static Atomic<int32_t> gAssertionCount;
 
 NS_IMPL_QUERY_INTERFACE(nsDebugImpl, nsIDebug, nsIDebug2)
 
@@ -386,19 +379,16 @@ NS_DebugBreak(uint32_t aSeverity, const 
   // errors on platforms without a debugdlg ring a bell on stderr
 #if !defined(XP_WIN)
   if (ll != PR_LOG_WARNING) {
     fprintf(stderr, "\07");
   }
 #endif
 
 #ifdef ANDROID
-#ifdef MOZ_B2G_LOADER
-  if (!gDisableAndroidLog)
-#endif
   __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", buf.buffer);
 #endif
 
   // Write the message to stderr unless it's a warning and MOZ_IGNORE_WARNINGS
   // is set.
   if (!(PR_GetEnv("MOZ_IGNORE_WARNINGS") && aSeverity == NS_DEBUG_WARNING)) {
     fprintf(stderr, "%s\n", buf.buffer);
     fflush(stderr);
--- a/xpcom/build/nsXULAppAPI.h
+++ b/xpcom/build/nsXULAppAPI.h
@@ -11,16 +11,17 @@
 #include "nsXPCOM.h"
 #include "nsISupports.h"
 #include "prlog.h"
 #include "nsXREAppData.h"
 #include "js/TypeDecls.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/Vector.h"
 
 /**
  * A directory service key which provides the platform-correct "application
  * data" directory as follows, where $name and $vendor are as defined above and
  * $vendor is optional:
  *
  * Windows:
  *   HOME = Documents and Settings\$USER\Application Data
@@ -465,19 +466,21 @@ enum WindowsEnvironmentType
  * under. Valid after a call to XRE_main.
  */
 XRE_API(WindowsEnvironmentType,
         XRE_GetWindowsEnvironment, ())
 #endif // XP_WIN
 
 #ifdef MOZ_B2G_LOADER
 XRE_API(int,
-        XRE_ProcLoaderServiceRun, (pid_t, int, int argc, const char* argv[]));
+        XRE_ProcLoaderServiceRun, (pid_t, int, int argc, const char* argv[],
+                                   mozilla::Vector<int>& aReservedFds));
 XRE_API(void,
-        XRE_ProcLoaderClientInit, (pid_t, int));
+        XRE_ProcLoaderClientInit, (pid_t, int,
+                                   mozilla::Vector<int>& aReservedFds));
 XRE_API(void,
         XRE_ProcLoaderPreload, (const char* aProgramDir,
                                 const nsXREAppData* aAppData));
 #endif // MOZ_B2G_LOADER
 
 XRE_API(int,
         XRE_XPCShellMain, (int argc, char** argv, char** envp))