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 <fcntl.h>
16 #include <limits.h>
17 #include <sys/types.h>
18
19 #include <cstddef>
20 #include <cstdint>
21 #include <memory>
22 #include <string>
23 #include <utility>
24 #include <vector>
25
26 #include "benchmark/benchmark.h"
27 #include "gmock/gmock.h"
28 #include "gtest/gtest.h"
29 #include "absl/log/log.h"
30 #include "absl/status/status.h"
31 #include "absl/status/statusor.h"
32 #include "absl/strings/string_view.h"
33 #include "absl/time/clock.h"
34 #include "absl/time/time.h"
35 #include "absl/types/span.h"
36 #include "sandboxed_api/examples/stringop/stringop-sapi.sapi.h"
37 #include "sandboxed_api/examples/stringop/stringop_params.pb.h"
38 #include "sandboxed_api/examples/sum/sum-sapi.sapi.h"
39 #include "sandboxed_api/sandbox.h"
40 #include "sandboxed_api/sandbox2/result.h"
41 #include "sandboxed_api/testing.h"
42 #include "sandboxed_api/transaction.h"
43 #include "sandboxed_api/util/status_macros.h"
44 #include "sandboxed_api/util/status_matchers.h"
45 #include "sandboxed_api/util/thread.h"
46 #include "sandboxed_api/var_array.h"
47 #include "sandboxed_api/var_int.h"
48 #include "sandboxed_api/var_lenval.h"
49 #include "sandboxed_api/var_reg.h"
50 #include "sandboxed_api/var_struct.h"
51
52 namespace sapi {
53 namespace {
54
55 using ::sapi::IsOk;
56 using ::sapi::StatusIs;
57 using ::testing::ContainerEq;
58 using ::testing::Eq;
59 using ::testing::Gt;
60 using ::testing::HasSubstr;
61 using ::testing::NotNull;
62
63 // Functions that will be used during the benchmarks:
64
65 // Function causing no load in the sandboxee.
InvokeNop(Sandbox * sandbox)66 absl::Status InvokeNop(Sandbox* sandbox) {
67 StringopApi api(sandbox);
68 return api.nop();
69 }
70
71 // Function that makes use of our special protobuf (de)-serialization code
72 // inside SAPI (including the back-synchronization of the structure).
InvokeStringReversal(Sandbox * sandbox)73 absl::Status InvokeStringReversal(Sandbox* sandbox) {
74 StringopApi api(sandbox);
75 stringop::StringReverse proto;
76 proto.set_input("Hello");
77 absl::StatusOr<v::Proto<stringop::StringReverse>> pp(
78 v::Proto<stringop::StringReverse>::FromMessage(proto));
79 SAPI_RETURN_IF_ERROR(pp.status());
80 SAPI_ASSIGN_OR_RETURN(int return_code, api.pb_reverse_string(pp->PtrBoth()));
81 TRANSACTION_FAIL_IF_NOT(return_code != 0, "pb_reverse_string failed");
82 SAPI_ASSIGN_OR_RETURN(auto pb_result, pp->GetMessage());
83 TRANSACTION_FAIL_IF_NOT(pb_result.output() == "olleH", "Incorrect output");
84 return absl::OkStatus();
85 }
86
87 // Benchmark functions:
88
89 // Restart SAPI sandbox by letting the sandbox object go out of scope.
90 // Minimal case for measuring the minimum overhead of restarting the sandbox.
BenchmarkSandboxRestartOverhead(benchmark::State & state)91 void BenchmarkSandboxRestartOverhead(benchmark::State& state) {
92 for (auto _ : state) {
93 BasicTransaction st(std::make_unique<StringopSandbox>());
94 // Invoke nop() to make sure that our sandbox is running.
95 EXPECT_THAT(st.Run(InvokeNop), IsOk());
96 }
97 }
98 BENCHMARK(BenchmarkSandboxRestartOverhead);
99
BenchmarkSandboxRestartForkserverOverhead(benchmark::State & state)100 void BenchmarkSandboxRestartForkserverOverhead(benchmark::State& state) {
101 sapi::BasicTransaction st(std::make_unique<StringopSandbox>());
102 for (auto _ : state) {
103 EXPECT_THAT(st.Run(InvokeNop), IsOk());
104 EXPECT_THAT(st.sandbox()->Restart(true), IsOk());
105 }
106 }
107 BENCHMARK(BenchmarkSandboxRestartForkserverOverhead);
108
BenchmarkSandboxRestartForkserverOverheadForced(benchmark::State & state)109 void BenchmarkSandboxRestartForkserverOverheadForced(benchmark::State& state) {
110 sapi::BasicTransaction st{std::make_unique<StringopSandbox>()};
111 for (auto _ : state) {
112 EXPECT_THAT(st.Run(InvokeNop), IsOk());
113 EXPECT_THAT(st.sandbox()->Restart(false), IsOk());
114 }
115 }
116 BENCHMARK(BenchmarkSandboxRestartForkserverOverheadForced);
117
118 // Reuse the sandbox. Used to measure the overhead of the call invocation.
BenchmarkCallOverhead(benchmark::State & state)119 void BenchmarkCallOverhead(benchmark::State& state) {
120 BasicTransaction st(std::make_unique<StringopSandbox>());
121 for (auto _ : state) {
122 EXPECT_THAT(st.Run(InvokeNop), IsOk());
123 }
124 }
125 BENCHMARK(BenchmarkCallOverhead);
126
127 // Make use of protobufs.
BenchmarkProtobufHandling(benchmark::State & state)128 void BenchmarkProtobufHandling(benchmark::State& state) {
129 BasicTransaction st(std::make_unique<StringopSandbox>());
130 for (auto _ : state) {
131 EXPECT_THAT(st.Run(InvokeStringReversal), IsOk());
132 }
133 }
134 BENCHMARK(BenchmarkProtobufHandling);
135
136 // Measure overhead of synchronizing data.
BenchmarkIntDataSynchronization(benchmark::State & state)137 void BenchmarkIntDataSynchronization(benchmark::State& state) {
138 auto sandbox = std::make_unique<StringopSandbox>();
139 ASSERT_THAT(sandbox->Init(), IsOk());
140
141 long current_val = 0; // NOLINT
142 v::Long long_var;
143 // Allocate remote memory.
144 ASSERT_THAT(sandbox->Allocate(&long_var, false), IsOk());
145
146 for (auto _ : state) {
147 // Write current_val to the process.
148 long_var.SetValue(current_val);
149 EXPECT_THAT(sandbox->TransferToSandboxee(&long_var), IsOk());
150 // Invalidate value to make sure that the next call
151 // is not simply a noop.
152 long_var.SetValue(-1);
153 // Read value back.
154 EXPECT_THAT(sandbox->TransferFromSandboxee(&long_var), IsOk());
155 EXPECT_THAT(long_var.GetValue(), Eq(current_val));
156
157 current_val++;
158 }
159 }
160 BENCHMARK(BenchmarkIntDataSynchronization);
161
162 // Test whether stack trace generation works.
TEST(SapiTest,HasStackTraces)163 TEST(SapiTest, HasStackTraces) {
164 SKIP_SANITIZERS_AND_COVERAGE;
165
166 auto sandbox = std::make_unique<StringopSandbox>();
167 ASSERT_THAT(sandbox->Init(), IsOk());
168 StringopApi api(sandbox.get());
169 EXPECT_THAT(api.violate(), StatusIs(absl::StatusCode::kUnavailable));
170 const auto& result = sandbox->AwaitResult();
171 EXPECT_THAT(
172 result.GetStackTrace(),
173 // Check that at least one expected function is present in the stack
174 // trace.
175 // Note: Typically, in optimized builds, on x86-64, only
176 // "ViolateIndirect()" will be present in the stack trace. On POWER, all
177 // stack frames are generated, but libunwind will be unable to track
178 // "ViolateIndirect()" on the stack and instead show its IP as zero.
179 AnyOf(HasSubstr("ViolateIndirect"), HasSubstr("violate")));
180 EXPECT_THAT(result.final_status(), Eq(sandbox2::Result::VIOLATION));
181 }
182
183 // Various tests:
184
185 // Leaks a file descriptor inside the sandboxee.
LeakFileDescriptor(sapi::Sandbox * sandbox,const char * path)186 int LeakFileDescriptor(sapi::Sandbox* sandbox, const char* path) {
187 int raw_fd = open(path, O_RDONLY);
188 sapi::v::Fd fd(raw_fd); // Takes ownership of the raw fd.
189 EXPECT_THAT(sandbox->TransferToSandboxee(&fd), IsOk());
190 // We want to leak the remote FD. The local FD will still be closed.
191 fd.OwnRemoteFd(false);
192 return fd.GetRemoteFd();
193 }
194
195 // Make sure that restarting the sandboxee works (= fresh set of FDs).
TEST(SandboxTest,RestartSandboxFD)196 TEST(SandboxTest, RestartSandboxFD) {
197 sapi::BasicTransaction st{std::make_unique<SumSandbox>()};
198
199 auto test_body = [](sapi::Sandbox* sandbox) -> absl::Status {
200 // Open some FDs and check their value.
201 int first_remote_fd = LeakFileDescriptor(sandbox, "/proc/self/exe");
202 EXPECT_THAT(LeakFileDescriptor(sandbox, "/proc/self/exe"),
203 Eq(first_remote_fd + 1));
204 SAPI_RETURN_IF_ERROR(sandbox->Restart(false));
205 // We should have a fresh sandbox now = FDs open previously should be
206 // closed now.
207 EXPECT_THAT(LeakFileDescriptor(sandbox, "/proc/self/exe"),
208 Eq(first_remote_fd));
209 return absl::OkStatus();
210 };
211
212 EXPECT_THAT(st.Run(test_body), IsOk());
213 }
214
TEST(SandboxTest,RestartTransactionSandboxFD)215 TEST(SandboxTest, RestartTransactionSandboxFD) {
216 sapi::BasicTransaction st{std::make_unique<SumSandbox>()};
217
218 int fd_no = -1;
219 ASSERT_THAT(st.Run([&fd_no](sapi::Sandbox* sandbox) -> absl::Status {
220 fd_no = LeakFileDescriptor(sandbox, "/proc/self/exe");
221 return absl::OkStatus();
222 }),
223 IsOk());
224
225 EXPECT_THAT(st.Run([fd_no](sapi::Sandbox* sandbox) -> absl::Status {
226 EXPECT_THAT(LeakFileDescriptor(sandbox, "/proc/self/exe"), Gt(fd_no));
227 return absl::OkStatus();
228 }),
229 IsOk());
230
231 EXPECT_THAT(st.Restart(), IsOk());
232
233 EXPECT_THAT(st.Run([fd_no](sapi::Sandbox* sandbox) -> absl::Status {
234 EXPECT_THAT(LeakFileDescriptor(sandbox, "/proc/self/exe"), Eq(fd_no));
235 return absl::OkStatus();
236 }),
237 IsOk());
238 }
239
240 // Make sure we can recover from a dying sandbox.
TEST(SandboxTest,RestartSandboxAfterCrash)241 TEST(SandboxTest, RestartSandboxAfterCrash) {
242 SumSandbox sandbox;
243 ASSERT_THAT(sandbox.Init(), IsOk());
244 SumApi api(&sandbox);
245
246 // Crash the sandbox.
247 EXPECT_THAT(api.crash(), StatusIs(absl::StatusCode::kUnavailable));
248 EXPECT_THAT(api.sum(1, 2).status(), StatusIs(absl::StatusCode::kUnavailable));
249 EXPECT_THAT(sandbox.AwaitResult().final_status(),
250 Eq(sandbox2::Result::SIGNALED));
251
252 // Restart the sandbox.
253 ASSERT_THAT(sandbox.Restart(false), IsOk());
254
255 // The sandbox should now be responsive again.
256 SAPI_ASSERT_OK_AND_ASSIGN(int result, api.sum(1, 2));
257 EXPECT_THAT(result, Eq(3));
258 }
259
TEST(SandboxTest,RestartSandboxAfterViolation)260 TEST(SandboxTest, RestartSandboxAfterViolation) {
261 SumSandbox sandbox;
262 ASSERT_THAT(sandbox.Init(), IsOk());
263 SumApi api(&sandbox);
264
265 // Violate the sandbox policy.
266 EXPECT_THAT(api.violate(), StatusIs(absl::StatusCode::kUnavailable));
267 EXPECT_THAT(api.sum(1, 2).status(), StatusIs(absl::StatusCode::kUnavailable));
268 EXPECT_THAT(sandbox.AwaitResult().final_status(),
269 Eq(sandbox2::Result::VIOLATION));
270
271 // Restart the sandbox.
272 ASSERT_THAT(sandbox.Restart(false), IsOk());
273
274 // The sandbox should now be responsive again.
275 SAPI_ASSERT_OK_AND_ASSIGN(int result, api.sum(1, 2));
276 EXPECT_THAT(result, Eq(3));
277 }
278
TEST(SandboxTest,NoRaceInAwaitResult)279 TEST(SandboxTest, NoRaceInAwaitResult) {
280 StringopSandbox sandbox;
281 ASSERT_THAT(sandbox.Init(), IsOk());
282 StringopApi api(&sandbox);
283
284 EXPECT_THAT(api.violate(), StatusIs(absl::StatusCode::kUnavailable));
285 absl::SleepFor(absl::Milliseconds(200)); // Make sure we lose the race
286 const auto& result = sandbox.AwaitResult();
287 EXPECT_THAT(result.final_status(), Eq(sandbox2::Result::VIOLATION));
288 }
289
TEST(SandboxTest,NoRaceInConcurrentTerminate)290 TEST(SandboxTest, NoRaceInConcurrentTerminate) {
291 SumSandbox sandbox;
292 ASSERT_THAT(sandbox.Init(), IsOk());
293 SumApi api(&sandbox);
294 sapi::Thread th([&sandbox] {
295 // Sleep so that the call already starts
296 absl::SleepFor(absl::Seconds(1));
297 sandbox.Terminate(/*attempt_graceful_exit=*/false);
298 });
299 EXPECT_THAT(api.sleep_for_sec(10), StatusIs(absl::StatusCode::kUnavailable));
300 th.Join();
301 const auto& result = sandbox.AwaitResult();
302 EXPECT_THAT(result.final_status(), Eq(sandbox2::Result::EXTERNAL_KILL));
303 }
304
TEST(SandboxTest,UseUnotifyMonitor)305 TEST(SandboxTest, UseUnotifyMonitor) {
306 SumSandbox sandbox;
307 ASSERT_THAT(sandbox.Init(/*use_unotify_monitor=*/true), IsOk());
308 SumApi api(&sandbox);
309
310 // Violate the sandbox policy.
311 EXPECT_THAT(api.violate(), StatusIs(absl::StatusCode::kUnavailable));
312 EXPECT_THAT(api.sum(1, 2).status(), StatusIs(absl::StatusCode::kUnavailable));
313 EXPECT_THAT(sandbox.AwaitResult().final_status(),
314 Eq(sandbox2::Result::VIOLATION));
315
316 // Restart the sandbox.
317 ASSERT_THAT(sandbox.Restart(false), IsOk());
318
319 // The sandbox should now be responsive again.
320 SAPI_ASSERT_OK_AND_ASSIGN(int result, api.sum(1, 2));
321 EXPECT_THAT(result, Eq(3));
322 }
323
TEST(SandboxTest,AllocateAndTransferTest)324 TEST(SandboxTest, AllocateAndTransferTest) {
325 std::string test_string("This is a test");
326 std::vector<uint8_t> test_string_vector(test_string.begin(),
327 test_string.end());
328
329 absl::Span<uint8_t> buffer_input(
330 reinterpret_cast<uint8_t*>(test_string_vector.data()),
331 test_string_vector.size());
332 std::vector<uint8_t> buffer_output(test_string_vector.size());
333
334 SumSandbox sandbox;
335 ASSERT_THAT(sandbox.Init(), IsOk());
336 SumApi api(&sandbox);
337
338 SAPI_ASSERT_OK_AND_ASSIGN(
339 auto sapi_array, sandbox.AllocateAndTransferToSandboxee(buffer_input));
340 ASSERT_THAT(sapi_array, NotNull());
341 sapi::v::Array<const uint8_t> sapi_buffer_output(
342 reinterpret_cast<const uint8_t*>(buffer_output.data()),
343 buffer_output.size());
344 sapi_buffer_output.SetRemote(sapi_array->GetRemote());
345 ASSERT_THAT(sandbox.TransferFromSandboxee(&sapi_buffer_output), IsOk());
346 EXPECT_THAT(test_string_vector, ContainerEq(buffer_output));
347 }
348
TEST(SandboxTest,AllocateAndTransferTestLarge)349 TEST(SandboxTest, AllocateAndTransferTestLarge) {
350 const size_t kLargeSize = getpagesize() * (IOV_MAX + 1);
351 const std::string test_string(kLargeSize, 'A');
352 std::vector<uint8_t> test_string_vector(test_string.begin(),
353 test_string.end());
354
355 absl::Span<uint8_t> buffer_input(
356 reinterpret_cast<uint8_t*>(test_string_vector.data()),
357 test_string_vector.size());
358 std::vector<uint8_t> buffer_output(test_string_vector.size());
359
360 SumSandbox sandbox;
361 ASSERT_THAT(sandbox.Init(), IsOk());
362 SumApi api(&sandbox);
363
364 SAPI_ASSERT_OK_AND_ASSIGN(
365 auto sapi_array, sandbox.AllocateAndTransferToSandboxee(buffer_input));
366 ASSERT_THAT(sapi_array, NotNull());
367 sapi::v::Array<const uint8_t> sapi_buffer_output(
368 reinterpret_cast<const uint8_t*>(buffer_output.data()),
369 buffer_output.size());
370 sapi_buffer_output.SetRemote(sapi_array->GetRemote());
371 ASSERT_THAT(sandbox.TransferFromSandboxee(&sapi_buffer_output), IsOk());
372 EXPECT_THAT(test_string_vector, ContainerEq(buffer_output));
373 }
374
TEST(VarsTest,MoveOperations)375 TEST(VarsTest, MoveOperations) {
376 {
377 v::Array<const uint8_t> array_orig(128); // Allocates locally
378 const uint8_t* data_before = array_orig.GetData();
379
380 v::Array<const uint8_t> array_new(std::move(array_orig));
381 array_orig = std::move(array_new); // Move back
382
383 const uint8_t* data_after = array_orig.GetData();
384 EXPECT_THAT(data_before, Eq(data_after));
385 }
386 {
387 constexpr absl::string_view kData = "Physcially fit";
388 v::LenVal len_val_orig(kData.data(), kData.size());
389 const uint8_t* data_before = len_val_orig.GetData();
390
391 v::LenVal len_val_new(std::move(len_val_orig));
392 len_val_orig = std::move(len_val_new); // Move back
393
394 const uint8_t* data_after = len_val_orig.GetData();
395 EXPECT_THAT(data_before, Eq(data_after));
396 }
397 {
398 stringop::StringDuplication underlying_proto;
399 SAPI_ASSERT_OK_AND_ASSIGN(
400 auto proto_orig,
401 v::Proto<stringop::StringDuplication>::FromMessage(underlying_proto));
402
403 v::Proto<stringop::StringDuplication> proto_new(std::move(proto_orig));
404 proto_orig = std::move(proto_new); // Move back
405 }
406 {
407 v::Reg<uint64_t> reg_orig(0x414141);
408 uint64_t value_before = reg_orig.GetValue();
409
410 v::Reg<uint64_t> reg_new(std::move(reg_orig));
411 reg_orig = std::move(reg_new); // Move back
412
413 uint64_t value_after = reg_orig.GetValue();
414 EXPECT_THAT(value_before, Eq(value_after));
415 }
416 {
417 struct MyStruct {
418 int member = 0x414141;
419 };
420 v::Struct<MyStruct> struct_orig;
421 MyStruct* data_before = struct_orig.mutable_data();
422
423 v::Struct<MyStruct> struct_new(std::move(struct_orig));
424 struct_orig = std::move(struct_new); // Move back
425
426 MyStruct* data_after = struct_orig.mutable_data();
427 EXPECT_THAT(data_before, Eq(data_after));
428 }
429 }
430
431 } // namespace
432 } // namespace sapi
433