• 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 "sandbox/linux/services/scoped_process.h"
16 #include "sandbox/linux/services/yama.h"
17 #include "sandbox/linux/tests/unit_tests.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 
20 namespace sandbox {
21 
22 namespace {
23 
CanPtrace(pid_t pid)24 bool CanPtrace(pid_t pid) {
25   int ret;
26   ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
27   if (ret == -1) {
28     CHECK_EQ(EPERM, errno);
29     return false;
30   }
31   // Wait for the process to be stopped so that it can be detached.
32   siginfo_t process_info;
33   int wait_ret = HANDLE_EINTR(waitid(P_PID, pid, &process_info, WSTOPPED));
34   PCHECK(0 == wait_ret);
35   PCHECK(0 == ptrace(PTRACE_DETACH, pid, NULL, NULL));
36   return true;
37 }
38 
39 // _exit(0) if pid can be ptraced by the current process.
40 // _exit(1) otherwise.
ExitZeroIfCanPtrace(pid_t pid)41 void ExitZeroIfCanPtrace(pid_t pid) {
42   if (CanPtrace(pid)) {
43     _exit(0);
44   } else {
45     _exit(1);
46   }
47 }
48 
CanSubProcessPtrace(pid_t pid)49 bool CanSubProcessPtrace(pid_t pid) {
50   ScopedProcess process(base::Bind(&ExitZeroIfCanPtrace, pid));
51   bool signaled;
52   int exit_code = process.WaitForExit(&signaled);
53   CHECK(!signaled);
54   return 0 == exit_code;
55 }
56 
57 // The tests below assume that the system-level configuration will not change
58 // while they run.
59 
TEST(Yama,GetStatus)60 TEST(Yama, GetStatus) {
61   int status1 = Yama::GetStatus();
62 
63   // Check that the value is a possible bitmask.
64   ASSERT_LE(0, status1);
65   ASSERT_GE(Yama::STATUS_KNOWN | Yama::STATUS_PRESENT | Yama::STATUS_ENFORCING |
66                 Yama::STATUS_STRICT_ENFORCING,
67             status1);
68 
69   // The status should not just be a random value.
70   int status2 = Yama::GetStatus();
71   EXPECT_EQ(status1, status2);
72 
73   // This test is not running sandboxed, there is no reason to not know the
74   // status.
75   EXPECT_NE(0, Yama::STATUS_KNOWN & status1);
76 
77   if (status1 & Yama::STATUS_STRICT_ENFORCING) {
78     // If Yama is strictly enforcing, it is also enforcing.
79     EXPECT_TRUE(status1 & Yama::STATUS_ENFORCING);
80   }
81 
82   if (status1 & Yama::STATUS_ENFORCING) {
83     // If Yama is enforcing, Yama is present.
84     EXPECT_NE(0, status1 & Yama::STATUS_PRESENT);
85   }
86 
87   // Verify that the helper functions work as intended.
88   EXPECT_EQ(static_cast<bool>(status1 & Yama::STATUS_ENFORCING),
89             Yama::IsEnforcing());
90   EXPECT_EQ(static_cast<bool>(status1 & Yama::STATUS_PRESENT),
91             Yama::IsPresent());
92 
93   fprintf(stdout,
94           "Yama present: %s - enforcing: %s\n",
95           Yama::IsPresent() ? "Y" : "N",
96           Yama::IsEnforcing() ? "Y" : "N");
97 }
98 
SANDBOX_TEST(Yama,RestrictPtraceSucceedsWhenYamaPresent)99 SANDBOX_TEST(Yama, RestrictPtraceSucceedsWhenYamaPresent) {
100   // This call will succeed iff Yama is present.
101   bool restricted = Yama::RestrictPtracersToAncestors();
102   CHECK_EQ(restricted, Yama::IsPresent());
103 }
104 
105 // Attempts to enable or disable Yama restrictions.
SetYamaRestrictions(bool enable_restriction)106 void SetYamaRestrictions(bool enable_restriction) {
107   if (enable_restriction) {
108     Yama::RestrictPtracersToAncestors();
109   } else {
110     Yama::DisableYamaRestrictions();
111   }
112 }
113 
TEST(Yama,RestrictPtraceWorks)114 TEST(Yama, RestrictPtraceWorks) {
115   ScopedProcess process1(base::Bind(&SetYamaRestrictions, true));
116   ASSERT_TRUE(process1.WaitForClosureToRun());
117 
118   if (Yama::IsEnforcing()) {
119     // A sibling process cannot ptrace process1.
120     ASSERT_FALSE(CanSubProcessPtrace(process1.GetPid()));
121   }
122 
123   if (!(Yama::GetStatus() & Yama::STATUS_STRICT_ENFORCING)) {
124     // However, parent can ptrace process1.
125     ASSERT_TRUE(CanPtrace(process1.GetPid()));
126 
127     // A sibling can ptrace process2 which disables any Yama protection.
128     ScopedProcess process2(base::Bind(&SetYamaRestrictions, false));
129     ASSERT_TRUE(process2.WaitForClosureToRun());
130     ASSERT_TRUE(CanSubProcessPtrace(process2.GetPid()));
131   }
132 }
133 
DoNothing()134 void DoNothing() {}
135 
SANDBOX_TEST(Yama,RestrictPtraceIsDefault)136 SANDBOX_TEST(Yama, RestrictPtraceIsDefault) {
137   if (!Yama::IsPresent())
138     return;
139 
140   CHECK(Yama::DisableYamaRestrictions());
141   ScopedProcess process1(base::Bind(&DoNothing));
142 
143   if (Yama::IsEnforcing()) {
144     // Check that process1 is protected by Yama, even though it has
145     // been created from a process that disabled Yama.
146     CHECK(!CanSubProcessPtrace(process1.GetPid()));
147   }
148 }
149 
150 }  // namespace
151 
152 }  // namespace sandbox
153