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