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