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