xref: /aosp_15_r20/external/sandboxed-api/sandboxed_api/sandbox2/regs_test.cc (revision ec63e07ab9515d95e79c211197c445ef84cefa6a)
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