xref: /aosp_15_r20/external/sandboxed-api/sandboxed_api/sapi_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 <fcntl.h>
16 
17 #include <memory>
18 #include <string>
19 #include <thread>  // NOLINT(build/c++11)
20 
21 #include "benchmark/benchmark.h"
22 #include "gmock/gmock.h"
23 #include "gtest/gtest.h"
24 #include "absl/status/status.h"
25 #include "absl/status/statusor.h"
26 #include "absl/time/clock.h"
27 #include "absl/time/time.h"
28 #include "sandboxed_api/examples/stringop/stringop-sapi.sapi.h"
29 #include "sandboxed_api/examples/stringop/stringop_params.pb.h"
30 #include "sandboxed_api/examples/sum/sum-sapi.sapi.h"
31 #include "sandboxed_api/sandbox.h"
32 #include "sandboxed_api/testing.h"
33 #include "sandboxed_api/transaction.h"
34 #include "sandboxed_api/util/status_matchers.h"
35 
36 namespace sapi {
37 namespace {
38 
39 using ::sapi::IsOk;
40 using ::sapi::StatusIs;
41 using ::testing::Eq;
42 using ::testing::Gt;
43 using ::testing::HasSubstr;
44 
45 // Functions that will be used during the benchmarks:
46 
47 // Function causing no load in the sandboxee.
InvokeNop(Sandbox * sandbox)48 absl::Status InvokeNop(Sandbox* sandbox) {
49   StringopApi api(sandbox);
50   return api.nop();
51 }
52 
53 // Function that makes use of our special protobuf (de)-serialization code
54 // inside SAPI (including the back-synchronization of the structure).
InvokeStringReversal(Sandbox * sandbox)55 absl::Status InvokeStringReversal(Sandbox* sandbox) {
56   StringopApi api(sandbox);
57   stringop::StringReverse proto;
58   proto.set_input("Hello");
59   absl::StatusOr<v::Proto<stringop::StringReverse>> pp(
60       v::Proto<stringop::StringReverse>::FromMessage(proto));
61   SAPI_RETURN_IF_ERROR(pp.status());
62   SAPI_ASSIGN_OR_RETURN(int return_code, api.pb_reverse_string(pp->PtrBoth()));
63   TRANSACTION_FAIL_IF_NOT(return_code != 0, "pb_reverse_string failed");
64   SAPI_ASSIGN_OR_RETURN(auto pb_result, pp->GetMessage());
65   TRANSACTION_FAIL_IF_NOT(pb_result.output() == "olleH", "Incorrect output");
66   return absl::OkStatus();
67 }
68 
69 // Benchmark functions:
70 
71 // Restart SAPI sandbox by letting the sandbox object go out of scope.
72 // Minimal case for measuring the minimum overhead of restarting the sandbox.
BenchmarkSandboxRestartOverhead(benchmark::State & state)73 void BenchmarkSandboxRestartOverhead(benchmark::State& state) {
74   for (auto _ : state) {
75     BasicTransaction st(std::make_unique<StringopSandbox>());
76     // Invoke nop() to make sure that our sandbox is running.
77     EXPECT_THAT(st.Run(InvokeNop), IsOk());
78   }
79 }
80 BENCHMARK(BenchmarkSandboxRestartOverhead);
81 
BenchmarkSandboxRestartForkserverOverhead(benchmark::State & state)82 void BenchmarkSandboxRestartForkserverOverhead(benchmark::State& state) {
83   sapi::BasicTransaction st(std::make_unique<StringopSandbox>());
84   for (auto _ : state) {
85     EXPECT_THAT(st.Run(InvokeNop), IsOk());
86     EXPECT_THAT(st.sandbox()->Restart(true), IsOk());
87   }
88 }
89 BENCHMARK(BenchmarkSandboxRestartForkserverOverhead);
90 
BenchmarkSandboxRestartForkserverOverheadForced(benchmark::State & state)91 void BenchmarkSandboxRestartForkserverOverheadForced(benchmark::State& state) {
92   sapi::BasicTransaction st{std::make_unique<StringopSandbox>()};
93   for (auto _ : state) {
94     EXPECT_THAT(st.Run(InvokeNop), IsOk());
95     EXPECT_THAT(st.sandbox()->Restart(false), IsOk());
96   }
97 }
98 BENCHMARK(BenchmarkSandboxRestartForkserverOverheadForced);
99 
100 // Reuse the sandbox. Used to measure the overhead of the call invocation.
BenchmarkCallOverhead(benchmark::State & state)101 void BenchmarkCallOverhead(benchmark::State& state) {
102   BasicTransaction st(std::make_unique<StringopSandbox>());
103   for (auto _ : state) {
104     EXPECT_THAT(st.Run(InvokeNop), IsOk());
105   }
106 }
107 BENCHMARK(BenchmarkCallOverhead);
108 
109 // Make use of protobufs.
BenchmarkProtobufHandling(benchmark::State & state)110 void BenchmarkProtobufHandling(benchmark::State& state) {
111   BasicTransaction st(std::make_unique<StringopSandbox>());
112   for (auto _ : state) {
113     EXPECT_THAT(st.Run(InvokeStringReversal), IsOk());
114   }
115 }
116 BENCHMARK(BenchmarkProtobufHandling);
117 
118 // Measure overhead of synchronizing data.
BenchmarkIntDataSynchronization(benchmark::State & state)119 void BenchmarkIntDataSynchronization(benchmark::State& state) {
120   auto sandbox = std::make_unique<StringopSandbox>();
121   ASSERT_THAT(sandbox->Init(), IsOk());
122 
123   long current_val = 0;  // NOLINT
124   v::Long long_var;
125   // Allocate remote memory.
126   ASSERT_THAT(sandbox->Allocate(&long_var, false), IsOk());
127 
128   for (auto _ : state) {
129     // Write current_val to the process.
130     long_var.SetValue(current_val);
131     EXPECT_THAT(sandbox->TransferToSandboxee(&long_var), IsOk());
132     // Invalidate value to make sure that the next call
133     // is not simply a noop.
134     long_var.SetValue(-1);
135     // Read value back.
136     EXPECT_THAT(sandbox->TransferFromSandboxee(&long_var), IsOk());
137     EXPECT_THAT(long_var.GetValue(), Eq(current_val));
138 
139     current_val++;
140   }
141 }
142 BENCHMARK(BenchmarkIntDataSynchronization);
143 
144 // Test whether stack trace generation works.
TEST(SapiTest,HasStackTraces)145 TEST(SapiTest, HasStackTraces) {
146   SKIP_SANITIZERS_AND_COVERAGE;
147 
148   auto sandbox = std::make_unique<StringopSandbox>();
149   ASSERT_THAT(sandbox->Init(), IsOk());
150   StringopApi api(sandbox.get());
151   EXPECT_THAT(api.violate(), StatusIs(absl::StatusCode::kUnavailable));
152   const auto& result = sandbox->AwaitResult();
153   EXPECT_THAT(
154       result.GetStackTrace(),
155       // Check that at least one expected function is present in the stack
156       // trace.
157       // Note: Typically, in optimized builds, on x86-64, only
158       // "ViolateIndirect()" will be present in the stack trace. On POWER, all
159       // stack frames are generated, but libunwind will be unable to track
160       // "ViolateIndirect()" on the stack and instead show its IP as zero.
161       AnyOf(HasSubstr("ViolateIndirect"), HasSubstr("violate")));
162   EXPECT_THAT(result.final_status(), Eq(sandbox2::Result::VIOLATION));
163 }
164 
165 // Various tests:
166 
167 // Leaks a file descriptor inside the sandboxee.
LeakFileDescriptor(sapi::Sandbox * sandbox,const char * path)168 int LeakFileDescriptor(sapi::Sandbox* sandbox, const char* path) {
169   int raw_fd = open(path, O_RDONLY);
170   sapi::v::Fd fd(raw_fd);  // Takes ownership of the raw fd.
171   EXPECT_THAT(sandbox->TransferToSandboxee(&fd), IsOk());
172   // We want to leak the remote FD. The local FD will still be closed.
173   fd.OwnRemoteFd(false);
174   return fd.GetRemoteFd();
175 }
176 
177 // Make sure that restarting the sandboxee works (= fresh set of FDs).
TEST(SandboxTest,RestartSandboxFD)178 TEST(SandboxTest, RestartSandboxFD) {
179   sapi::BasicTransaction st{std::make_unique<SumSandbox>()};
180 
181   auto test_body = [](sapi::Sandbox* sandbox) -> absl::Status {
182     // Open some FDs and check their value.
183     int first_remote_fd = LeakFileDescriptor(sandbox, "/proc/self/exe");
184     EXPECT_THAT(LeakFileDescriptor(sandbox, "/proc/self/exe"),
185                 Eq(first_remote_fd + 1));
186     SAPI_RETURN_IF_ERROR(sandbox->Restart(false));
187     // We should have a fresh sandbox now = FDs open previously should be
188     // closed now.
189     EXPECT_THAT(LeakFileDescriptor(sandbox, "/proc/self/exe"),
190                 Eq(first_remote_fd));
191     return absl::OkStatus();
192   };
193 
194   EXPECT_THAT(st.Run(test_body), IsOk());
195 }
196 
TEST(SandboxTest,RestartTransactionSandboxFD)197 TEST(SandboxTest, RestartTransactionSandboxFD) {
198   sapi::BasicTransaction st{std::make_unique<SumSandbox>()};
199 
200   int fd_no = -1;
201   ASSERT_THAT(st.Run([&fd_no](sapi::Sandbox* sandbox) -> absl::Status {
202     fd_no = LeakFileDescriptor(sandbox, "/proc/self/exe");
203     return absl::OkStatus();
204   }),
205               IsOk());
206 
207   EXPECT_THAT(st.Run([fd_no](sapi::Sandbox* sandbox) -> absl::Status {
208     EXPECT_THAT(LeakFileDescriptor(sandbox, "/proc/self/exe"), Gt(fd_no));
209     return absl::OkStatus();
210   }),
211               IsOk());
212 
213   EXPECT_THAT(st.Restart(), IsOk());
214 
215   EXPECT_THAT(st.Run([fd_no](sapi::Sandbox* sandbox) -> absl::Status {
216     EXPECT_THAT(LeakFileDescriptor(sandbox, "/proc/self/exe"), Eq(fd_no));
217     return absl::OkStatus();
218   }),
219               IsOk());
220 }
221 
222 // Make sure we can recover from a dying sandbox.
TEST(SandboxTest,RestartSandboxAfterCrash)223 TEST(SandboxTest, RestartSandboxAfterCrash) {
224   SumSandbox sandbox;
225   ASSERT_THAT(sandbox.Init(), IsOk());
226   SumApi api(&sandbox);
227 
228   // Crash the sandbox.
229   EXPECT_THAT(api.crash(), StatusIs(absl::StatusCode::kUnavailable));
230   EXPECT_THAT(api.sum(1, 2).status(), StatusIs(absl::StatusCode::kUnavailable));
231   EXPECT_THAT(sandbox.AwaitResult().final_status(),
232               Eq(sandbox2::Result::SIGNALED));
233 
234   // Restart the sandbox.
235   ASSERT_THAT(sandbox.Restart(false), IsOk());
236 
237   // The sandbox should now be responsive again.
238   SAPI_ASSERT_OK_AND_ASSIGN(int result, api.sum(1, 2));
239   EXPECT_THAT(result, Eq(3));
240 }
241 
TEST(SandboxTest,RestartSandboxAfterViolation)242 TEST(SandboxTest, RestartSandboxAfterViolation) {
243   SumSandbox sandbox;
244   ASSERT_THAT(sandbox.Init(), IsOk());
245   SumApi api(&sandbox);
246 
247   // Violate the sandbox policy.
248   EXPECT_THAT(api.violate(), StatusIs(absl::StatusCode::kUnavailable));
249   EXPECT_THAT(api.sum(1, 2).status(), StatusIs(absl::StatusCode::kUnavailable));
250   EXPECT_THAT(sandbox.AwaitResult().final_status(),
251               Eq(sandbox2::Result::VIOLATION));
252 
253   // Restart the sandbox.
254   ASSERT_THAT(sandbox.Restart(false), IsOk());
255 
256   // The sandbox should now be responsive again.
257   SAPI_ASSERT_OK_AND_ASSIGN(int result, api.sum(1, 2));
258   EXPECT_THAT(result, Eq(3));
259 }
260 
TEST(SandboxTest,NoRaceInAwaitResult)261 TEST(SandboxTest, NoRaceInAwaitResult) {
262   StringopSandbox sandbox;
263   ASSERT_THAT(sandbox.Init(), IsOk());
264   StringopApi api(&sandbox);
265 
266   EXPECT_THAT(api.violate(), StatusIs(absl::StatusCode::kUnavailable));
267   absl::SleepFor(absl::Milliseconds(200));  // Make sure we lose the race
268   const auto& result = sandbox.AwaitResult();
269   EXPECT_THAT(result.final_status(), Eq(sandbox2::Result::VIOLATION));
270 }
271 
TEST(SandboxTest,NoRaceInConcurrentTerminate)272 TEST(SandboxTest, NoRaceInConcurrentTerminate) {
273   SumSandbox sandbox;
274   ASSERT_THAT(sandbox.Init(), IsOk());
275   SumApi api(&sandbox);
276   std::thread th([&sandbox] {
277     // Sleep so that the call already starts
278     absl::SleepFor(absl::Seconds(1));
279     sandbox.Terminate(/*attempt_graceful_exit=*/false);
280   });
281   EXPECT_THAT(api.sleep_for_sec(10), StatusIs(absl::StatusCode::kUnavailable));
282   th.join();
283   const auto& result = sandbox.AwaitResult();
284   EXPECT_THAT(result.final_status(), Eq(sandbox2::Result::EXTERNAL_KILL));
285 }
286 
TEST(SandboxTest,UseUnotifyMonitor)287 TEST(SandboxTest, UseUnotifyMonitor) {
288   SumSandbox sandbox;
289   ASSERT_THAT(sandbox.Init(/*use_unotify_monitor=*/true), IsOk());
290   SumApi api(&sandbox);
291 
292   // Violate the sandbox policy.
293   EXPECT_THAT(api.violate(), StatusIs(absl::StatusCode::kUnavailable));
294   EXPECT_THAT(api.sum(1, 2).status(), StatusIs(absl::StatusCode::kUnavailable));
295   EXPECT_THAT(sandbox.AwaitResult().final_status(),
296               Eq(sandbox2::Result::VIOLATION));
297 
298   // Restart the sandbox.
299   ASSERT_THAT(sandbox.Restart(false), IsOk());
300 
301   // The sandbox should now be responsive again.
302   SAPI_ASSERT_OK_AND_ASSIGN(int result, api.sum(1, 2));
303   EXPECT_THAT(result, Eq(3));
304 }
305 
306 }  // namespace
307 }  // namespace sapi
308