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 3b7470b4176ac10e32ea650823061b3e63c4e431..e7e6448853f3366a0991c555fa5824e37c60d6d1 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> |
@@ -23,6 +25,7 @@ |
#include "base/bind.h" |
#include "base/logging.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" |
@@ -1806,6 +1809,121 @@ BPF_TEST(SandboxBPF, PthreadEquality, PthreadPolicyEquality) { PthreadTest(); } |
BPF_TEST(SandboxBPF, PthreadBitMask, PthreadPolicyBitMask) { PthreadTest(); } |
+// It is possible that libc does not define these, but the kernel supports this. |
+// We define them here anyway. The test will give up silently if it gets an |
+// EINVAL as a result of these not being supported. |
+#ifndef PTRACE_O_TRACESECCOMP |
+#define PTRACE_O_TRACESECCOMP 0x00000080 |
+#endif |
+#ifndef PTRACE_EVENT_SECCOMP |
+#define PTRACE_EVENT_SECCOMP 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 { |
+ if (!SandboxBPF::IsValidSyscallNumber(system_call_number)) { |
jln (very slow on Chromium)
2014/05/20 03:02:10
This if is not needed anymore (due to a recent CL)
rickyz (Google)
2014/05/20 22:34:01
Done.
|
+ return ErrorCode(ENOSYS); |
+ } |
+ 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(pid != -1); |
jln (very slow on Chromium)
2014/05/20 03:02:10
BPF_ASSERT_NE()
rickyz (Google)
2014/05/20 22:34:01
Done.
|
+ if (pid == 0) { |
+ pid_t my_pid = getpid(); |
+ BPF_ASSERT(ptrace(PTRACE_TRACEME, -1, NULL, NULL) != -1); |
+ BPF_ASSERT(raise(SIGSTOP) == 0); |
jln (very slow on Chromium)
2014/05/20 03:02:10
BPF_ASSERT_EQ() (etc. for the rest of the test).
rickyz (Google)
2014/05/20 22:34:01
Done.
|
+ SandboxBPF sandbox; |
+ sandbox.SetSandboxPolicy(new TraceAllPolicy); |
+ BPF_ASSERT(sandbox.StartSandbox(SandboxBPF::PROCESS_SINGLE_THREADED)); |
+ |
+ // getpid is allowed. |
+ BPF_ASSERT(syscall(__NR_getpid) == my_pid); |
+ |
+ // write is skipped and returns a fake value. |
+ BPF_ASSERT(write(STDERR_FILENO, "A", 1) == kExpectedReturnValue); |
+ |
+ // kill is rewritten to exit(kExpectedReturnValue). |
+ syscall(__NR_kill, my_pid, SIGKILL); |
+ |
+ // Should not be reached. |
+ BPF_ASSERT(0); |
jln (very slow on Chromium)
2014/05/20 03:02:10
s/0/false/
rickyz (Google)
2014/05/20 22:34:01
Done.
|
+ } |
+ |
+ int status; |
+ BPF_ASSERT(HANDLE_EINTR(waitpid(pid, &status, WUNTRACED)) != -1); |
+ BPF_ASSERT(WIFSTOPPED(status)); |
+ |
+ errno = 0; |
+ if (ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACESECCOMP) == -1) { |
+ // Give up (but don't fail) if PTRACE_O_TRACESECCOMP is not supported. |
jln (very slow on Chromium)
2014/05/20 03:02:10
It should now be supported anywhere seccomp-bpf is
rickyz (Google)
2014/05/20 22:34:01
Yeah, I don't see any release with seccomp-bpf and
|
+ BPF_ASSERT(errno == EINVAL); |
+ BPF_ASSERT(ptrace(PTRACE_CONT, pid, NULL, SIGKILL) != -1); |
+ return; |
+ } |
+ |
+ BPF_ASSERT(ptrace(PTRACE_CONT, pid, NULL, NULL) != -1); |
+ while (true) { |
+ BPF_ASSERT(HANDLE_EINTR(waitpid(pid, &status, 0)) != -1); |
+ if (WIFEXITED(status) || WIFSIGNALED(status)) { |
+ BPF_ASSERT(WIFEXITED(status) && |
+ WEXITSTATUS(status) == kExpectedReturnValue); |
+ break; |
+ } |
+ |
+ if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP || |
+ (status >> 16) != PTRACE_EVENT_SECCOMP) { |
+ BPF_ASSERT(ptrace(PTRACE_CONT, pid, NULL, NULL) != -1); |
+ continue; |
+ } |
+ |
+ unsigned long data; |
+ BPF_ASSERT(ptrace(PTRACE_GETEVENTMSG, pid, NULL, &data) != -1); |
+ BPF_ASSERT(data == kTraceData); |
+ |
+ struct pt_regs regs; |
+ BPF_ASSERT(ptrace(PTRACE_GETREGS, pid, NULL, ®s) != -1); |
+ switch (SECCOMP_PT_SYSCALL(regs)) { |
+ case __NR_write: |
+ // Skip write, make it return kExpectedReturnValue. |
+ SECCOMP_PT_SYSCALL(regs) = -1; |
+ SECCOMP_PT_RESULT(regs) = kExpectedReturnValue; |
+ BPF_ASSERT(ptrace(PTRACE_SETREGS, pid, NULL, ®s) != -1); |
+ break; |
+ |
+ case __NR_kill: |
+ // Rewrite to exit(kExpectedReturnValue). |
+ SECCOMP_PT_SYSCALL(regs) = __NR_exit; |
+ SECCOMP_PT_PARM1(regs) = kExpectedReturnValue; |
+ BPF_ASSERT(ptrace(PTRACE_SETREGS, pid, NULL, ®s) != -1); |
+ break; |
+ |
+ default: |
+ // Allow all other syscalls. |
+ break; |
+ } |
+ |
+ BPF_ASSERT(ptrace(PTRACE_CONT, pid, NULL, NULL) != -1); |
+ } |
+} |
+ |
} // namespace |
} // namespace sandbox |