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
This commit is contained in:
Cervantes Yu 2014-09-30 00:00:00 +08:00
parent b0cdc6c892
commit 1d61b4360f
5 changed files with 120 additions and 33 deletions

View File

@ -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<int> 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);
}

View File

@ -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<int> 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<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
{
@ -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<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
@ -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<FdArray>(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 */

View File

@ -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[<pid>] " prefix and the NUL.
char* fmt = (char*) InfallibleAllocPolicy::malloc_(strlen(aFmt) + 64);

View File

@ -102,13 +102,6 @@ Break(const char* aMsg);
#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;
@ -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

View File

@ -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<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));