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..30d8a18d36d9b97c1fe6469b917f36b80ba18994 |
--- /dev/null |
+++ b/sandbox/linux/services/yama_unittests.cc |
@@ -0,0 +1,154 @@ |
+// 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 "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 { |
+ |
+void WaitForever() { |
Jorge Lucangeli Obes
2014/03/06 15:42:09
test_utils.{h|cc} for these functions eventually?
jln (very slow on Chromium)
2014/03/06 21:43:46
I've removed this anyways now.
|
+ while (true) |
+ pause(); |
+} |
+ |
+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"); |
+} |
+ |
+// Attempts to enable or disable Yama restrictions. |
+void SetYamaRestrictionsAndPause(bool enable_restriction) { |
+ if (enable_restriction) { |
+ Yama::RestrictPtracersToAncestors(); |
+ } else { |
+ Yama::DisableYamaRestrictions(); |
+ } |
+ WaitForever(); |
+} |
+ |
+SANDBOX_TEST(Yama, RestrictPtraceSucceedsWhenYamaPresent) { |
+ // This call will always succeed if Yama is present (however it may not be |
+ // enforcing. |
+ bool restricted = Yama::RestrictPtracersToAncestors(); |
+ CHECK_EQ(restricted, static_cast<bool>(Yama::IsPresent())); |
+} |
+ |
+TEST(Yama, RestrictPtraceWorks) { |
+ ScopedProcess process1(base::Bind(&SetYamaRestrictionsAndPause, true)); |
+ |
+ 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(&SetYamaRestrictionsAndPause, false)); |
+ ASSERT_TRUE(CanSubProcessPtrace(process2.GetPid())); |
+ } |
+} |
+ |
+SANDBOX_TEST(Yama, RestrictPtraceIsDefault) { |
+ if (!Yama::IsPresent()) |
+ return; |
+ |
+ CHECK(Yama::DisableYamaRestrictions()); |
+ ScopedProcess process1(base::Bind(&WaitForever)); |
+ 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 |