| Index: sandbox/linux/services/yama_unittests.cc
|
| diff --git a/sandbox/linux/services/yama_unittests.cc b/sandbox/linux/services/yama_unittests.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a4100a6c37d346d0ca5af6e230444f3bc44cb596
|
| --- /dev/null
|
| +++ b/sandbox/linux/services/yama_unittests.cc
|
| @@ -0,0 +1,172 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include <errno.h>
|
| +#include <fcntl.h>
|
| +#include <sys/ptrace.h>
|
| +#include <sys/stat.h>
|
| +#include <sys/types.h>
|
| +#include <unistd.h>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/compiler_specific.h"
|
| +#include "base/posix/eintr_wrapper.h"
|
| +#include "base/strings/string_util.h"
|
| +#include "base/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 = StartsWithASCII(
|
| + base::SysInfo::OperatingSystemVersion(), "3.2", /*case_sensitive=*/false);
|
| + 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::Bind(&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::Bind(&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::Bind(&SetYamaRestrictions, false));
|
| + ASSERT_TRUE(process2.WaitForClosureToRun());
|
| + ASSERT_TRUE(CanSubProcessPtrace(process2.GetPid()));
|
| + }
|
| +}
|
| +
|
| +void DoNothing() {}
|
| +
|
| +SANDBOX_TEST(Yama, RestrictPtraceIsDefault) {
|
| + if (!Yama::IsPresent() || HasLinux32Bug())
|
| + return;
|
| +
|
| + CHECK(Yama::DisableYamaRestrictions());
|
| + ScopedProcess process1(base::Bind(&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
|
|
|