• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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