1 #include "sandboxed_api/sandbox2/regs.h"
2
3 #include <linux/filter.h>
4 #include <linux/seccomp.h>
5 #include <sys/prctl.h>
6 #include <sys/ptrace.h>
7 #include <sys/socket.h>
8 #include <sys/wait.h>
9 #include <syscall.h>
10 #include <unistd.h>
11
12 #include <cerrno>
13 #include <cstdint>
14 #include <cstdlib>
15 #include <vector>
16
17 #include "gmock/gmock.h"
18 #include "gtest/gtest.h"
19 #include "absl/log/check.h"
20 #include "sandboxed_api/config.h"
21 #include "sandboxed_api/sandbox2/sanitizer.h"
22 #include "sandboxed_api/sandbox2/syscall.h"
23 #include "sandboxed_api/sandbox2/util.h"
24 #include "sandboxed_api/sandbox2/util/bpf_helper.h"
25 #include "sandboxed_api/util/status_matchers.h"
26
27 namespace sandbox2 {
28 namespace {
29
30 using ::sapi::IsOk;
31
32 #define __WPTRACEEVENT(x) ((x & 0xff0000) >> 16)
33
TEST(RegsTest,SkipSyscallWorks)34 TEST(RegsTest, SkipSyscallWorks) {
35 std::vector<sock_filter> policy = {
36 LOAD_SYSCALL_NR,
37 JEQ32(__NR_getpid, TRACE(0)),
38 ALLOW,
39 };
40 sock_fprog prog = {
41 .len = static_cast<uint8_t>(policy.size()),
42 .filter = policy.data(),
43 };
44 // Create socketpair for synchronization
45 int sv[2];
46 ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, sv), 0);
47 // Fork a child process to run the syscalls in.
48 pid_t ppid = util::Syscall(__NR_gettid);
49 pid_t pid = fork();
50 ASSERT_NE(pid, -1);
51 char c = 'C';
52 if (pid == 0) {
53 // Get ready for being ptraced.
54 sanitizer::WaitForSanitizer();
55 CHECK_EQ(prctl(PR_SET_DUMPABLE, 1), 0);
56 prctl(PR_SET_PTRACER, ppid);
57 CHECK_EQ(prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0), 0);
58 CHECK_EQ(prctl(PR_SET_KEEPCAPS, 0), 0);
59 // Notify parent that we're ready for ptrace.
60 CHECK_EQ(write(sv[0], &c, 1), 1);
61 // Apply seccomp policy
62 CHECK_EQ(util::Syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, 0,
63 reinterpret_cast<uintptr_t>(&prog)),
64 0);
65 // Wait for tracer to be attached.
66 CHECK_EQ(read(sv[0], &c, 1), 1);
67 // Run the test syscall
68 errno = 0;
69 util::Syscall(__NR_getpid, 123, reinterpret_cast<uintptr_t>(&c), 1);
70 _Exit(errno == ENOENT ? 0 : 1);
71 }
72 // Wait for child to be ready for ptrace
73 ASSERT_EQ(read(sv[1], &c, 1), 1);
74 ASSERT_EQ(ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_TRACESECCOMP), 0);
75 // Notify child it has been ptraced.
76 ASSERT_EQ(write(sv[1], &c, 1), 1);
77 // Wait for seccomp TRACE stop
78 int status;
79 ASSERT_EQ(waitpid(pid, &status, __WNOTHREAD | __WALL | WUNTRACED), pid);
80 ASSERT_TRUE(WIFSTOPPED(status));
81 ASSERT_EQ(__WPTRACEEVENT(status), PTRACE_EVENT_SECCOMP);
82 // Fetch the registers
83 Regs regs(pid);
84 ASSERT_THAT(regs.Fetch(), IsOk());
85 // Check syscall arguments
86 Syscall syscall = regs.ToSyscall(sapi::host_cpu::Architecture());
87 EXPECT_EQ(syscall.nr(), __NR_getpid);
88 EXPECT_EQ(syscall.args()[0], 123);
89 EXPECT_EQ(syscall.args()[1], reinterpret_cast<uintptr_t>(&c));
90 EXPECT_EQ(syscall.args()[2], 1);
91 // Skip syscall
92 ASSERT_THAT(regs.SkipSyscallReturnValue(-ENOENT), IsOk());
93 // Continue&detach the child process
94 ASSERT_EQ(ptrace(PTRACE_DETACH, pid, 0, 0), 0);
95 // Wait for the child to exit
96 ASSERT_EQ(waitpid(pid, &status, __WNOTHREAD | __WALL | WUNTRACED), pid);
97 ASSERT_TRUE(WIFEXITED(status));
98 EXPECT_EQ(WEXITSTATUS(status), 0);
99 }
100
101 } // namespace
102 } // namespace sandbox2
103