| 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
 | 
| 
 |