xref: /aosp_15_r20/external/sandboxed-api/sandboxed_api/sandbox2/stack_trace_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 "sandboxed_api/sandbox2/stack_trace.h"
16 
17 #include <sys/types.h>
18 
19 #include <cstdio>
20 #include <functional>
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/base/log_severity.h"
29 #include "absl/flags/declare.h"
30 #include "absl/flags/flag.h"
31 #include "absl/flags/reflection.h"
32 #include "absl/log/check.h"
33 #include "absl/log/scoped_mock_log.h"
34 #include "absl/strings/str_cat.h"
35 #include "absl/time/time.h"
36 #include "sandboxed_api/sandbox2/executor.h"
37 #include "sandboxed_api/sandbox2/global_forkclient.h"
38 #include "sandboxed_api/sandbox2/policy.h"
39 #include "sandboxed_api/sandbox2/policybuilder.h"
40 #include "sandboxed_api/sandbox2/result.h"
41 #include "sandboxed_api/sandbox2/sandbox2.h"
42 #include "sandboxed_api/testing.h"
43 #include "sandboxed_api/util/fileops.h"
44 #include "sandboxed_api/util/status_matchers.h"
45 
46 ABSL_DECLARE_FLAG(bool, sandbox_libunwind_crash_handler);
47 
48 namespace sandbox2 {
49 
50 class StackTraceTestPeer {
51  public:
GetInstance()52   static StackTraceTestPeer& GetInstance() {
53     static auto* peer = new StackTraceTestPeer();
54     return *peer;
55   }
SpawnFn(std::unique_ptr<Executor> executor,std::unique_ptr<Policy> policy)56   std::unique_ptr<internal::SandboxPeer> SpawnFn(
57       std::unique_ptr<Executor> executor, std::unique_ptr<Policy> policy) {
58     if (crash_unwind_) {
59       policy = PolicyBuilder().BuildOrDie();
60       crash_unwind_ = false;
61     }
62     return old_spawn_fn_(std::move(executor), std::move(policy));
63   }
ReplaceSpawnFn()64   void ReplaceSpawnFn() {
65     old_spawn_fn_ = internal::SandboxPeer::spawn_fn_;
66     internal::SandboxPeer::spawn_fn_ = +[](std::unique_ptr<Executor> executor,
67                                            std::unique_ptr<Policy> policy) {
68       return GetInstance().SpawnFn(std::move(executor), std::move(policy));
69     };
70   }
RestoreSpawnFn()71   void RestoreSpawnFn() { internal::SandboxPeer::spawn_fn_ = old_spawn_fn_; }
CrashNextUnwind()72   void CrashNextUnwind() { crash_unwind_ = true; }
73 
74  private:
75   internal::SandboxPeer::SpawnFn old_spawn_fn_;
76   bool crash_unwind_ = false;
77 };
78 
79 struct ScopedSpawnOverride {
ScopedSpawnOverridesandbox2::ScopedSpawnOverride80   ScopedSpawnOverride() { StackTraceTestPeer::GetInstance().ReplaceSpawnFn(); }
~ScopedSpawnOverridesandbox2::ScopedSpawnOverride81   ~ScopedSpawnOverride() { StackTraceTestPeer::GetInstance().RestoreSpawnFn(); }
82   ScopedSpawnOverride(ScopedSpawnOverride&&) = delete;
83   ScopedSpawnOverride& operator=(ScopedSpawnOverride&&) = delete;
84   ScopedSpawnOverride(const ScopedSpawnOverride&) = delete;
85   ScopedSpawnOverride& operator=(const ScopedSpawnOverride&) = delete;
86 
CrashNextUnwindsandbox2::ScopedSpawnOverride87   void CrashNextUnwind() {
88     StackTraceTestPeer::GetInstance().CrashNextUnwind();
89   }
90 };
91 
92 namespace {
93 
94 namespace file_util = ::sapi::file_util;
95 using ::sapi::CreateDefaultPermissiveTestPolicy;
96 using ::sapi::GetTestSourcePath;
97 using ::testing::_;
98 using ::testing::Contains;
99 using ::testing::ContainsRegex;
100 using ::testing::ElementsAre;
101 using ::testing::Eq;
102 using ::testing::IsEmpty;
103 using ::testing::StartsWith;
104 
105 struct TestCase {
106   std::string testname = "CrashMe";
107   int testno = 1;
108   int testmode = 1;
109   int final_status = Result::SIGNALED;
110   std::string function_name = testname;
111   std::string full_function_description = "CrashMe(char)";
112   std::function<void(PolicyBuilder*)> modify_policy;
113   absl::Duration wall_time_limit = absl::ZeroDuration();
114 };
115 
116 class StackTraceTest : public ::testing::TestWithParam<TestCase> {};
117 
118 // Test that symbolization of stack traces works.
SymbolizationWorksCommon(TestCase param)119 void SymbolizationWorksCommon(TestCase param) {
120   const std::string path = GetTestSourcePath("sandbox2/testcases/symbolize");
121   std::vector<std::string> args = {path, absl::StrCat(param.testno),
122                                    absl::StrCat(param.testmode)};
123 
124   PolicyBuilder builder = CreateDefaultPermissiveTestPolicy(path);
125   if (param.modify_policy) {
126     param.modify_policy(&builder);
127   }
128   SAPI_ASSERT_OK_AND_ASSIGN(auto policy, builder.TryBuild());
129 
130   Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
131   ASSERT_TRUE(s2.RunAsync());
132   s2.set_walltime_limit(param.wall_time_limit);
133   auto result = s2.AwaitResult();
134 
135   EXPECT_THAT(result.final_status(), Eq(param.final_status));
136   EXPECT_THAT(result.stack_trace(), Contains(StartsWith(param.function_name)));
137   // Check that demangling works as well.
138   EXPECT_THAT(result.stack_trace(),
139               Contains(StartsWith(param.full_function_description)));
140   EXPECT_THAT(result.stack_trace(), Contains(StartsWith("RunTest")));
141   EXPECT_THAT(result.stack_trace(), Contains(StartsWith("main")));
142   if (param.testmode == 2) {
143     EXPECT_THAT(result.stack_trace(),
144                 Contains(StartsWith("RecurseA")).Times(5));
145     EXPECT_THAT(result.stack_trace(),
146                 Contains(StartsWith("RecurseB")).Times(5));
147   } else if (param.testmode == 3) {
148     EXPECT_THAT(result.stack_trace(), Contains(StartsWith("LibCallCallback")));
149     EXPECT_THAT(result.stack_trace(), Contains(StartsWith("LibRecurse")));
150     EXPECT_THAT(result.stack_trace(),
151                 Contains(StartsWith("LibRecurseA")).Times(5));
152     EXPECT_THAT(result.stack_trace(),
153                 Contains(StartsWith("LibRecurseB")).Times(5));
154   }
155 }
156 
SymbolizationWorksWithModifiedPolicy(std::function<void (PolicyBuilder *)> modify_policy)157 void SymbolizationWorksWithModifiedPolicy(
158     std::function<void(PolicyBuilder*)> modify_policy) {
159   TestCase test_case;
160   test_case.modify_policy = std::move(modify_policy);
161   SymbolizationWorksCommon(test_case);
162 }
163 
TEST_P(StackTraceTest,SymbolizationWorksNonSandboxedLibunwind)164 TEST_P(StackTraceTest, SymbolizationWorksNonSandboxedLibunwind) {
165   absl::FlagSaver fs;
166   absl::SetFlag(&FLAGS_sandbox_libunwind_crash_handler, false);
167 
168   SymbolizationWorksCommon(GetParam());
169 }
170 
TEST_P(StackTraceTest,SymbolizationWorksSandboxedLibunwind)171 TEST_P(StackTraceTest, SymbolizationWorksSandboxedLibunwind) {
172   absl::FlagSaver fs;
173   absl::SetFlag(&FLAGS_sandbox_libunwind_crash_handler, true);
174 
175   SymbolizationWorksCommon(GetParam());
176 }
177 
TEST(StackTraceTest,SymbolizationWorksSandboxedLibunwindProcDirMounted)178 TEST(StackTraceTest, SymbolizationWorksSandboxedLibunwindProcDirMounted) {
179   absl::FlagSaver fs;
180   absl::SetFlag(&FLAGS_sandbox_libunwind_crash_handler, true);
181 
182   SymbolizationWorksWithModifiedPolicy(
183       [](PolicyBuilder* builder) { builder->AddDirectory("/proc"); });
184 }
185 
TEST(StackTraceTest,SymbolizationWorksSandboxedLibunwindProcFileMounted)186 TEST(StackTraceTest, SymbolizationWorksSandboxedLibunwindProcFileMounted) {
187   absl::FlagSaver fs;
188   absl::SetFlag(&FLAGS_sandbox_libunwind_crash_handler, true);
189 
190   SymbolizationWorksWithModifiedPolicy([](PolicyBuilder* builder) {
191     builder->AddFile("/proc/sys/vm/overcommit_memory");
192   });
193 }
194 
TEST(StackTraceTest,SymbolizationWorksSandboxedLibunwindSysDirMounted)195 TEST(StackTraceTest, SymbolizationWorksSandboxedLibunwindSysDirMounted) {
196   absl::FlagSaver fs;
197   absl::SetFlag(&FLAGS_sandbox_libunwind_crash_handler, true);
198 
199   SymbolizationWorksWithModifiedPolicy(
200       [](PolicyBuilder* builder) { builder->AddDirectory("/sys"); });
201 }
202 
TEST(StackTraceTest,SymbolizationWorksSandboxedLibunwindSysFileMounted)203 TEST(StackTraceTest, SymbolizationWorksSandboxedLibunwindSysFileMounted) {
204   absl::FlagSaver fs;
205   absl::SetFlag(&FLAGS_sandbox_libunwind_crash_handler, true);
206 
207   SymbolizationWorksWithModifiedPolicy([](PolicyBuilder* builder) {
208     builder->AddFile("/sys/devices/system/cpu/online");
209   });
210 }
211 
FileCountInDirectory(const std::string & path)212 size_t FileCountInDirectory(const std::string& path) {
213   std::vector<std::string> fds;
214   std::string error;
215   CHECK(file_util::fileops::ListDirectoryEntries(path, &fds, &error));
216   return fds.size();
217 }
218 
TEST(StackTraceTest,ForkEnterNsLibunwindDoesNotLeakFDs)219 TEST(StackTraceTest, ForkEnterNsLibunwindDoesNotLeakFDs) {
220   absl::FlagSaver fs;
221   absl::SetFlag(&FLAGS_sandbox_libunwind_crash_handler, true);
222 
223   // Very first sanitization might create some fds (e.g. for initial
224   // namespaces).
225   SymbolizationWorksCommon({});
226 
227   // Get list of open FDs in the global forkserver.
228   pid_t forkserver_pid = GlobalForkClient::GetPid();
229   std::string forkserver_fd_path =
230       absl::StrCat("/proc/", forkserver_pid, "/fd");
231   size_t filecount_before = FileCountInDirectory(forkserver_fd_path);
232 
233   SymbolizationWorksCommon({});
234 
235   EXPECT_THAT(filecount_before, Eq(FileCountInDirectory(forkserver_fd_path)));
236 }
237 
TEST(StackTraceTest,CompactStackTrace)238 TEST(StackTraceTest, CompactStackTrace) {
239   EXPECT_THAT(CompactStackTrace({}), IsEmpty());
240   EXPECT_THAT(CompactStackTrace({"_start"}), ElementsAre("_start"));
241   EXPECT_THAT(CompactStackTrace({
242                   "_start",
243                   "main",
244                   "recursive_call",
245                   "recursive_call",
246                   "recursive_call",
247                   "tail_call",
248               }),
249               ElementsAre("_start", "main", "recursive_call",
250                           "(previous frame repeated 2 times)", "tail_call"));
251   EXPECT_THAT(CompactStackTrace({
252                   "_start",
253                   "main",
254                   "recursive_call",
255                   "recursive_call",
256                   "recursive_call",
257                   "recursive_call",
258               }),
259               ElementsAre("_start", "main", "recursive_call",
260                           "(previous frame repeated 3 times)"));
261 }
262 
TEST(StackTraceTest,RecursiveStackTrace)263 TEST(StackTraceTest, RecursiveStackTrace) {
264   // Very first sandbox run will initialize spawn_fn_
265   SKIP_SANITIZERS;
266   ScopedSpawnOverride spawn_override;
267   SymbolizationWorksCommon({});
268   absl::ScopedMockLog log;
269   EXPECT_CALL(
270       log,
271       Log(absl::LogSeverity::kInfo, _,
272           ContainsRegex(
273               "Libunwind execution status: SYSCALL VIOLATION.*Stack: \\w+")));
274   spawn_override.CrashNextUnwind();
275   const std::string path = GetTestSourcePath("sandbox2/testcases/symbolize");
276   std::vector<std::string> args = {path, absl::StrCat(1), absl::StrCat(1)};
277   PolicyBuilder builder = CreateDefaultPermissiveTestPolicy(path);
278   SAPI_ASSERT_OK_AND_ASSIGN(auto policy, builder.TryBuild());
279 
280   Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
281   log.StartCapturingLogs();
282   ASSERT_TRUE(s2.RunAsync());
283   auto result = s2.AwaitResult();
284   EXPECT_THAT(result.final_status(), Eq(Result::SIGNALED));
285 }
286 
287 INSTANTIATE_TEST_SUITE_P(
288     Instantiation, StackTraceTest,
289     ::testing::Values(
290         TestCase{
291             .testname = "CrashMe",
292             .testno = 1,
293             .final_status = Result::SIGNALED,
294             .full_function_description = "CrashMe(char)",
295         },
296         TestCase{
297             .testname = "ViolatePolicy",
298             .testno = 2,
299             .final_status = Result::VIOLATION,
300             .full_function_description = "ViolatePolicy(int)",
301         },
302         TestCase{
303             .testname = "ExitNormally",
304             .testno = 3,
305             .final_status = Result::OK,
306             .full_function_description = "ExitNormally(int)",
307             .modify_policy =
__anon6e1b7efc0702(PolicyBuilder* builder) 308                 [](PolicyBuilder* builder) {
309                   builder->CollectStacktracesOnExit(true);
310                 },
311         },
312         TestCase{
313             .testname = "SleepForXSeconds",
314             .testno = 4,
315             .final_status = Result::TIMEOUT,
316             .full_function_description = "SleepForXSeconds(int)",
317             .wall_time_limit = absl::Seconds(1),
318         },
319         TestCase{
320             .testname = "ViolatePolicyRecursive",
321             .testno = 2,
322             .testmode = 2,
323             .final_status = Result::VIOLATION,
324             .function_name = "ViolatePolicy",
325             .full_function_description = "ViolatePolicy(int)",
326         },
327         TestCase{
328             .testname = "ViolatePolicyRecursiveLib",
329             .testno = 2,
330             .testmode = 3,
331             .final_status = Result::VIOLATION,
332             .function_name = "ViolatePolicy",
333             .full_function_description = "ViolatePolicy(int)",
334         }),
__anon6e1b7efc0802(const ::testing::TestParamInfo<TestCase>& info) 335     [](const ::testing::TestParamInfo<TestCase>& info) {
336       return info.param.testname;
337     });
338 
339 }  // namespace
340 }  // namespace sandbox2
341