Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(673)

Unified Diff: third_party/crashpad/crashpad/util/win/safe_terminate_process_test.cc

Issue 2833533003: Update Crashpad to f487da4ff2c47a129e2f8f3a7e0c769b54e4585f (Closed)
Patch Set: Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/crashpad/crashpad/util/win/safe_terminate_process_test.cc
diff --git a/third_party/crashpad/crashpad/util/win/safe_terminate_process_test.cc b/third_party/crashpad/crashpad/util/win/safe_terminate_process_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..2fbc8483e9f4757f2f37a5bf7d20fa4d21680d91
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/win/safe_terminate_process_test.cc
@@ -0,0 +1,185 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/safe_terminate_process.h"
+
+#include <string.h>
+
+#include <string>
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "gtest/gtest.h"
+#include "test/errors.h"
+#include "test/test_paths.h"
+#include "test/win/child_launcher.h"
+#include "util/win/scoped_handle.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+// Patches executable code, saving a copy of the original code so that it can be
+// restored on destruction.
+class ScopedExecutablePatch {
+ public:
+ ScopedExecutablePatch(void* target, const void* source, size_t size)
+ : original_(new uint8_t[size]), target_(target), size_(size) {
+ memcpy(original_.get(), target_, size_);
+
+ ScopedVirtualProtectRWX protect_rwx(target_, size_);
+ memcpy(target_, source, size_);
+ }
+
+ ~ScopedExecutablePatch() {
+ ScopedVirtualProtectRWX protect_rwx(target_, size_);
+ memcpy(target_, original_.get(), size_);
+ }
+
+ private:
+ // Sets the protection on (address, size) to PAGE_EXECUTE_READWRITE by calling
+ // VirtualProtect(), and restores the original protection on destruction. Note
+ // that the region may span multiple pages, but the first page’s original
+ // protection will be applied to the entire region on destruction. This
+ // shouldn’t be a problem in practice for patching a function for this test’s
+ // purposes.
+ class ScopedVirtualProtectRWX {
+ public:
+ // If either the constructor or destructor fails, PCHECK() to terminate
+ // immediately, because the process will be in a weird and untrustworthy
+ // state, and gtest error handling isn’t worthwhile at that point.
+
+ ScopedVirtualProtectRWX(void* address, size_t size)
+ : address_(address), size_(size) {
+ PCHECK(VirtualProtect(
+ address_, size_, PAGE_EXECUTE_READWRITE, &old_protect_))
+ << "VirtualProtect";
+ }
+
+ ~ScopedVirtualProtectRWX() {
+ DWORD last_protect_;
+ PCHECK(VirtualProtect(address_, size_, old_protect_, &last_protect_))
+ << "VirtualProtect";
+ }
+
+ private:
+ void* address_;
+ size_t size_;
+ DWORD old_protect_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedVirtualProtectRWX);
+ };
+
+ std::unique_ptr<uint8_t[]> original_;
+ void* target_;
+ size_t size_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedExecutablePatch);
+};
+
+TEST(SafeTerminateProcess, PatchBadly) {
+ // This is a test of SafeTerminateProcess(), but it doesn’t actually terminate
+ // anything. Instead, it works with a process handle for the current process
+ // that doesn’t have PROCESS_TERMINATE access. The whole point of this test is
+ // to patch the real TerminateProcess() badly with a cdecl implementation to
+ // ensure that SafeTerminateProcess() can recover from such gross misconduct.
+ // The actual termination isn’t relevant to this test.
+ //
+ // Notably, don’t duplicate the process handle with PROCESS_TERMINATE access
+ // or with the DUPLICATE_SAME_ACCESS option. The SafeTerminateProcess() calls
+ // that follow operate on a duplicate of the current process’ process handle,
+ // and they’re supposed to fail, not terminate this process.
+ HANDLE process;
+ ASSERT_TRUE(DuplicateHandle(GetCurrentProcess(),
+ GetCurrentProcess(),
+ GetCurrentProcess(),
+ &process,
+ PROCESS_QUERY_INFORMATION,
+ false,
+ 0))
+ << ErrorMessage("DuplicateHandle");
+ ScopedKernelHANDLE process_owner(process);
+
+ // Make sure that TerminateProcess() works as a baseline.
+ SetLastError(ERROR_SUCCESS);
+ EXPECT_FALSE(TerminateProcess(process, 0));
+ EXPECT_EQ(GetLastError(), ERROR_ACCESS_DENIED);
+
+ // Make sure that SafeTerminateProcess() works, calling through to
+ // TerminateProcess() properly.
+ SetLastError(ERROR_SUCCESS);
+ EXPECT_FALSE(SafeTerminateProcess(process, 0));
+ EXPECT_EQ(GetLastError(), ERROR_ACCESS_DENIED);
+
+ {
+ // Patch TerminateProcess() badly. This turns it into a no-op that returns 0
+ // without cleaning up arguments from the stack, as a stdcall function is
+ // expected to do.
+ //
+ // This simulates the unexpected cdecl-patched TerminateProcess() as seen at
+ // https://crashpad.chromium.org/bug/179. In reality, this only affects
+ // 32-bit x86, as there’s no calling convention confusion on x86_64. It
+ // doesn’t hurt to run this test in the 64-bit environment, though.
+ const uint8_t patch[] = {
+#if defined(ARCH_CPU_X86)
+ 0x31, 0xc0, // xor eax, eax
+#elif defined(ARCH_CPU_X86_64)
+ 0x48, 0x31, 0xc0, // xor rax, rax
+#else
+#error Port
+#endif
+ 0xc3, // ret
+ };
+
+ void* target = reinterpret_cast<void*>(TerminateProcess);
+ ScopedExecutablePatch executable_patch(target, patch, arraysize(patch));
+
+ // Make sure that SafeTerminateProcess() can be called. Since it’s been
+ // patched with a no-op stub, GetLastError() shouldn’t be modified.
+ SetLastError(ERROR_SUCCESS);
+ EXPECT_FALSE(SafeTerminateProcess(process, 0));
+ EXPECT_EQ(GetLastError(), ERROR_SUCCESS);
+ }
+
+ // Now that the real TerminateProcess() has been restored, verify that it
+ // still works properly.
+ SetLastError(ERROR_SUCCESS);
+ EXPECT_FALSE(SafeTerminateProcess(process, 0));
+ EXPECT_EQ(GetLastError(), ERROR_ACCESS_DENIED);
+}
+
+TEST(SafeTerminateProcess, TerminateChild) {
+ base::FilePath test_executable = TestPaths::Executable();
+ std::wstring child_executable =
+ test_executable.DirName()
+ .Append(test_executable.BaseName().RemoveFinalExtension().value() +
+ L"_safe_terminate_process_test_child.exe")
+ .value();
+
+ ChildLauncher child(child_executable, std::wstring());
+ ASSERT_NO_FATAL_FAILURE(child.Start());
+
+ constexpr DWORD kExitCode = 0x51ee9d1e; // Sort of like “sleep and die.”
+
+ ASSERT_TRUE(SafeTerminateProcess(child.process_handle(), kExitCode))
+ << ErrorMessage("TerminateProcess");
+ EXPECT_EQ(child.WaitForExit(), kExitCode);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad

Powered by Google App Engine
This is Rietveld 408576698