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 "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 |
| 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 |
| 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. |
| 58 void ExitZeroIfCanPtrace(pid_t pid) { |
| 59 if (CanPtrace(pid)) { |
| 60 _exit(0); |
| 61 } else { |
| 62 _exit(1); |
| 63 } |
| 64 } |
| 65 |
| 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 |
| 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 |
| 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. |
| 123 void SetYamaRestrictions(bool enable_restriction) { |
| 124 if (enable_restriction) { |
| 125 Yama::RestrictPtracersToAncestors(); |
| 126 } else { |
| 127 Yama::DisableYamaRestrictions(); |
| 128 } |
| 129 } |
| 130 |
| 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 |
| 154 void DoNothing() {} |
| 155 |
| 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 |
OLD | NEW |