Bug 1051633: Make sure magic file descriptors in the content process will not be taken for other uses. r=khuey, a=bajaj
authorCervantes Yu <cyu@mozilla.com>
Tue, 30 Sep 2014 00:00:00 +0800
changeset 225260 baaa0c3ab8fd682d8828c83422cb2e0cea82f6f5
parent 225259 b0aee943388490bd64bf04b18eacee9bf886d581
child 225261 f30c30258703123f97f6efae4feeb4d98327853b
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey, bajaj
bugs1051633
milestone34.0a2
Bug 1051633: Make sure magic file descriptors in the content process will not be taken for other uses. r=khuey, a=bajaj
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)
 }
 
 static 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
@@ -96,23 +96,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)
 
@@ -385,19 +378,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))