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