diff options
author | rickyz <rickyz@chromium.org> | 2015-01-13 14:59:48 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-01-13 23:00:58 +0000 |
commit | f1eb9ccb53367a38340b05caa74769c7b492ad73 (patch) | |
tree | e9a1f4ca07910d4bf17c44d7d8b02968ab233590 /sandbox | |
parent | ecbbe049cd76030e07a7fe53fc5bd9de4c0d8804 (diff) | |
download | chromium_src-f1eb9ccb53367a38340b05caa74769c7b492ad73.zip chromium_src-f1eb9ccb53367a38340b05caa74769c7b492ad73.tar.gz chromium_src-f1eb9ccb53367a38340b05caa74769c7b492ad73.tar.bz2 |
Move ForkWithFlags from sandbox/ to base/ and plug it into LaunchProcess.
ForkWithFlags is a wrapper around the clone syscall that uses the libc clone
wrapper, and thus updates the libc's pid cache if it has one (using sys_clone
directly does not update the pid cache, so getpid may return an incorrect
result in the child). This exposes the ability to set clone flags, which is
needed to use Linux namespaces.
BUG=312380
Review URL: https://codereview.chromium.org/840893003
Cr-Commit-Position: refs/heads/master@{#311356}
Diffstat (limited to 'sandbox')
-rw-r--r-- | sandbox/linux/services/syscall_wrappers.cc | 89 | ||||
-rw-r--r-- | sandbox/linux/services/syscall_wrappers.h | 12 | ||||
-rw-r--r-- | sandbox/linux/services/syscall_wrappers_unittest.cc | 29 |
3 files changed, 6 insertions, 124 deletions
diff --git a/sandbox/linux/services/syscall_wrappers.cc b/sandbox/linux/services/syscall_wrappers.cc index 1840c96..efadbaf 100644 --- a/sandbox/linux/services/syscall_wrappers.cc +++ b/sandbox/linux/services/syscall_wrappers.cc @@ -29,9 +29,11 @@ pid_t sys_gettid(void) { return syscall(__NR_gettid); } -namespace { - -bool CloneArgumentsValid(unsigned long flags, pid_t* ptid, pid_t* ctid) { +long sys_clone(unsigned long flags, + decltype(nullptr) child_stack, + pid_t* ptid, + pid_t* ctid, + decltype(nullptr) tls) { const bool clone_tls_used = flags & CLONE_SETTLS; const bool invalid_ctid = (flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) && !ctid; @@ -39,65 +41,7 @@ bool CloneArgumentsValid(unsigned long flags, pid_t* ptid, pid_t* ctid) { // We do not support CLONE_VM. const bool clone_vm_used = flags & CLONE_VM; - - return !(clone_tls_used || invalid_ctid || invalid_ptid || clone_vm_used); -} - -bool IsRunningOnValgrind() { - return RUNNING_ON_VALGRIND; -} - -// This function runs on the stack specified on the clone call. It uses longjmp -// to switch back to the original stack so the child can return from sys_clone. -int CloneHelper(void* arg) { - jmp_buf* env_ptr = reinterpret_cast<jmp_buf*>(arg); - longjmp(*env_ptr, 1); - - // Should not be reached. - RAW_CHECK(false); - return 1; -} - -// This function is noinline to ensure that stack_buf is below the stack pointer -// that is saved when setjmp is called below. This is needed because when -// compiled with FORTIFY_SOURCE, glibc's longjmp checks that the stack is moved -// upwards. See crbug.com/442912 for more details. -#if defined(ADDRESS_SANITIZER) -// Disable AddressSanitizer instrumentation for this function to make sure -// |stack_buf| is allocated on thread stack instead of ASan's fake stack. -// Under ASan longjmp() will attempt to clean up the area between the old and -// new stack pointers and print a warning that may confuse the user. -__attribute__((no_sanitize_address)) -#endif -NOINLINE pid_t CloneAndLongjmpInChild(unsigned long flags, - pid_t* ptid, - pid_t* ctid, - jmp_buf* env) { - // We use the libc clone wrapper instead of making the syscall - // directly because making the syscall may fail to update the libc's - // internal pid cache. The libc interface unfortunately requires - // specifying a new stack, so we use setjmp/longjmp to emulate - // fork-like behavior. - char stack_buf[PTHREAD_STACK_MIN]; -#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \ - defined(ARCH_CPU_MIPS64_FAMILY) || defined(ARCH_CPU_MIPS_FAMILY) - // The stack grows downward. - void* stack = stack_buf + sizeof(stack_buf); -#else -#error "Unsupported architecture" -#endif - return clone(&CloneHelper, stack, flags, env, ptid, nullptr, ctid); -} - -} // namespace - -long sys_clone(unsigned long flags, - decltype(nullptr) child_stack, - pid_t* ptid, - pid_t* ctid, - decltype(nullptr) tls) { - - if (!CloneArgumentsValid(flags, ptid, ctid)) { + if (clone_tls_used || invalid_ctid || invalid_ptid || clone_vm_used) { RAW_LOG(FATAL, "Invalid usage of sys_clone"); } @@ -116,27 +60,6 @@ long sys_clone(unsigned long flags) { return sys_clone(flags, nullptr, nullptr, nullptr, nullptr); } -pid_t ForkWithFlags(unsigned long flags, pid_t* ptid, pid_t* ctid) { - if (!CloneArgumentsValid(flags, ptid, ctid)) { - RAW_LOG(FATAL, "Invalid usage of ForkWithFlags"); - } - - // Valgrind's clone implementation does not support specifiying a child_stack - // without CLONE_VM, so we cannot use libc's clone wrapper when running under - // Valgrind. As a result, the libc pid cache may be incorrect under Valgrind. - // See crbug.com/442817 for more details. - if (IsRunningOnValgrind()) { - return sys_clone(flags, nullptr, ptid, ctid, nullptr); - } - - jmp_buf env; - if (setjmp(env) == 0) { - return CloneAndLongjmpInChild(flags, ptid, ctid, &env); - } - - return 0; -} - void sys_exit_group(int status) { syscall(__NR_exit_group, status); } diff --git a/sandbox/linux/services/syscall_wrappers.h b/sandbox/linux/services/syscall_wrappers.h index 2001f38..7f703e0 100644 --- a/sandbox/linux/services/syscall_wrappers.h +++ b/sandbox/linux/services/syscall_wrappers.h @@ -33,18 +33,6 @@ SANDBOX_EXPORT long sys_clone(unsigned long flags, pid_t* ctid, decltype(nullptr) regs); -// A wrapper for clone with fork-like behavior, meaning that it returns the -// child's pid in the parent and 0 in the child. |flags|, |ptid|, and |ctid| are -// as in the clone system call (the CLONE_VM flag is not supported). -// -// This function uses the libc clone wrapper (which updates libc's pid cache) -// internally, so callers may expect things like getpid() to work correctly -// after in both the child and parent. An exception is when this code is run -// under Valgrind. Valgrind does not support the libc clone wrapper, so the libc -// pid cache may be incorrect after this function is called under Valgrind. -SANDBOX_EXPORT pid_t -ForkWithFlags(unsigned long flags, pid_t* ptid, pid_t* ctid); - SANDBOX_EXPORT void sys_exit_group(int status); // The official system call takes |args| as void* (in order to be extensible), diff --git a/sandbox/linux/services/syscall_wrappers_unittest.cc b/sandbox/linux/services/syscall_wrappers_unittest.cc index bac3881..a8b48fe 100644 --- a/sandbox/linux/services/syscall_wrappers_unittest.cc +++ b/sandbox/linux/services/syscall_wrappers_unittest.cc @@ -60,35 +60,6 @@ TEST(SyscallWrappers, CloneChildSettid) { EXPECT_EQ(kSuccessExit, WEXITSTATUS(status)); } -TEST(SyscallWrappers, ForkWithFlagsUpdatesPidCache) { - // The libc clone function, which allows ForkWithFlags to keep the pid cache - // up to date, does not work on Valgrind. - if (IsRunningOnValgrind()) { - return; - } - - // Warm up the libc pid cache, if there is one. - ASSERT_EQ(sys_getpid(), getpid()); - - pid_t ctid = 0; - pid_t pid = ForkWithFlags(CLONE_CHILD_SETTID | SIGCHLD, nullptr, &ctid); - - const int kSuccessExit = 0; - if (0 == pid) { - // In child. Check both the raw getpid syscall and the libc getpid wrapper - // (which may rely on a pid cache). - if (sys_getpid() == ctid && getpid() == ctid) - _exit(kSuccessExit); - _exit(1); - } - - ASSERT_NE(-1, pid); - int status = 0; - ASSERT_EQ(pid, HANDLE_EINTR(waitpid(pid, &status, 0))); - ASSERT_TRUE(WIFEXITED(status)); - EXPECT_EQ(kSuccessExit, WEXITSTATUS(status)); -} - } // namespace } // namespace sandbox |