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 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. 41 void ExitZeroIfCanPtrace(pid_t pid) { 42 if (CanPtrace(pid)) { 43 _exit(0); 44 } else { 45 _exit(1); 46 } 47 } 48 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 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 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. 106 void SetYamaRestrictions(bool enable_restriction) { 107 if (enable_restriction) { 108 Yama::RestrictPtracersToAncestors(); 109 } else { 110 Yama::DisableYamaRestrictions(); 111 } 112 } 113 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 134 void DoNothing() {} 135 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