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
23namespace sandbox {
24
25namespace {
26
27bool 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
43bool 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.
60void ExitZeroIfCanPtrace(pid_t pid) {
61  if (CanPtrace(pid)) {
62    _exit(0);
63  } else {
64    _exit(1);
65  }
66}
67
68bool 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
79TEST(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
118SANDBOX_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.
125void SetYamaRestrictions(bool enable_restriction) {
126  if (enable_restriction) {
127    Yama::RestrictPtracersToAncestors();
128  } else {
129    Yama::DisableYamaRestrictions();
130  }
131}
132
133TEST(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
156SANDBOX_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