| Index: sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
|
| diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
|
| index 0341d2f95db055f0eba10bb68de5e172d5f92b54..075604de7b8f1c10a0135bfbd6afdb6432136dcd 100644
|
| --- a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
|
| +++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
|
| @@ -5,7 +5,9 @@
|
| #include <errno.h>
|
| #include <pthread.h>
|
| #include <sched.h>
|
| +#include <signal.h>
|
| #include <sys/prctl.h>
|
| +#include <sys/ptrace.h>
|
| #include <sys/syscall.h>
|
| #include <sys/time.h>
|
| #include <sys/types.h>
|
| @@ -24,6 +26,7 @@
|
| #include "base/logging.h"
|
| #include "base/macros.h"
|
| #include "base/memory/scoped_ptr.h"
|
| +#include "base/posix/eintr_wrapper.h"
|
| #include "build/build_config.h"
|
| #include "sandbox/linux/seccomp-bpf/bpf_tests.h"
|
| #include "sandbox/linux/seccomp-bpf/syscall.h"
|
| @@ -1908,6 +1911,122 @@ BPF_TEST_C(SandboxBPF, PthreadBitMask, PthreadPolicyBitMask) {
|
| PthreadTest();
|
| }
|
|
|
| +// libc might not define these even though the kernel supports it.
|
| +#ifndef PTRACE_O_TRACESECCOMP
|
| +#define PTRACE_O_TRACESECCOMP 0x00000080
|
| +#endif
|
| +
|
| +#ifdef PTRACE_EVENT_SECCOMP
|
| +#define IS_SECCOMP_EVENT(status) ((status >> 16) == PTRACE_EVENT_SECCOMP)
|
| +#else
|
| +// When Debian/Ubuntu backported seccomp-bpf support into earlier kernels, they
|
| +// changed the value of PTRACE_EVENT_SECCOMP from 7 to 8, since 7 was taken by
|
| +// PTRACE_EVENT_STOP (upstream chose to renumber PTRACE_EVENT_STOP to 128). If
|
| +// PTRACE_EVENT_SECCOMP isn't defined, we have no choice but to consider both
|
| +// values here.
|
| +#define IS_SECCOMP_EVENT(status) ((status >> 16) == 7 || (status >> 16) == 8)
|
| +#endif
|
| +
|
| +const uint16_t kTraceData = 0xcc;
|
| +
|
| +class TraceAllPolicy : public SandboxBPFPolicy {
|
| + public:
|
| + TraceAllPolicy() {}
|
| + virtual ~TraceAllPolicy() {}
|
| +
|
| + virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler,
|
| + int system_call_number) const OVERRIDE {
|
| + return ErrorCode(ErrorCode::ERR_TRACE + kTraceData);
|
| + }
|
| +
|
| + private:
|
| + DISALLOW_COPY_AND_ASSIGN(TraceAllPolicy);
|
| +};
|
| +
|
| +SANDBOX_TEST(SandboxBPF, DISABLE_ON_TSAN(SeccompRetTrace)) {
|
| + if (SandboxBPF::SupportsSeccompSandbox(-1) !=
|
| + sandbox::SandboxBPF::STATUS_AVAILABLE) {
|
| + return;
|
| + }
|
| +
|
| + pid_t pid = fork();
|
| + BPF_ASSERT_NE(-1, pid);
|
| + if (pid == 0) {
|
| + pid_t my_pid = getpid();
|
| + BPF_ASSERT_NE(-1, ptrace(PTRACE_TRACEME, -1, NULL, NULL));
|
| + BPF_ASSERT_EQ(0, raise(SIGSTOP));
|
| + SandboxBPF sandbox;
|
| + sandbox.SetSandboxPolicy(new TraceAllPolicy);
|
| + BPF_ASSERT(sandbox.StartSandbox(SandboxBPF::PROCESS_SINGLE_THREADED));
|
| +
|
| + // getpid is allowed.
|
| + BPF_ASSERT_EQ(my_pid, syscall(__NR_getpid));
|
| +
|
| + // write to stdout is skipped and returns a fake value.
|
| + BPF_ASSERT_EQ(kExpectedReturnValue,
|
| + syscall(__NR_write, STDOUT_FILENO, "A", 1));
|
| +
|
| + // kill is rewritten to exit(kExpectedReturnValue).
|
| + syscall(__NR_kill, my_pid, SIGKILL);
|
| +
|
| + // Should not be reached.
|
| + BPF_ASSERT(false);
|
| + }
|
| +
|
| + int status;
|
| + BPF_ASSERT(HANDLE_EINTR(waitpid(pid, &status, WUNTRACED)) != -1);
|
| + BPF_ASSERT(WIFSTOPPED(status));
|
| +
|
| + BPF_ASSERT_NE(-1, ptrace(PTRACE_SETOPTIONS, pid, NULL,
|
| + reinterpret_cast<void*>(PTRACE_O_TRACESECCOMP)));
|
| + BPF_ASSERT_NE(-1, ptrace(PTRACE_CONT, pid, NULL, NULL));
|
| + while (true) {
|
| + BPF_ASSERT(HANDLE_EINTR(waitpid(pid, &status, 0)) != -1);
|
| + if (WIFEXITED(status) || WIFSIGNALED(status)) {
|
| + BPF_ASSERT(WIFEXITED(status));
|
| + BPF_ASSERT_EQ(kExpectedReturnValue, WEXITSTATUS(status));
|
| + break;
|
| + }
|
| +
|
| + if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP ||
|
| + !IS_SECCOMP_EVENT(status)) {
|
| + BPF_ASSERT_NE(-1, ptrace(PTRACE_CONT, pid, NULL, NULL));
|
| + continue;
|
| + }
|
| +
|
| + unsigned long data;
|
| + BPF_ASSERT_NE(-1, ptrace(PTRACE_GETEVENTMSG, pid, NULL, &data));
|
| + BPF_ASSERT_EQ(kTraceData, data);
|
| +
|
| + regs_struct regs;
|
| + BPF_ASSERT_NE(-1, ptrace(PTRACE_GETREGS, pid, NULL, ®s));
|
| + switch (SECCOMP_PT_SYSCALL(regs)) {
|
| + case __NR_write:
|
| + // Skip writes to stdout, make it return kExpectedReturnValue. Allow
|
| + // writes to stderr so that BPF_ASSERT messages show up.
|
| + if (SECCOMP_PT_PARM1(regs) == STDOUT_FILENO) {
|
| + SECCOMP_PT_SYSCALL(regs) = -1;
|
| + SECCOMP_PT_RESULT(regs) = kExpectedReturnValue;
|
| + BPF_ASSERT_NE(-1, ptrace(PTRACE_SETREGS, pid, NULL, ®s));
|
| + }
|
| + break;
|
| +
|
| + case __NR_kill:
|
| + // Rewrite to exit(kExpectedReturnValue).
|
| + SECCOMP_PT_SYSCALL(regs) = __NR_exit;
|
| + SECCOMP_PT_PARM1(regs) = kExpectedReturnValue;
|
| + BPF_ASSERT_NE(-1, ptrace(PTRACE_SETREGS, pid, NULL, ®s));
|
| + break;
|
| +
|
| + default:
|
| + // Allow all other syscalls.
|
| + break;
|
| + }
|
| +
|
| + BPF_ASSERT_NE(-1, ptrace(PTRACE_CONT, pid, NULL, NULL));
|
| + }
|
| +}
|
| +
|
| } // namespace
|
|
|
| } // namespace sandbox
|
|
|