• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <sys/ptrace.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #include <unistd.h>
11 
12 #include "base/bind.h"
13 #include "base/compiler_specific.h"
14 #include "base/posix/eintr_wrapper.h"
15 #include "base/strings/string_util.h"
16 #include "base/sys_info.h"
17 #include "sandbox/linux/services/scoped_process.h"
18 #include "sandbox/linux/services/yama.h"
19 #include "sandbox/linux/tests/unit_tests.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21 
22 namespace sandbox {
23 
24 namespace {
25 
HasLinux32Bug()26 bool HasLinux32Bug() {
27 #if defined(__i386__)
28   // On 3.2 kernels, yama doesn't work for 32-bit binaries on 64-bit kernels.
29   // This is fixed in 3.4.
30   bool is_kernel_64bit =
31       base::SysInfo::OperatingSystemArchitecture() == "x86_64";
32   bool is_linux = base::SysInfo::OperatingSystemName() == "Linux";
33   bool is_3_dot_2 = StartsWithASCII(
34       base::SysInfo::OperatingSystemVersion(), "3.2", /*case_sensitive=*/false);
35   if (is_kernel_64bit && is_linux && is_3_dot_2)
36     return true;
37 #endif  // defined(__i386__)
38   return false;
39 }
40 
CanPtrace(pid_t pid)41 bool CanPtrace(pid_t pid) {
42   int ret;
43   ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
44   if (ret == -1) {
45     CHECK_EQ(EPERM, errno);
46     return false;
47   }
48   // Wait for the process to be stopped so that it can be detached.
49   siginfo_t process_info;
50   int wait_ret = HANDLE_EINTR(waitid(P_PID, pid, &process_info, WSTOPPED));
51   PCHECK(0 == wait_ret);
52   PCHECK(0 == ptrace(PTRACE_DETACH, pid, NULL, NULL));
53   return true;
54 }
55 
56 // _exit(0) if pid can be ptraced by the current process.
57 // _exit(1) otherwise.
ExitZeroIfCanPtrace(pid_t pid)58 void ExitZeroIfCanPtrace(pid_t pid) {
59   if (CanPtrace(pid)) {
60     _exit(0);
61   } else {
62     _exit(1);
63   }
64 }
65 
CanSubProcessPtrace(pid_t pid)66 bool CanSubProcessPtrace(pid_t pid) {
67   ScopedProcess process(base::Bind(&ExitZeroIfCanPtrace, pid));
68   bool signaled;
69   int exit_code = process.WaitForExit(&signaled);
70   CHECK(!signaled);
71   return 0 == exit_code;
72 }
73 
74 // The tests below assume that the system-level configuration will not change
75 // while they run.
76 
TEST(Yama,GetStatus)77 TEST(Yama, GetStatus) {
78   int status1 = Yama::GetStatus();
79 
80   // Check that the value is a possible bitmask.
81   ASSERT_LE(0, status1);
82   ASSERT_GE(Yama::STATUS_KNOWN | Yama::STATUS_PRESENT | Yama::STATUS_ENFORCING |
83                 Yama::STATUS_STRICT_ENFORCING,
84             status1);
85 
86   // The status should not just be a random value.
87   int status2 = Yama::GetStatus();
88   EXPECT_EQ(status1, status2);
89 
90   // This test is not running sandboxed, there is no reason to not know the
91   // status.
92   EXPECT_NE(0, Yama::STATUS_KNOWN & status1);
93 
94   if (status1 & Yama::STATUS_STRICT_ENFORCING) {
95     // If Yama is strictly enforcing, it is also enforcing.
96     EXPECT_TRUE(status1 & Yama::STATUS_ENFORCING);
97   }
98 
99   if (status1 & Yama::STATUS_ENFORCING) {
100     // If Yama is enforcing, Yama is present.
101     EXPECT_NE(0, status1 & Yama::STATUS_PRESENT);
102   }
103 
104   // Verify that the helper functions work as intended.
105   EXPECT_EQ(static_cast<bool>(status1 & Yama::STATUS_ENFORCING),
106             Yama::IsEnforcing());
107   EXPECT_EQ(static_cast<bool>(status1 & Yama::STATUS_PRESENT),
108             Yama::IsPresent());
109 
110   fprintf(stdout,
111           "Yama present: %s - enforcing: %s\n",
112           Yama::IsPresent() ? "Y" : "N",
113           Yama::IsEnforcing() ? "Y" : "N");
114 }
115 
SANDBOX_TEST(Yama,RestrictPtraceSucceedsWhenYamaPresent)116 SANDBOX_TEST(Yama, RestrictPtraceSucceedsWhenYamaPresent) {
117   // This call will succeed iff Yama is present.
118   bool restricted = Yama::RestrictPtracersToAncestors();
119   CHECK_EQ(restricted, Yama::IsPresent());
120 }
121 
122 // Attempts to enable or disable Yama restrictions.
SetYamaRestrictions(bool enable_restriction)123 void SetYamaRestrictions(bool enable_restriction) {
124   if (enable_restriction) {
125     Yama::RestrictPtracersToAncestors();
126   } else {
127     Yama::DisableYamaRestrictions();
128   }
129 }
130 
TEST(Yama,RestrictPtraceWorks)131 TEST(Yama, RestrictPtraceWorks) {
132   if (HasLinux32Bug())
133     return;
134 
135   ScopedProcess process1(base::Bind(&SetYamaRestrictions, true));
136   ASSERT_TRUE(process1.WaitForClosureToRun());
137 
138   if (Yama::IsEnforcing()) {
139     // A sibling process cannot ptrace process1.
140     ASSERT_FALSE(CanSubProcessPtrace(process1.GetPid()));
141   }
142 
143   if (!(Yama::GetStatus() & Yama::STATUS_STRICT_ENFORCING)) {
144     // However, parent can ptrace process1.
145     ASSERT_TRUE(CanPtrace(process1.GetPid()));
146 
147     // A sibling can ptrace process2 which disables any Yama protection.
148     ScopedProcess process2(base::Bind(&SetYamaRestrictions, false));
149     ASSERT_TRUE(process2.WaitForClosureToRun());
150     ASSERT_TRUE(CanSubProcessPtrace(process2.GetPid()));
151   }
152 }
153 
DoNothing()154 void DoNothing() {}
155 
SANDBOX_TEST(Yama,RestrictPtraceIsDefault)156 SANDBOX_TEST(Yama, RestrictPtraceIsDefault) {
157   if (!Yama::IsPresent() || HasLinux32Bug())
158     return;
159 
160   CHECK(Yama::DisableYamaRestrictions());
161   ScopedProcess process1(base::Bind(&DoNothing));
162 
163   if (Yama::IsEnforcing()) {
164     // Check that process1 is protected by Yama, even though it has
165     // been created from a process that disabled Yama.
166     CHECK(!CanSubProcessPtrace(process1.GetPid()));
167   }
168 }
169 
170 }  // namespace
171 
172 }  // namespace sandbox
173