File: yama_unittest.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (175 lines) | stat: -rw-r--r-- 5,219 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/390223051): Remove C-library calls to fix the errors.
#pragma allow_unsafe_libc_calls
#endif

#include <errno.h>
#include <fcntl.h>
#include <sys/ptrace.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/posix/eintr_wrapper.h"
#include "base/strings/string_util.h"
#include "base/system/sys_info.h"
#include "sandbox/linux/services/scoped_process.h"
#include "sandbox/linux/services/yama.h"
#include "sandbox/linux/tests/unit_tests.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace sandbox {

namespace {

bool HasLinux32Bug() {
#if defined(__i386__)
  // On 3.2 kernels, yama doesn't work for 32-bit binaries on 64-bit kernels.
  // This is fixed in 3.4.
  bool is_kernel_64bit =
      base::SysInfo::OperatingSystemArchitecture() == "x86_64";
  bool is_linux = base::SysInfo::OperatingSystemName() == "Linux";
  bool is_3_dot_2 =
      base::StartsWith(base::SysInfo::OperatingSystemVersion(), "3.2",
                       base::CompareCase::INSENSITIVE_ASCII);
  if (is_kernel_64bit && is_linux && is_3_dot_2)
    return true;
#endif  // defined(__i386__)
  return false;
}

bool CanPtrace(pid_t pid) {
  int ret;
  ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
  if (ret == -1) {
    CHECK_EQ(EPERM, errno);
    return false;
  }
  // Wait for the process to be stopped so that it can be detached.
  siginfo_t process_info;
  int wait_ret = HANDLE_EINTR(waitid(P_PID, pid, &process_info, WSTOPPED));
  PCHECK(0 == wait_ret);
  PCHECK(0 == ptrace(PTRACE_DETACH, pid, NULL, NULL));
  return true;
}

// _exit(0) if pid can be ptraced by the current process.
// _exit(1) otherwise.
void ExitZeroIfCanPtrace(pid_t pid) {
  if (CanPtrace(pid)) {
    _exit(0);
  } else {
    _exit(1);
  }
}

bool CanSubProcessPtrace(pid_t pid) {
  ScopedProcess process(base::BindOnce(&ExitZeroIfCanPtrace, pid));
  bool signaled;
  int exit_code = process.WaitForExit(&signaled);
  CHECK(!signaled);
  return 0 == exit_code;
}

// The tests below assume that the system-level configuration will not change
// while they run.

TEST(Yama, GetStatus) {
  int status1 = Yama::GetStatus();

  // Check that the value is a possible bitmask.
  ASSERT_LE(0, status1);
  ASSERT_GE(Yama::STATUS_KNOWN | Yama::STATUS_PRESENT | Yama::STATUS_ENFORCING |
                Yama::STATUS_STRICT_ENFORCING,
            status1);

  // The status should not just be a random value.
  int status2 = Yama::GetStatus();
  EXPECT_EQ(status1, status2);

  // This test is not running sandboxed, there is no reason to not know the
  // status.
  EXPECT_NE(0, Yama::STATUS_KNOWN & status1);

  if (status1 & Yama::STATUS_STRICT_ENFORCING) {
    // If Yama is strictly enforcing, it is also enforcing.
    EXPECT_TRUE(status1 & Yama::STATUS_ENFORCING);
  }

  if (status1 & Yama::STATUS_ENFORCING) {
    // If Yama is enforcing, Yama is present.
    EXPECT_NE(0, status1 & Yama::STATUS_PRESENT);
  }

  // Verify that the helper functions work as intended.
  EXPECT_EQ(static_cast<bool>(status1 & Yama::STATUS_ENFORCING),
            Yama::IsEnforcing());
  EXPECT_EQ(static_cast<bool>(status1 & Yama::STATUS_PRESENT),
            Yama::IsPresent());

  fprintf(stdout, "Yama present: %s - enforcing: %s\n",
          Yama::IsPresent() ? "Y" : "N", Yama::IsEnforcing() ? "Y" : "N");
}

SANDBOX_TEST(Yama, RestrictPtraceSucceedsWhenYamaPresent) {
  // This call will succeed iff Yama is present.
  bool restricted = Yama::RestrictPtracersToAncestors();
  CHECK_EQ(restricted, Yama::IsPresent());
}

// Attempts to enable or disable Yama restrictions.
void SetYamaRestrictions(bool enable_restriction) {
  if (enable_restriction) {
    Yama::RestrictPtracersToAncestors();
  } else {
    Yama::DisableYamaRestrictions();
  }
}

TEST(Yama, RestrictPtraceWorks) {
  if (HasLinux32Bug())
    return;

  ScopedProcess process1(base::BindOnce(&SetYamaRestrictions, true));
  ASSERT_TRUE(process1.WaitForClosureToRun());

  if (Yama::IsEnforcing()) {
    // A sibling process cannot ptrace process1.
    ASSERT_FALSE(CanSubProcessPtrace(process1.GetPid()));
  }

  if (!(Yama::GetStatus() & Yama::STATUS_STRICT_ENFORCING)) {
    // However, parent can ptrace process1.
    ASSERT_TRUE(CanPtrace(process1.GetPid()));

    // A sibling can ptrace process2 which disables any Yama protection.
    ScopedProcess process2(base::BindOnce(&SetYamaRestrictions, false));
    ASSERT_TRUE(process2.WaitForClosureToRun());
    ASSERT_TRUE(CanSubProcessPtrace(process2.GetPid()));
  }
}

SANDBOX_TEST(Yama, RestrictPtraceIsDefault) {
  if (!Yama::IsPresent() || HasLinux32Bug())
    return;

  CHECK(Yama::DisableYamaRestrictions());
  ScopedProcess process1{base::DoNothing()};

  if (Yama::IsEnforcing()) {
    // Check that process1 is protected by Yama, even though it has
    // been created from a process that disabled Yama.
    CHECK(!CanSubProcessPtrace(process1.GetPid()));
  }
}

}  // namespace

}  // namespace sandbox