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
47 // Test that 32-bit syscalls from 64-bit are disallowed.
TEST(PolicyTest,AMD64Syscall32PolicyAllowed)48 TEST(PolicyTest, AMD64Syscall32PolicyAllowed) {
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 const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
66 std::vector<std::string> args = {path, "2"};
67
68 SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
69 CreateDefaultPermissiveTestPolicy(path).TryBuild());
70 Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
71 auto result = s2.Run();
72
73 ASSERT_THAT(result.final_status(), Eq(Result::VIOLATION));
74 EXPECT_THAT(result.reason_code(),
75 Eq(33)); // __NR_access in 32-bit
76 EXPECT_THAT(result.GetSyscallArch(), Eq(sapi::cpu::kX86));
77 }
78 #endif
79
80 // Test that ptrace(2) is disallowed.
TEST(PolicyTest,PtraceDisallowed)81 TEST(PolicyTest, PtraceDisallowed) {
82 const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
83 std::vector<std::string> args = {path, "3"};
84
85 SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
86 CreateDefaultPermissiveTestPolicy(path).TryBuild());
87 Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
88 auto result = s2.Run();
89
90 ASSERT_THAT(result.final_status(), Eq(Result::VIOLATION));
91 EXPECT_THAT(result.reason_code(), Eq(__NR_ptrace));
92 }
93
94 // Test that clone(2) with flag CLONE_UNTRACED is disallowed.
TEST(PolicyTest,CloneUntracedDisallowed)95 TEST(PolicyTest, CloneUntracedDisallowed) {
96 const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
97 std::vector<std::string> args = {path, "4"};
98 SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
99 CreateDefaultPermissiveTestPolicy(path).TryBuild());
100 Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
101 auto result = s2.Run();
102
103 ASSERT_THAT(result.final_status(), Eq(Result::VIOLATION));
104 EXPECT_THAT(result.reason_code(), Eq(__NR_clone));
105 }
106
107 // Test that bpf(2) is disallowed.
TEST(PolicyTest,BpfDisallowed)108 TEST(PolicyTest, BpfDisallowed) {
109 const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
110 std::vector<std::string> args = {path, "5"};
111 SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
112 CreateDefaultPermissiveTestPolicy(path).TryBuild());
113 Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
114 auto result = s2.Run();
115
116 ASSERT_THAT(result.final_status(), Eq(Result::VIOLATION));
117 EXPECT_THAT(result.reason_code(), Eq(__NR_bpf));
118 }
119
120 // Test that ptrace/bpf can return EPERM.
TEST(PolicyTest,BpfPtracePermissionDenied)121 TEST(PolicyTest, BpfPtracePermissionDenied) {
122 const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
123 std::vector<std::string> args = {path, "7"};
124
125 SAPI_ASSERT_OK_AND_ASSIGN(
126 auto policy, CreateDefaultPermissiveTestPolicy(path)
127 .BlockSyscallsWithErrno({__NR_ptrace, __NR_bpf}, EPERM)
128 .TryBuild());
129 Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
130 auto result = s2.Run();
131
132 // ptrace/bpf is not a violation due to explicit policy. EPERM is expected.
133 ASSERT_THAT(result.final_status(), Eq(Result::OK));
134 EXPECT_THAT(result.reason_code(), Eq(0));
135 }
136
TEST(PolicyTest,IsattyAllowed)137 TEST(PolicyTest, IsattyAllowed) {
138 SKIP_SANITIZERS;
139 PolicyBuilder builder;
140 builder.AllowStaticStartup()
141 .AllowExit()
142 .AllowRead()
143 .AllowWrite()
144 .AllowTCGETS()
145 .AllowLlvmCoverage();
146 const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
147 std::vector<std::string> args = {path, "6"};
148 SAPI_ASSERT_OK_AND_ASSIGN(auto policy, builder.TryBuild());
149 Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
150 auto result = s2.Run();
151
152 ASSERT_THAT(result.final_status(), Eq(Result::OK));
153 }
154
PosixTimersPolicyBuilder(absl::string_view path)155 PolicyBuilder PosixTimersPolicyBuilder(absl::string_view path) {
156 return PolicyBuilder()
157 // Required by google infra / logging.
158 .AllowDynamicStartup()
159 .AllowWrite()
160 .AllowSyscall(__NR_getcwd)
161 .AllowMmap()
162 .AllowMlock()
163 .AllowMkdir()
164 .AllowGetIDs()
165 .AllowExit()
166 .AllowRestartableSequences(PolicyBuilder::kAllowSlowFences)
167 .AllowSyscall(__NR_rt_sigtimedwait)
168 // Features used by the binary.
169 .AllowHandleSignals()
170 .AllowGetPIDs()
171 .AllowTime()
172 .AllowSleep()
173 .AllowAlarm()
174 // Posix timers themselves.
175 .AllowPosixTimers();
176 }
177
TEST(PolicyTest,PosixTimersWorkIfAllowed)178 TEST(PolicyTest, PosixTimersWorkIfAllowed) {
179 SKIP_SANITIZERS;
180 const std::string path = GetTestSourcePath("sandbox2/testcases/posix_timers");
181 for (absl::string_view kind : {"SIGEV_NONE", "SIGEV_SIGNAL",
182 "SIGEV_THREAD_ID", "syscall(SIGEV_THREAD)"}) {
183 std::vector<std::string> args = {path, "--sigev_notify_kind",
184 std::string(kind)};
185
186 SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
187 PosixTimersPolicyBuilder(path).TryBuild());
188 auto executor = std::make_unique<Executor>(path, args);
189 Sandbox2 sandbox(std::move(executor), std::move(policy));
190 Result result = sandbox.Run();
191 EXPECT_EQ(result.final_status(), Result::OK) << kind;
192 }
193 }
194
TEST(PolicyTest,PosixTimersCannotCreateThreadsIfThreadsAreProhibited)195 TEST(PolicyTest, PosixTimersCannotCreateThreadsIfThreadsAreProhibited) {
196 SKIP_SANITIZERS;
197 const std::string path = GetTestSourcePath("sandbox2/testcases/posix_timers");
198 std::vector<std::string> args = {
199 path,
200 // SIGEV_THREAD creates a thread as an implementation detail.
201 "--sigev_notify_kind=SIGEV_THREAD",
202 };
203
204 SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
205 PosixTimersPolicyBuilder(path).TryBuild());
206 auto executor = std::make_unique<Executor>(path, args);
207 Sandbox2 sandbox(std::move(executor), std::move(policy));
208 Result result = sandbox.Run();
209 EXPECT_EQ(result.final_status(), Result::VIOLATION);
210 }
211
TEST(PolicyTest,PosixTimersCanCreateThreadsIfThreadsAreAllowed)212 TEST(PolicyTest, PosixTimersCanCreateThreadsIfThreadsAreAllowed) {
213 SKIP_SANITIZERS;
214 const std::string path = GetTestSourcePath("sandbox2/testcases/posix_timers");
215 std::vector<std::string> args = {path, "--sigev_notify_kind=SIGEV_THREAD"};
216
217 SAPI_ASSERT_OK_AND_ASSIGN(auto policy, PosixTimersPolicyBuilder(path)
218 .AllowFork()
219 // For Arm.
220 .AllowSyscall(__NR_madvise)
221 .TryBuild());
222 auto executor = std::make_unique<Executor>(path, args);
223 Sandbox2 sandbox(std::move(executor), std::move(policy));
224 Result result = sandbox.Run();
225 EXPECT_EQ(result.final_status(), Result::OK);
226 }
227
MinimalTestcasePolicy(absl::string_view path="")228 std::unique_ptr<Policy> MinimalTestcasePolicy(absl::string_view path = "") {
229 PolicyBuilder builder;
230 builder.AllowStaticStartup().AllowExit().AllowLlvmCoverage();
231 return builder.BuildOrDie();
232 }
233
234 // Test that we can sandbox a minimal static binary returning 0.
235 // If this starts failing, it means something changed, maybe in the way we
236 // compile static binaries, and we need to update the policy just above.
TEST(MinimalTest,MinimalBinaryWorks)237 TEST(MinimalTest, MinimalBinaryWorks) {
238 SKIP_SANITIZERS;
239 const std::string path = GetTestSourcePath("sandbox2/testcases/minimal");
240 std::vector<std::string> args = {path};
241 Sandbox2 s2(std::make_unique<Executor>(path, args),
242 MinimalTestcasePolicy(path));
243 auto result = s2.Run();
244
245 ASSERT_THAT(result.final_status(), Eq(Result::OK));
246 EXPECT_THAT(result.reason_code(), Eq(EXIT_SUCCESS));
247 }
248
249 // Test that we can sandbox a minimal non-static binary returning 0.
TEST(MinimalTest,MinimalSharedBinaryWorks)250 TEST(MinimalTest, MinimalSharedBinaryWorks) {
251 SKIP_SANITIZERS;
252 const std::string path =
253 GetTestSourcePath("sandbox2/testcases/minimal_dynamic");
254 std::vector<std::string> args = {path};
255
256 PolicyBuilder builder;
257 builder.AddLibrariesForBinary(path)
258 .AllowDynamicStartup()
259 .AllowExit()
260 .AllowLlvmCoverage();
261 auto policy = builder.BuildOrDie();
262
263 Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
264 auto result = s2.Run();
265
266 ASSERT_THAT(result.final_status(), Eq(Result::OK));
267 EXPECT_THAT(result.reason_code(), Eq(EXIT_SUCCESS));
268 }
269
270 // Test that the AllowSystemMalloc helper works as expected.
TEST(MallocTest,SystemMallocWorks)271 TEST(MallocTest, SystemMallocWorks) {
272 SKIP_SANITIZERS;
273 const std::string path =
274 GetTestSourcePath("sandbox2/testcases/malloc_system");
275 std::vector<std::string> args = {path};
276
277 PolicyBuilder builder;
278 builder.AllowStaticStartup()
279 .AllowSystemMalloc()
280 .AllowExit()
281 .AllowLlvmCoverage();
282 auto policy = builder.BuildOrDie();
283
284 Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
285 auto result = s2.Run();
286
287 ASSERT_THAT(result.final_status(), Eq(Result::OK));
288 EXPECT_THAT(result.reason_code(), Eq(EXIT_SUCCESS));
289 }
290
291 // Complicated test to see that AddPolicyOnSyscalls work as
292 // expected. Specifically a worrisome corner-case would be that the logic was
293 // almost correct, but that the jump targets were off slightly. This uses the
294 // AddPolicyOnSyscall multiple times in a row to make any miscalculation
295 // unlikely to pass this check.
TEST(MultipleSyscalls,AddPolicyOnSyscallsWorks)296 TEST(MultipleSyscalls, AddPolicyOnSyscallsWorks) {
297 SKIP_SANITIZERS_AND_COVERAGE;
298 const std::string path =
299 GetTestSourcePath("sandbox2/testcases/add_policy_on_syscalls");
300 std::vector<std::string> args = {path};
301
302 PolicyBuilder builder;
303 builder.AllowStaticStartup()
304 .AllowTcMalloc()
305 .AllowExit()
306 .AddPolicyOnSyscalls(
307 {
308 __NR_getuid,
309 __NR_getgid,
310 __NR_geteuid,
311 __NR_getegid,
312 #ifdef __NR_getuid32
313 __NR_getuid32,
314 #endif
315 #ifdef __NR_getgid32
316 __NR_getgid32,
317 #endif
318 #ifdef __NR_geteuid32
319 __NR_geteuid32,
320 #endif
321 #ifdef __NR_getegid32
322 __NR_getegid32,
323 #endif
324 },
325 {ALLOW})
326 .AddPolicyOnSyscalls(
327 {
328 __NR_getresuid,
329 __NR_getresgid,
330 #ifdef __NR_getresuid32
331 __NR_getresuid32,
332 #endif
333 #ifdef __NR_getresgid32
334 __NR_getresgid32,
335 #endif
336 },
337 {ERRNO(42)})
338 .AddPolicyOnSyscalls({__NR_write}, {ERRNO(43)})
339 .AddPolicyOnSyscall(__NR_umask, {DENY});
340 auto policy = builder.BuildOrDie();
341
342 Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
343 auto result = s2.Run();
344
345 ASSERT_THAT(result.final_status(), Eq(Result::VIOLATION));
346 EXPECT_THAT(result.reason_code(), Eq(__NR_umask));
347 }
348
349 // Test that util::kMagicSyscallNo is returns ENOSYS or util::kMagicSyscallErr.
TEST(PolicyTest,DetectSandboxSyscall)350 TEST(PolicyTest, DetectSandboxSyscall) {
351 const std::string path =
352 GetTestSourcePath("sandbox2/testcases/sandbox_detection");
353 std::vector<std::string> args = {path};
354
355 SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
356 CreateDefaultPermissiveTestPolicy(path).TryBuild());
357 auto executor = std::make_unique<Executor>(path, args);
358 executor->set_enable_sandbox_before_exec(false);
359 Sandbox2 s2(std::move(executor), std::move(policy));
360 auto result = s2.Run();
361
362 // The test binary should exit with success.
363 ASSERT_THAT(result.final_status(), Eq(Result::OK));
364 EXPECT_THAT(result.reason_code(), Eq(0));
365 }
366
367 } // namespace
368 } // namespace sandbox2
369