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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "util/win/safe_terminate_process.h"
16
17 #include <string.h>
18
19 #include <string>
20 #include <memory>
21
22 #include "base/files/file_path.h"
23 #include "base/logging.h"
24 #include "base/macros.h"
25 #include "build/build_config.h"
26 #include "gtest/gtest.h"
27 #include "test/errors.h"
28 #include "test/test_paths.h"
29 #include "test/win/child_launcher.h"
30 #include "util/win/scoped_handle.h"
31
32 namespace crashpad {
33 namespace test {
34 namespace {
35
36 // Patches executable code, saving a copy of the original code so that it can be
37 // restored on destruction.
38 class ScopedExecutablePatch {
39 public:
40 ScopedExecutablePatch(void* target, const void* source, size_t size)
41 : original_(new uint8_t[size]), target_(target), size_(size) {
42 memcpy(original_.get(), target_, size_);
43
44 ScopedVirtualProtectRWX protect_rwx(target_, size_);
45 memcpy(target_, source, size_);
46 }
47
48 ~ScopedExecutablePatch() {
49 ScopedVirtualProtectRWX protect_rwx(target_, size_);
50 memcpy(target_, original_.get(), size_);
51 }
52
53 private:
54 // Sets the protection on (address, size) to PAGE_EXECUTE_READWRITE by calling
55 // VirtualProtect(), and restores the original protection on destruction. Note
56 // that the region may span multiple pages, but the first page’s original
57 // protection will be applied to the entire region on destruction. This
58 // shouldn’t be a problem in practice for patching a function for this test’s
59 // purposes.
60 class ScopedVirtualProtectRWX {
61 public:
62 // If either the constructor or destructor fails, PCHECK() to terminate
63 // immediately, because the process will be in a weird and untrustworthy
64 // state, and gtest error handling isn’t worthwhile at that point.
65
66 ScopedVirtualProtectRWX(void* address, size_t size)
67 : address_(address), size_(size) {
68 PCHECK(VirtualProtect(
69 address_, size_, PAGE_EXECUTE_READWRITE, &old_protect_))
70 << "VirtualProtect";
71 }
72
73 ~ScopedVirtualProtectRWX() {
74 DWORD last_protect_;
75 PCHECK(VirtualProtect(address_, size_, old_protect_, &last_protect_))
76 << "VirtualProtect";
77 }
78
79 private:
80 void* address_;
81 size_t size_;
82 DWORD old_protect_;
83
84 DISALLOW_COPY_AND_ASSIGN(ScopedVirtualProtectRWX);
85 };
86
87 std::unique_ptr<uint8_t[]> original_;
88 void* target_;
89 size_t size_;
90
91 DISALLOW_COPY_AND_ASSIGN(ScopedExecutablePatch);
92 };
93
94 TEST(SafeTerminateProcess, PatchBadly) {
95 // This is a test of SafeTerminateProcess(), but it doesn’t actually terminate
96 // anything. Instead, it works with a process handle for the current process
97 // that doesn’t have PROCESS_TERMINATE access. The whole point of this test is
98 // to patch the real TerminateProcess() badly with a cdecl implementation to
99 // ensure that SafeTerminateProcess() can recover from such gross misconduct.
100 // The actual termination isn’t relevant to this test.
101 //
102 // Notably, don’t duplicate the process handle with PROCESS_TERMINATE access
103 // or with the DUPLICATE_SAME_ACCESS option. The SafeTerminateProcess() calls
104 // that follow operate on a duplicate of the current process’ process handle,
105 // and they’re supposed to fail, not terminate this process.
106 HANDLE process;
107 ASSERT_TRUE(DuplicateHandle(GetCurrentProcess(),
108 GetCurrentProcess(),
109 GetCurrentProcess(),
110 &process,
111 PROCESS_QUERY_INFORMATION,
112 false,
113 0))
114 << ErrorMessage("DuplicateHandle");
115 ScopedKernelHANDLE process_owner(process);
116
117 // Make sure that TerminateProcess() works as a baseline.
118 SetLastError(ERROR_SUCCESS);
119 EXPECT_FALSE(TerminateProcess(process, 0));
120 EXPECT_EQ(GetLastError(), ERROR_ACCESS_DENIED);
121
122 // Make sure that SafeTerminateProcess() works, calling through to
123 // TerminateProcess() properly.
124 SetLastError(ERROR_SUCCESS);
125 EXPECT_FALSE(SafeTerminateProcess(process, 0));
126 EXPECT_EQ(GetLastError(), ERROR_ACCESS_DENIED);
127
128 {
129 // Patch TerminateProcess() badly. This turns it into a no-op that returns 0
130 // without cleaning up arguments from the stack, as a stdcall function is
131 // expected to do.
132 //
133 // This simulates the unexpected cdecl-patched TerminateProcess() as seen at
134 // https://crashpad.chromium.org/bug/179. In reality, this only affects
135 // 32-bit x86, as there’s no calling convention confusion on x86_64. It
136 // doesn’t hurt to run this test in the 64-bit environment, though.
137 const uint8_t patch[] = {
138 #if defined(ARCH_CPU_X86)
139 0x31, 0xc0, // xor eax, eax
140 #elif defined(ARCH_CPU_X86_64)
141 0x48, 0x31, 0xc0, // xor rax, rax
142 #else
143 #error Port
144 #endif
145 0xc3, // ret
146 };
147
148 void* target = reinterpret_cast<void*>(TerminateProcess);
149 ScopedExecutablePatch executable_patch(target, patch, arraysize(patch));
150
151 // Make sure that SafeTerminateProcess() can be called. Since it’s been
152 // patched with a no-op stub, GetLastError() shouldn’t be modified.
153 SetLastError(ERROR_SUCCESS);
154 EXPECT_FALSE(SafeTerminateProcess(process, 0));
155 EXPECT_EQ(GetLastError(), ERROR_SUCCESS);
156 }
157
158 // Now that the real TerminateProcess() has been restored, verify that it
159 // still works properly.
160 SetLastError(ERROR_SUCCESS);
161 EXPECT_FALSE(SafeTerminateProcess(process, 0));
162 EXPECT_EQ(GetLastError(), ERROR_ACCESS_DENIED);
163 }
164
165 TEST(SafeTerminateProcess, TerminateChild) {
166 base::FilePath test_executable = TestPaths::Executable();
167 std::wstring child_executable =
168 test_executable.DirName()
169 .Append(test_executable.BaseName().RemoveFinalExtension().value() +
170 L"_safe_terminate_process_test_child.exe")
171 .value();
172
173 ChildLauncher child(child_executable, std::wstring());
174 ASSERT_NO_FATAL_FAILURE(child.Start());
175
176 constexpr DWORD kExitCode = 0x51ee9d1e; // Sort of like “sleep and die.”
177
178 ASSERT_TRUE(SafeTerminateProcess(child.process_handle(), kExitCode))
179 << ErrorMessage("TerminateProcess");
180 EXPECT_EQ(child.WaitForExit(), kExitCode);
181 }
182
183 } // namespace
184 } // namespace test
185 } // namespace crashpad
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698