diff options
author | dkegel@google.com <dkegel@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-06 19:59:36 +0000 |
---|---|---|
committer | dkegel@google.com <dkegel@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-06 19:59:36 +0000 |
commit | 4883a4e4209bab557f4ba40a002936acf755205f (patch) | |
tree | 8b1aef5a4e1fe01ee5d4e178838dec8e98356177 /base/zygote_manager_unittest.cc | |
parent | e2ffeae017f1c85d472e3f04e62e4cba21f6e9a8 (diff) | |
download | chromium_src-4883a4e4209bab557f4ba40a002936acf755205f.zip chromium_src-4883a4e4209bab557f4ba40a002936acf755205f.tar.gz chromium_src-4883a4e4209bab557f4ba40a002936acf755205f.tar.bz2 |
Prototype implementation of zygotes.
Limitations that need addressing still:
- Doesn't forcibly terminate children that should have exited but haven't
Enable with env var ENABLE_ZYGOTE_MANAGER=1.
BUG=11841
TEST=
start the browser, then make chrome and all .pak files unreadable; or alternately, start an installed browser, and uninstall the browser while it's running. Then create a new tab and browse to two new sites.
Here's an example script to hide and unhide the .pak files (note: do not move the directory they're in, that doesn't work):
#!/bin/sh
chmod_all() {
chmod $1 sconsbuild/Debug/chrome
for path in . locales obj/chrome/app/intermediate/repack obj/global_intermediate/* themes
do
chmod $1 sconsbuild/Debug/$path/*.pak
done
}
case $1 in
hide) chmod_all 000 ;;
show) chmod_all 755 ;;
esac
Review URL: http://codereview.chromium.org/115773
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17840 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/zygote_manager_unittest.cc')
-rw-r--r-- | base/zygote_manager_unittest.cc | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/base/zygote_manager_unittest.cc b/base/zygote_manager_unittest.cc new file mode 100644 index 0000000..d879d1a --- /dev/null +++ b/base/zygote_manager_unittest.cc @@ -0,0 +1,266 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/zygote_manager.h" + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "base/eintr_wrapper.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/process_util.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" + +#include "testing/gtest/include/gtest/gtest.h" + +using file_util::Delete; +using file_util::WriteFile; +using file_util::ReadFileToString; +using file_util::GetCurrentDirectory; + +#if defined(OS_LINUX) +// ZygoteManager is only used on Linux at the moment + +typedef testing::Test ZygoteManagerTest; + +TEST_F(ZygoteManagerTest, Ping) { + base::ZygoteManager zm; + + scoped_ptr< std::vector<std::string> > new_argv; + new_argv.reset(zm.Start()); + EXPECT_TRUE(new_argv.get() == NULL); + // Measure round trip time + base::TimeDelta delta; + EXPECT_EQ(zm.Ping(&delta), true); + EXPECT_LT(delta.InMilliseconds(), 5000); +} + +TEST_F(ZygoteManagerTest, SpawnChild) { + base::ZygoteManager zm; + const int kDummyChildExitCode = 39; + + scoped_ptr< std::vector<std::string> > new_argv; + new_argv.reset(zm.Start()); + if (new_argv.get() == NULL) { + // original process + // Launch a child process + std::vector<std::string> myargs; + myargs.push_back(std::string("0thArg")); + myargs.push_back(std::string("1stArg")); + base::file_handle_mapping_vector no_files; + pid_t child = zm.LongFork(myargs, no_files); + EXPECT_NE(child, -1); + EXPECT_NE(child, 0); + LOG(INFO) << "child pid " << child; + + // ZygoteManager doesn't support waiting for exit status + int status; + int err = HANDLE_EINTR(waitpid(child, &status, 0)); + EXPECT_EQ(-1, err); + EXPECT_EQ(ECHILD, errno); + } else { + LOG(INFO) << "Hello from child!"; + // child process + std::string arg0(new_argv.get()->at(0)); + std::string arg1(new_argv.get()->at(1)); + EXPECT_EQ(arg0, std::string("0thArg")); + EXPECT_EQ(arg1, std::string("1stArg")); + exit(kDummyChildExitCode); + } +} + +TEST_F(ZygoteManagerTest, MapFile) { + base::ZygoteManager zm; + const int kDummyChildExitCode = 39; + const int kSpecialFDSlot = 5; + + scoped_ptr< std::vector<std::string> > new_argv; + new_argv.reset(zm.Start()); + if (new_argv.get() == NULL) { + // original process + // Launch a child process + std::vector<std::string> myargs; + myargs.push_back(std::string("0thArg")); + myargs.push_back(std::string("1stArg")); + base::file_handle_mapping_vector fds_to_map; + int fd = open("/tmp/zygote_manager_unittest.tmp", O_CREAT|O_RDWR|O_TRUNC, 0644); + fds_to_map.push_back(std::pair<int, int>(fd, kSpecialFDSlot)); + pid_t child = zm.LongFork(myargs, fds_to_map); + EXPECT_NE(child, -1); + EXPECT_NE(child, 0); + // FIXME: really wait for child + sleep(3); + char buf[100]; + + // Expect fd to be seeked to end, so reading without seeking should fail + off_t loc = lseek(fd, 0L, SEEK_CUR); + EXPECT_EQ(3, loc); + + memset(buf, 0, sizeof(buf)); + int nread = read(fd, buf, 5); + EXPECT_EQ(0, nread); + + // Try again from beginning + lseek(fd, 0L, SEEK_SET); + memset(buf, 0, sizeof(buf)); + nread = read(fd, buf, 2); + EXPECT_EQ(2, nread); + EXPECT_EQ(strncmp(buf, "hi", 2), 0); + close(fd); + } else { + // child process + // Write three bytes; this happens to seek the file descriptor to the end. + int nwritten = write(kSpecialFDSlot, "hi\n", 3); + EXPECT_EQ(3, nwritten); + exit(kDummyChildExitCode); + } +} + +TEST_F(ZygoteManagerTest, OpenFile) { + base::ZygoteManager zm; + + scoped_ptr< std::vector<std::string> > new_argv; + new_argv.reset(zm.Start()); + EXPECT_EQ(NULL, new_argv.get()); + + const char kSomeText[] = "foobar\n"; + + // Verify that we disallow nonabsolute paths. + FilePath badfilepath(FilePath::kCurrentDirectory); + badfilepath = badfilepath.AppendASCII("zygote_manager_test.pak"); + ASSERT_FALSE(badfilepath.IsAbsolute()); + EXPECT_NE(-1, WriteFile(badfilepath, kSomeText, strlen(kSomeText))); + std::string badfilename = WideToASCII(badfilepath.ToWStringHack()); + int fd = zm.OpenFile(badfilename); + EXPECT_EQ(-1, fd); + EXPECT_TRUE(Delete(badfilepath, false)); + + // Verify that we disallow non-plain files. + ASSERT_TRUE(GetCurrentDirectory(&badfilepath)); + badfilepath = badfilepath.AppendASCII("zygote_manager_test.pak"); + std::string badfilenameA = WideToASCII(badfilepath.ToWStringHack()); + EXPECT_EQ(0, mkfifo(badfilenameA.c_str(), 0644)); + badfilename = WideToASCII(badfilepath.ToWStringHack()); + fd = zm.OpenFile(badfilename); + ASSERT_EQ(-1, fd); + EXPECT_TRUE(Delete(badfilepath, false)); + + // Verify that we disallow files not ending in .pak. + ASSERT_TRUE(GetCurrentDirectory(&badfilepath)); + badfilepath = badfilepath.AppendASCII("zygote_manager_test.tmp"); + ASSERT_NE(-1, WriteFile(badfilepath, kSomeText, strlen(kSomeText))); + badfilename = WideToASCII(badfilepath.ToWStringHack()); + fd = zm.OpenFile(badfilename); + ASSERT_EQ(-1, fd); + EXPECT_TRUE(Delete(badfilepath, false)); + + // Verify that we disallow files in /etc + badfilepath = FilePath(FILE_PATH_LITERAL("/")); + badfilepath = badfilepath.AppendASCII("etc"); + badfilepath = badfilepath.AppendASCII("hosts"); + EXPECT_TRUE(badfilepath.IsAbsolute()); + badfilename = WideToASCII(badfilepath.ToWStringHack()); + fd = zm.OpenFile(badfilename); + ASSERT_EQ(-1, fd); + + // Verify that we disallow files in /dev + badfilepath = FilePath(FILE_PATH_LITERAL("/")); + badfilepath = badfilepath.AppendASCII("dev"); + badfilepath = badfilepath.AppendASCII("tty"); + EXPECT_TRUE(badfilepath.IsAbsolute()); + badfilename = WideToASCII(badfilepath.ToWStringHack()); + fd = zm.OpenFile(badfilename); + ASSERT_EQ(-1, fd); + + // Verify that we allow absolute paths with filename ending in .pak, + // and that we can open them a second time even if they were + // deleted after we opened them the first time. + // Because of our restrictive filename checks, can't put + // test file in /tmp, so put it in current directory. + FilePath goodfilepath; + ASSERT_TRUE(GetCurrentDirectory(&goodfilepath)); + goodfilepath = goodfilepath.AppendASCII("zygote_manager_test.pak"); + ASSERT_NE(-1, WriteFile(goodfilepath, kSomeText, strlen(kSomeText))); + std::string goodfilename = WideToASCII(goodfilepath.ToWStringHack()); + for (int i = 0; i < 2; i++) { + fd = zm.OpenFile(goodfilename); + ASSERT_NE(-1, fd); + char buf[sizeof(kSomeText)]; + // Can't use read because it depends on file position. + // (In practice these files are mmapped.) + int nread = pread(fd, buf, strlen(kSomeText), 0); + ASSERT_EQ(strlen(kSomeText), static_cast<size_t>(nread)); + EXPECT_EQ(0, strncmp(buf, kSomeText, strlen(kSomeText))); + EXPECT_EQ(0, close(fd)); + + // oddly, our Delete returns true for nonexistant files. + EXPECT_EQ(true, Delete(goodfilepath, false)); + } +} + +TEST_F(ZygoteManagerTest, ChildOpenFile) { + base::ZygoteManager zm; + const int kDummyChildExitCode = 39; + + const char kSomeText[] = "foobar\n"; + + FilePath resultfilepath(FILE_PATH_LITERAL("/tmp")); + resultfilepath = resultfilepath.AppendASCII("zygote_manager_test_result.tmp"); + EXPECT_EQ(true, Delete(resultfilepath, false)); + + scoped_ptr< std::vector<std::string> > new_argv; + new_argv.reset(zm.Start()); + if (new_argv.get() == NULL) { + // original process + // Launch a child process + std::vector<std::string> myargs; + base::file_handle_mapping_vector no_files; + pid_t child = zm.LongFork(myargs, no_files); + EXPECT_NE(child, -1); + EXPECT_NE(child, 0); + LOG(INFO) << "child pid " << child; + + // Wait for resultfile to be created + std::string result; + int nloops = 0; + while (!ReadFileToString(resultfilepath, &result)) { + sleep(1); + ++nloops; + ASSERT_NE(10, nloops); + } + ASSERT_EQ(result, std::string(kSomeText)); + + EXPECT_EQ(true, Delete(resultfilepath, false)); + + } else { + LOG(INFO) << "Hello from child!"; + + FilePath goodfilepath; + ASSERT_TRUE(GetCurrentDirectory(&goodfilepath)); + goodfilepath = goodfilepath.AppendASCII("zygote_manager_test.pak"); + ASSERT_NE(-1, WriteFile(goodfilepath, kSomeText, strlen(kSomeText))); + std::string goodfilename = WideToASCII(goodfilepath.ToWStringHack()); + int fd = zm.OpenFile(goodfilename); + EXPECT_EQ(true, Delete(goodfilepath, false)); + ASSERT_NE(-1, fd); + char buf[sizeof(kSomeText)]; + // Can't use read because it depends on file position. + // (In practice these files are mmapped.) + int nread = pread(fd, buf, strlen(kSomeText), 0); + ASSERT_EQ(strlen(kSomeText), static_cast<size_t>(nread)); + EXPECT_EQ(0, strncmp(buf, kSomeText, strlen(kSomeText))); + EXPECT_EQ(0, close(fd)); + + ASSERT_NE(-1, WriteFile(resultfilepath, kSomeText, strlen(kSomeText))); + + exit(kDummyChildExitCode); + } +} + +#endif |