From 1d61b4360fe6efdc5ace65a1d68668a8c3122bb4 Mon Sep 17 00:00:00 2001 From: Cervantes Yu Date: Tue, 30 Sep 2014 00:00:00 +0800 Subject: [PATCH] Bug 1051633: Make sure magic file descriptors in the content process will not be taken for other uses. r=khuey --HG-- extra : rebase_source : 5e2dedb855dd5e0e6637d6f42c80c69df8081971 --- b2g/app/B2GLoader.cpp | 57 ++++++++++++++++++++++---- ipc/glue/ProcessUtils_linux.cpp | 72 +++++++++++++++++++++++++++------ memory/replace/dmd/DMD.cpp | 7 +++- xpcom/base/nsDebugImpl.cpp | 10 ----- xpcom/build/nsXULAppAPI.h | 7 +++- 5 files changed, 120 insertions(+), 33 deletions(-) diff --git a/b2g/app/B2GLoader.cpp b/b2g/app/B2GLoader.cpp index 7e0aa962784..8846ac794ef 100644 --- a/b2g/app/B2GLoader.cpp +++ b/b2g/app/B2GLoader.cpp @@ -26,7 +26,6 @@ #define ASSERT(x) if (!(x)) { MOZ_CRASH(); } - // Functions being loaded by XPCOMGlue XRE_ProcLoaderServiceRunType XRE_ProcLoaderServiceRun; XRE_ProcLoaderClientInitType XRE_ProcLoaderClientInit; @@ -43,6 +42,10 @@ static const nsDynamicFunctionLoad kXULFuncs[] = { { nullptr, nullptr } }; +typedef mozilla::Vector FdArray; +static const int kReservedFileDescriptors = 5; +static const int kBeginReserveFileDescriptor = STDERR_FILENO + 1; + static int GetDirnameSlash(const char *aPath, char *aOutDir, int aMaxLen) { @@ -69,7 +72,7 @@ GetXPCOMPath(const char *aProgram, char *aOutPath, int aMaxLen) 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; @@ -181,7 +184,7 @@ LoadStaticData(int argc, const char *argv[]) * 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 @@ -212,15 +215,50 @@ RunProcesses(int argc, const char *argv[]) * 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 @@ -234,7 +272,12 @@ RunProcesses(int argc, const char *argv[]) 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. @@ -244,5 +287,5 @@ main(int argc, const char* argv[]) return 255; } - return RunProcesses(argc, argv); + return RunProcesses(argc, argv, reservedFds); } diff --git a/ipc/glue/ProcessUtils_linux.cpp b/ipc/glue/ProcessUtils_linux.cpp index 42998991bd7..ca401a57e55 100644 --- a/ipc/glue/ProcessUtils_linux.cpp +++ b/ipc/glue/ProcessUtils_linux.cpp @@ -27,7 +27,10 @@ #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" @@ -37,11 +40,12 @@ int content_process_main(int argc, char *argv[]); -extern bool gDisableAndroidLog; +typedef mozilla::Vector FdArray; #endif /* MOZ_B2G_LOADER */ namespace mozilla { + namespace ipc { void SetThisProcessName(const char *aName) @@ -98,9 +102,18 @@ static pid_t sProcLoaderPid = 0; static int sProcLoaderChannelFd = -1; static PProcLoaderParent *sProcLoaderParent = nullptr; static MessageLoop *sProcLoaderLoop = nullptr; +static mozilla::UniquePtr 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 { @@ -139,6 +152,14 @@ ProcLoaderParent::RecvLoadComplete(const int32_t &aPid, 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() { @@ -292,6 +313,7 @@ class ProcLoaderRunnerBase { public: virtual int DoWork() = 0; + virtual ~ProcLoaderRunnerBase() {} }; @@ -305,7 +327,6 @@ ProcLoaderNoopRunner::DoWork() { return 0; } - /** * The runner to load Nuwa at the current process. */ @@ -336,9 +357,12 @@ 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(); @@ -346,12 +370,28 @@ ProcLoaderLoadRunner::ShuffleFds() 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 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 @@ -482,8 +522,13 @@ public: */ 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(mozilla::Move(aReservedFds)); + ScopedLogging logging; char **_argv; @@ -498,11 +543,8 @@ ProcLoaderServiceRun(pid_t aPeerPid, int aFd, gArgc = aArgc; { - gDisableAndroidLog = true; - nsresult rv = XRE_InitCommandLine(aArgc, _argv); if (NS_FAILED(rv)) { - gDisableAndroidLog = false; MOZ_CRASH(); } @@ -530,8 +572,6 @@ ProcLoaderServiceRun(pid_t aPeerPid, int aFd, BackgroundHangMonitor::Allow(); XRE_DeinitCommandLine(); - - gDisableAndroidLog = false; } MOZ_ASSERT(sProcLoaderDispatchedTask != nullptr); @@ -555,16 +595,22 @@ ProcLoaderServiceRun(pid_t aPeerPid, int aFd, #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 */ diff --git a/memory/replace/dmd/DMD.cpp b/memory/replace/dmd/DMD.cpp index 909e5baaa12..01c4378c781 100644 --- a/memory/replace/dmd/DMD.cpp +++ b/memory/replace/dmd/DMD.cpp @@ -201,7 +201,12 @@ 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[] " prefix and the NUL. char* fmt = (char*) InfallibleAllocPolicy::malloc_(strlen(aFmt) + 64); diff --git a/xpcom/base/nsDebugImpl.cpp b/xpcom/base/nsDebugImpl.cpp index dbcf58f67d9..f61277a8d7e 100644 --- a/xpcom/base/nsDebugImpl.cpp +++ b/xpcom/base/nsDebugImpl.cpp @@ -102,13 +102,6 @@ Break(const char* aMsg); #include #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; @@ -391,9 +384,6 @@ NS_DebugBreak(uint32_t aSeverity, const char* aStr, const char* aExpr, #endif #ifdef ANDROID -#ifdef MOZ_B2G_LOADER - if (!gDisableAndroidLog) -#endif __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", buf.buffer); #endif diff --git a/xpcom/build/nsXULAppAPI.h b/xpcom/build/nsXULAppAPI.h index eb60f2a8d76..a08e99418e3 100644 --- a/xpcom/build/nsXULAppAPI.h +++ b/xpcom/build/nsXULAppAPI.h @@ -16,6 +16,7 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/Assertions.h" +#include "mozilla/Vector.h" /** * A directory service key which provides the platform-correct "application @@ -470,9 +471,11 @@ XRE_API(WindowsEnvironmentType, #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& aReservedFds)); XRE_API(void, - XRE_ProcLoaderClientInit, (pid_t, int)); + XRE_ProcLoaderClientInit, (pid_t, int, + mozilla::Vector& aReservedFds)); XRE_API(void, XRE_ProcLoaderPreload, (const char* aProgramDir, const nsXREAppData* aAppData));