xref: /aosp_15_r20/external/sandboxed-api/sandboxed_api/sandbox2/policy_test.cc (revision ec63e07ab9515d95e79c211197c445ef84cefa6a)
1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "sandboxed_api/sandbox2/policy.h"
16 
17 #include <syscall.h>
18 
19 #include <cerrno>
20 #include <cstdlib>
21 #include <memory>
22 #include <string>
23 #include <utility>
24 #include <vector>
25 
26 #include "gmock/gmock.h"
27 #include "gtest/gtest.h"
28 #include "absl/strings/string_view.h"
29 #include "sandboxed_api/config.h"
30 #include "sandboxed_api/sandbox2/executor.h"
31 #include "sandboxed_api/sandbox2/policybuilder.h"
32 #include "sandboxed_api/sandbox2/result.h"
33 #include "sandboxed_api/sandbox2/sandbox2.h"
34 #include "sandboxed_api/sandbox2/util/bpf_helper.h"
35 #include "sandboxed_api/testing.h"
36 #include "sandboxed_api/util/status_matchers.h"
37 
38 namespace sandbox2 {
39 namespace {
40 
41 using ::sapi::CreateDefaultPermissiveTestPolicy;
42 using ::sapi::GetTestSourcePath;
43 using ::testing::Eq;
44 
45 #ifdef SAPI_X86_64
46 // Test that 32-bit syscalls from 64-bit are disallowed.
TEST(PolicyTest,AMD64Syscall32PolicyAllowed)47 TEST(PolicyTest, AMD64Syscall32PolicyAllowed) {
48   SKIP_ANDROID;
49   const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
50 
51   std::vector<std::string> args = {path, "1"};
52 
53   SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
54                             CreateDefaultPermissiveTestPolicy(path).TryBuild());
55   Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
56   auto result = s2.Run();
57 
58   ASSERT_THAT(result.final_status(), Eq(Result::VIOLATION));
59   EXPECT_THAT(result.reason_code(), Eq(1));  // __NR_exit in 32-bit
60   EXPECT_THAT(result.GetSyscallArch(), Eq(sapi::cpu::kX86));
61 }
62 
63 // Test that 32-bit syscalls from 64-bit for FS checks are disallowed.
TEST(PolicyTest,AMD64Syscall32FsAllowed)64 TEST(PolicyTest, AMD64Syscall32FsAllowed) {
65   SKIP_ANDROID;
66   const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
67   std::vector<std::string> args = {path, "2"};
68 
69   SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
70                             CreateDefaultPermissiveTestPolicy(path).TryBuild());
71   Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
72   auto result = s2.Run();
73 
74   ASSERT_THAT(result.final_status(), Eq(Result::VIOLATION));
75   EXPECT_THAT(result.reason_code(),
76               Eq(33));  // __NR_access in 32-bit
77   EXPECT_THAT(result.GetSyscallArch(), Eq(sapi::cpu::kX86));
78 }
79 #endif
80 
81 // Test that ptrace(2) is disallowed.
TEST(PolicyTest,PtraceDisallowed)82 TEST(PolicyTest, PtraceDisallowed) {
83   const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
84   std::vector<std::string> args = {path, "3"};
85 
86   SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
87                             CreateDefaultPermissiveTestPolicy(path).TryBuild());
88   Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
89   auto result = s2.Run();
90 
91   ASSERT_THAT(result.final_status(), Eq(Result::VIOLATION));
92   EXPECT_THAT(result.reason_code(), Eq(__NR_ptrace));
93 }
94 
95 // Test that clone(2) with flag CLONE_UNTRACED is disallowed.
TEST(PolicyTest,CloneUntracedDisallowed)96 TEST(PolicyTest, CloneUntracedDisallowed) {
97   const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
98   std::vector<std::string> args = {path, "4"};
99   SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
100                             CreateDefaultPermissiveTestPolicy(path).TryBuild());
101   Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
102   auto result = s2.Run();
103 
104   ASSERT_THAT(result.final_status(), Eq(Result::VIOLATION));
105   EXPECT_THAT(result.reason_code(), Eq(__NR_clone));
106 }
107 
108 // Test that bpf(2) is disallowed.
TEST(PolicyTest,BpfDisallowed)109 TEST(PolicyTest, BpfDisallowed) {
110   const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
111   std::vector<std::string> args = {path, "5"};
112   SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
113                             CreateDefaultPermissiveTestPolicy(path).TryBuild());
114   Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
115   auto result = s2.Run();
116 
117   ASSERT_THAT(result.final_status(), Eq(Result::VIOLATION));
118   EXPECT_THAT(result.reason_code(), Eq(__NR_bpf));
119 }
120 
121 // Test that ptrace/bpf can return EPERM.
TEST(PolicyTest,BpfPtracePermissionDenied)122 TEST(PolicyTest, BpfPtracePermissionDenied) {
123   const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
124   std::vector<std::string> args = {path, "7"};
125 
126   SAPI_ASSERT_OK_AND_ASSIGN(
127       auto policy, CreateDefaultPermissiveTestPolicy(path)
128                        .BlockSyscallsWithErrno({__NR_ptrace, __NR_bpf}, EPERM)
129                        .TryBuild());
130   Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
131   auto result = s2.Run();
132 
133   // ptrace/bpf is not a violation due to explicit policy.  EPERM is expected.
134   ASSERT_THAT(result.final_status(), Eq(Result::OK));
135   EXPECT_THAT(result.reason_code(), Eq(0));
136 }
137 
TEST(PolicyTest,IsattyAllowed)138 TEST(PolicyTest, IsattyAllowed) {
139   SKIP_SANITIZERS;
140   PolicyBuilder builder;
141   if constexpr (sapi::host_os::IsAndroid()) {
142     builder.DisableNamespaces().AllowDynamicStartup();
143   }
144   builder.AllowStaticStartup()
145       .AllowExit()
146       .AllowRead()
147       .AllowWrite()
148       .AllowTCGETS()
149       .AllowLlvmCoverage();
150   const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
151   std::vector<std::string> args = {path, "6"};
152   SAPI_ASSERT_OK_AND_ASSIGN(auto policy, builder.TryBuild());
153   Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
154   auto result = s2.Run();
155 
156   ASSERT_THAT(result.final_status(), Eq(Result::OK));
157 }
158 
MinimalTestcasePolicy(absl::string_view path="")159 std::unique_ptr<Policy> MinimalTestcasePolicy(absl::string_view path = "") {
160   PolicyBuilder builder;
161 
162   if constexpr (sapi::host_os::IsAndroid()) {
163     builder.AllowDynamicStartup();
164     builder.DisableNamespaces();
165   }
166 
167   builder.AllowStaticStartup().AllowExit().AllowLlvmCoverage();
168   return builder.BuildOrDie();
169 }
170 
171 // Test that we can sandbox a minimal static binary returning 0.
172 // If this starts failing, it means something changed, maybe in the way we
173 // compile static binaries, and we need to update the policy just above.
TEST(MinimalTest,MinimalBinaryWorks)174 TEST(MinimalTest, MinimalBinaryWorks) {
175   SKIP_ANDROID;
176   SKIP_SANITIZERS;
177   const std::string path = GetTestSourcePath("sandbox2/testcases/minimal");
178   std::vector<std::string> args = {path};
179   Sandbox2 s2(std::make_unique<Executor>(path, args),
180               MinimalTestcasePolicy(path));
181   auto result = s2.Run();
182 
183   ASSERT_THAT(result.final_status(), Eq(Result::OK));
184   EXPECT_THAT(result.reason_code(), Eq(EXIT_SUCCESS));
185 }
186 
187 // Test that we can sandbox a minimal non-static binary returning 0.
TEST(MinimalTest,MinimalSharedBinaryWorks)188 TEST(MinimalTest, MinimalSharedBinaryWorks) {
189   SKIP_SANITIZERS;
190   const std::string path =
191       GetTestSourcePath("sandbox2/testcases/minimal_dynamic");
192   std::vector<std::string> args = {path};
193 
194   PolicyBuilder builder;
195 
196   if constexpr (sapi::host_os::IsAndroid()) {
197     builder.DisableNamespaces();
198   } else {
199     builder.AddLibrariesForBinary(path);
200   }
201 
202   builder.AllowDynamicStartup().AllowExit().AllowLlvmCoverage();
203   auto policy = builder.BuildOrDie();
204 
205   Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
206   auto result = s2.Run();
207 
208   ASSERT_THAT(result.final_status(), Eq(Result::OK));
209   EXPECT_THAT(result.reason_code(), Eq(EXIT_SUCCESS));
210 }
211 
212 // Test that the AllowSystemMalloc helper works as expected.
TEST(MallocTest,SystemMallocWorks)213 TEST(MallocTest, SystemMallocWorks) {
214   SKIP_SANITIZERS;
215   const std::string path =
216       GetTestSourcePath("sandbox2/testcases/malloc_system");
217   std::vector<std::string> args = {path};
218 
219   PolicyBuilder builder;
220 
221   if constexpr (sapi::host_os::IsAndroid()) {
222     builder.DisableNamespaces();
223     builder.AllowDynamicStartup();
224     builder.AllowSyscalls({
225         __NR_madvise,
226     });
227   }
228 
229   builder.AllowStaticStartup()
230       .AllowSystemMalloc()
231       .AllowExit()
232       .AllowLlvmCoverage();
233   auto policy = builder.BuildOrDie();
234 
235   Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
236   auto result = s2.Run();
237 
238   ASSERT_THAT(result.final_status(), Eq(Result::OK));
239   EXPECT_THAT(result.reason_code(), Eq(EXIT_SUCCESS));
240 }
241 
242 // Complicated test to see that AddPolicyOnSyscalls work as
243 // expected. Specifically a worrisome corner-case would be that the logic was
244 // almost correct, but that the jump targets were off slightly. This uses the
245 // AddPolicyOnSyscall multiple times in a row to make any miscalculation
246 // unlikely to pass this check.
TEST(MultipleSyscalls,AddPolicyOnSyscallsWorks)247 TEST(MultipleSyscalls, AddPolicyOnSyscallsWorks) {
248   SKIP_SANITIZERS_AND_COVERAGE;
249   const std::string path =
250       GetTestSourcePath("sandbox2/testcases/add_policy_on_syscalls");
251   std::vector<std::string> args = {path};
252 
253   PolicyBuilder builder;
254   if constexpr (sapi::host_os::IsAndroid()) {
255     builder.DisableNamespaces();
256     builder.AllowDynamicStartup();
257   }
258 
259   builder.AllowStaticStartup()
260       .AllowTcMalloc()
261       .AllowExit()
262       .AddPolicyOnSyscalls(
263           {
264               __NR_getuid,
265               __NR_getgid,
266               __NR_geteuid,
267               __NR_getegid,
268 #ifdef __NR_getuid32
269               __NR_getuid32,
270 #endif
271 #ifdef __NR_getgid32
272               __NR_getgid32,
273 #endif
274 #ifdef __NR_geteuid32
275               __NR_geteuid32,
276 #endif
277 #ifdef __NR_getegid32
278               __NR_getegid32,
279 #endif
280           },
281           {ALLOW})
282       .AddPolicyOnSyscalls(
283           {
284               __NR_getresuid,
285               __NR_getresgid,
286 #ifdef __NR_getresuid32
287               __NR_getresuid32,
288 #endif
289 #ifdef __NR_getresgid32
290               __NR_getresgid32,
291 #endif
292           },
293           {ERRNO(42)})
294       .AddPolicyOnSyscalls({__NR_write}, {ERRNO(43)})
295       .AddPolicyOnSyscall(__NR_umask, {DENY});
296   auto policy = builder.BuildOrDie();
297 
298   Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
299   auto result = s2.Run();
300 
301   ASSERT_THAT(result.final_status(), Eq(Result::VIOLATION));
302   EXPECT_THAT(result.reason_code(), Eq(__NR_umask));
303 }
304 
305 }  // namespace
306 }  // namespace sandbox2
307