OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 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 "handler/win/registration_server.h" | |
16 | |
17 #include <windows.h> | |
18 | |
19 #include <vector> | |
20 | |
21 #include "base/basictypes.h" | |
22 #include "base/strings/string16.h" | |
23 #include "base/strings/stringprintf.h" | |
24 #include "base/strings/utf_string_conversions.h" | |
25 #include "client/crashpad_info.h" | |
26 #include "client/registration_protocol_win.h" | |
27 #include "gtest/gtest.h" | |
28 #include "util/stdlib/pointer_container.h" | |
29 #include "util/thread/thread.h" | |
30 #include "util/win/address_types.h" | |
31 #include "util/win/scoped_handle.h" | |
32 | |
33 namespace crashpad { | |
34 namespace test { | |
35 namespace { | |
36 | |
37 // Simulates a registrar to collect requests from and feed responses to the | |
38 // RegistrationServer. | |
39 class MockDelegate : public RegistrationServer::Delegate { | |
40 public: | |
41 // Records a single simulated client registration. | |
42 struct Entry { | |
43 Entry(ScopedKernelHANDLE client_process, | |
44 WinVMAddress crashpad_info_address, | |
45 HANDLE fake_request_dump_event, | |
46 HANDLE fake_dump_complete_event) | |
47 : client_process(client_process.Pass()), | |
48 crashpad_info_address(crashpad_info_address), | |
49 fake_request_dump_event_handle(fake_request_dump_event), | |
50 fake_dump_complete_event_handle(fake_dump_complete_event) {} | |
51 | |
52 ScopedKernelHANDLE client_process; | |
53 WinVMAddress crashpad_info_address; | |
54 HANDLE fake_request_dump_event_handle; | |
55 HANDLE fake_dump_complete_event_handle; | |
56 }; | |
57 | |
58 MockDelegate() | |
59 : started_event_(CreateEvent(nullptr, true, false, nullptr)), | |
60 registered_processes_(), | |
61 next_fake_handle_(1), | |
62 fail_(false) { | |
63 EXPECT_TRUE(started_event_.is_valid()); | |
64 } | |
65 | |
66 ~MockDelegate() override {} | |
67 | |
68 // Blocks until RegistrationServer::Delegate::OnStarted is invoked. | |
69 void WaitForStart() { | |
70 DWORD wait_result = WaitForSingleObject(started_event_.get(), INFINITE); | |
71 if (wait_result == WAIT_FAILED) | |
72 PLOG(ERROR); | |
73 ASSERT_EQ(wait_result, WAIT_OBJECT_0); | |
74 } | |
75 | |
76 // RegistrationServer::Delegate: | |
77 void OnStarted() override { | |
78 EXPECT_EQ(WAIT_TIMEOUT, WaitForSingleObject(started_event_.get(), 0)); | |
79 SetEvent(started_event_.get()); | |
80 } | |
81 | |
82 bool RegisterClient(ScopedKernelHANDLE client_process, | |
83 WinVMAddress crashpad_info_address, | |
84 HANDLE* request_dump_event, | |
85 HANDLE* dump_complete_event) override { | |
86 if (fail_) | |
87 return false; | |
88 | |
89 if (!request_dump_event || !dump_complete_event) { | |
90 ADD_FAILURE() << "NULL 'out' parameter."; | |
91 return false; | |
92 } | |
93 *request_dump_event = reinterpret_cast<HANDLE>(next_fake_handle_++); | |
94 *dump_complete_event = reinterpret_cast<HANDLE>(next_fake_handle_++); | |
95 | |
96 registered_processes_.push_back(new Entry(client_process.Pass(), | |
97 crashpad_info_address, | |
98 *request_dump_event, | |
99 *dump_complete_event)); | |
100 return true; | |
101 } | |
102 | |
103 // Provides access to the registered process data. | |
104 const std::vector<Entry*> registered_processes() { | |
105 return registered_processes_; | |
106 } | |
107 | |
108 // If true, causes RegisterClient to simulate registration failure. | |
109 void set_fail_mode(bool fail) { fail_ = fail; } | |
110 | |
111 private: | |
112 ScopedKernelHANDLE started_event_; | |
113 PointerVector<Entry> registered_processes_; | |
114 int next_fake_handle_; | |
115 bool fail_; | |
116 | |
117 DISALLOW_COPY_AND_ASSIGN(MockDelegate); | |
118 }; | |
119 | |
120 // Verifies that the request and response match what was received and sent by | |
121 // the MockDelegate. | |
122 void VerifyRegistration(const MockDelegate::Entry& registered_process, | |
123 const RegistrationRequest& request, | |
124 const RegistrationResponse& response) { | |
125 EXPECT_EQ(request.crashpad_info_address, | |
126 registered_process.crashpad_info_address); | |
127 EXPECT_EQ(registered_process.fake_request_dump_event_handle, | |
128 response.request_report_event); | |
129 EXPECT_EQ(registered_process.fake_dump_complete_event_handle, | |
130 response.report_complete_event); | |
131 EXPECT_EQ(request.client_process_id, | |
132 GetProcessId(registered_process.client_process.get())); | |
133 } | |
134 | |
135 // Runs the RegistrationServer on a background thread. | |
136 class RunServerThread : public Thread { | |
137 public: | |
138 // Instantiates a thread which will invoke server->Run(pipe_name, delegate). | |
139 RunServerThread(RegistrationServer* server, | |
140 const base::string16& pipe_name, | |
141 RegistrationServer::Delegate* delegate) | |
142 : server_(server), pipe_name_(pipe_name), delegate_(delegate) {} | |
143 ~RunServerThread() override {} | |
144 | |
145 private: | |
146 // Thread: | |
147 void ThreadMain() override { server_->Run(pipe_name_, delegate_); } | |
148 | |
149 RegistrationServer* server_; | |
150 base::string16 pipe_name_; | |
151 RegistrationServer::Delegate* delegate_; | |
152 | |
153 DISALLOW_COPY_AND_ASSIGN(RunServerThread); | |
154 }; | |
155 | |
156 class RegistrationServerTest : public testing::Test { | |
157 public: | |
158 RegistrationServerTest() | |
159 : server_(), | |
160 pipe_name_(L"\\\\.\\pipe\\registration_server_test_pipe_" + | |
161 base::UTF8ToUTF16( | |
162 base::StringPrintf("%08x", GetCurrentProcessId()))), | |
163 delegate_(), | |
164 server_thread_(&server_, pipe_name_, &delegate_) {} | |
165 | |
166 RegistrationServer& server() { return server_; } | |
167 MockDelegate& delegate() { return delegate_; } | |
168 Thread& server_thread() { return server_thread_; } | |
169 | |
170 // Returns a pipe handle connected to the RegistrationServer. | |
171 ScopedFileHANDLE Connect() { | |
172 ScopedFileHANDLE pipe; | |
173 const int kMaxRetries = 5; | |
174 for (int retries = 0; !pipe.is_valid() && retries < kMaxRetries; | |
175 ++retries) { | |
176 if (!WaitNamedPipe(pipe_name_.c_str(), NMPWAIT_WAIT_FOREVER)) | |
177 break; | |
178 pipe.reset(CreateFile(pipe_name_.c_str(), | |
179 GENERIC_READ | GENERIC_WRITE, | |
scottmg
2015/05/21 02:32:36
Wrong indent (fyi, there's a .clang-format in the
erikwright (departed)
2015/05/21 15:12:38
Sorry, oversight from removing leading '::'.
| |
180 0, | |
181 NULL, | |
182 OPEN_EXISTING, | |
183 SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION, | |
184 NULL)); | |
185 } | |
186 EXPECT_TRUE(pipe.is_valid()); | |
187 return pipe.Pass(); | |
188 } | |
189 | |
190 // Sends the provided request and receives a response via the provided pipe. | |
191 bool SendRequest(ScopedFileHANDLE pipe, | |
192 const void* request_buffer, | |
193 size_t request_size, | |
194 RegistrationResponse* response) { | |
195 DWORD mode = PIPE_READMODE_MESSAGE; | |
196 SetNamedPipeHandleState(pipe.get(), &mode, NULL, NULL); | |
197 DWORD bytes_read = 0; | |
198 if (TransactNamedPipe(pipe.get(), | |
199 const_cast<void*>(request_buffer), | |
scottmg
2015/05/21 02:32:37
wrong indent
erikwright (departed)
2015/05/21 15:12:38
Done.
| |
200 static_cast<DWORD>(request_size), | |
201 response, | |
202 sizeof(*response), | |
203 &bytes_read, | |
204 NULL)) { | |
205 if (bytes_read == sizeof(*response)) | |
206 return true; | |
207 } | |
208 return false; | |
209 } | |
210 | |
211 private: | |
212 RegistrationServer server_; | |
213 base::string16 pipe_name_; | |
214 MockDelegate delegate_; | |
215 RunServerThread server_thread_; | |
216 | |
217 DISALLOW_COPY_AND_ASSIGN(RegistrationServerTest); | |
218 }; | |
219 | |
220 // During destruction, ensures that the server is stopped and the background | |
221 // thread joined. | |
222 class ScopedStopServerAndJoinThread { | |
223 public: | |
224 explicit ScopedStopServerAndJoinThread(RegistrationServer* server, | |
225 Thread* thread) | |
226 : server_(server), thread_(thread) {} | |
227 ~ScopedStopServerAndJoinThread() { | |
228 server_->Stop(); | |
229 thread_->Join(); | |
230 } | |
231 | |
232 private: | |
233 RegistrationServer* server_; | |
234 Thread* thread_; | |
235 DISALLOW_COPY_AND_ASSIGN(ScopedStopServerAndJoinThread); | |
236 }; | |
237 | |
238 TEST_F(RegistrationServerTest, Instantiate) { | |
239 } | |
240 | |
241 TEST_F(RegistrationServerTest, StartAndStop) { | |
242 server_thread().Start(); | |
243 ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread( | |
244 &server(), &server_thread()); | |
245 ASSERT_NO_FATAL_FAILURE(delegate().WaitForStart()); | |
246 } | |
247 | |
248 TEST_F(RegistrationServerTest, StopWhileConnected) { | |
249 ScopedFileHANDLE connection; | |
250 { | |
251 server_thread().Start(); | |
252 ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread( | |
253 &server(), &server_thread()); | |
254 ASSERT_NO_FATAL_FAILURE(delegate().WaitForStart()); | |
255 connection = Connect(); | |
256 ASSERT_TRUE(connection.is_valid()); | |
257 // Leaving this scope causes the server to be stopped, while the connection | |
258 // is still open. | |
259 } | |
260 } | |
261 | |
262 TEST_F(RegistrationServerTest, Register) { | |
263 RegistrationRequest request = {0}; | |
264 RegistrationResponse response = {0}; | |
265 CrashpadInfo crashpad_info; | |
266 request.client_process_id = GetCurrentProcessId(); | |
267 request.crashpad_info_address = | |
268 reinterpret_cast<WinVMAddress>(&crashpad_info); | |
269 | |
270 server_thread().Start(); | |
271 ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread( | |
272 &server(), &server_thread()); | |
273 | |
274 ASSERT_NO_FATAL_FAILURE(delegate().WaitForStart()); | |
275 | |
276 ASSERT_TRUE(SendRequest(Connect(), &request, sizeof(request), &response)); | |
277 | |
278 ASSERT_EQ(1, delegate().registered_processes().size()); | |
279 VerifyRegistration(*delegate().registered_processes()[0], request, response); | |
280 } | |
281 | |
282 TEST_F(RegistrationServerTest, ForgedClientId) { | |
283 // Skip this test on pre-Vista as the forged PID detection is not supported | |
284 // there. | |
285 OSVERSIONINFO vi = {0}; | |
286 vi.dwOSVersionInfoSize = sizeof(vi); | |
287 GetVersionEx(&vi); | |
288 if (vi.dwMajorVersion < 6) | |
289 return; | |
290 | |
291 RegistrationRequest request = {0}; | |
292 RegistrationResponse response = {0}; | |
293 CrashpadInfo crashpad_info; | |
294 // Note that we forge the PID here. | |
295 request.client_process_id = GetCurrentProcessId() + 1; | |
296 request.crashpad_info_address = | |
297 reinterpret_cast<WinVMAddress>(&crashpad_info); | |
298 | |
299 server_thread().Start(); | |
300 ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread( | |
301 &server(), &server_thread()); | |
302 | |
303 ASSERT_NO_FATAL_FAILURE(delegate().WaitForStart()); | |
304 | |
305 ASSERT_FALSE(SendRequest(Connect(), &request, sizeof(request), &response)); | |
306 ASSERT_EQ(0, delegate().registered_processes().size()); | |
307 | |
308 // Correct the PID and verify that this was the only reason we failed. | |
309 request.client_process_id = GetCurrentProcessId(); | |
310 ASSERT_TRUE(SendRequest(Connect(), &request, sizeof(request), &response)); | |
311 ASSERT_EQ(1, delegate().registered_processes().size()); | |
312 VerifyRegistration(*delegate().registered_processes()[0], request, response); | |
313 } | |
314 | |
315 TEST_F(RegistrationServerTest, RegisterClientFails) { | |
316 RegistrationRequest request = {0}; | |
317 RegistrationResponse response = {0}; | |
318 CrashpadInfo crashpad_info; | |
319 request.client_process_id = GetCurrentProcessId(); | |
320 request.crashpad_info_address = | |
321 reinterpret_cast<WinVMAddress>(&crashpad_info); | |
322 | |
323 server_thread().Start(); | |
324 ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread( | |
325 &server(), &server_thread()); | |
326 | |
327 ASSERT_NO_FATAL_FAILURE(delegate().WaitForStart()); | |
328 | |
329 // Simulate some failures | |
330 delegate().set_fail_mode(true); | |
331 for (int i = 0; i < 10; ++i) { | |
332 ASSERT_FALSE(SendRequest(Connect(), &request, sizeof(request), &response)); | |
333 ASSERT_EQ(0, delegate().registered_processes().size()); | |
334 } | |
335 | |
336 // Now verify that a valid response may still be processed. | |
337 delegate().set_fail_mode(false); | |
338 ASSERT_TRUE(SendRequest(Connect(), &request, sizeof(request), &response)); | |
339 | |
340 ASSERT_EQ(1, delegate().registered_processes().size()); | |
341 VerifyRegistration(*delegate().registered_processes()[0], request, response); | |
342 } | |
343 | |
344 TEST_F(RegistrationServerTest, BadRequests) { | |
345 server_thread().Start(); | |
346 ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread( | |
347 &server(), &server_thread()); | |
348 ASSERT_NO_FATAL_FAILURE(delegate().WaitForStart()); | |
349 | |
350 RegistrationRequest request = {0}; | |
351 RegistrationResponse response = {0}; | |
352 CrashpadInfo crashpad_info; | |
353 request.client_process_id = GetCurrentProcessId(); | |
354 request.crashpad_info_address = | |
355 reinterpret_cast<WinVMAddress>(&crashpad_info); | |
356 | |
357 // Concatenate a valid request with a single byte of garbage. | |
358 std::vector<char> extra_long; | |
359 extra_long.insert(extra_long.begin(), | |
360 reinterpret_cast<char*>(&request), | |
361 reinterpret_cast<char*>(&request) + sizeof(request)); | |
362 extra_long.push_back('x'); | |
363 | |
364 for (int i = 0; i < 10; ++i) { | |
365 ASSERT_FALSE(SendRequest(Connect(), "a", 1, &response)); | |
366 ASSERT_FALSE(SendRequest( | |
367 Connect(), extra_long.data(), extra_long.size(), &response)); | |
368 ASSERT_TRUE(Connect().is_valid()); | |
369 } | |
370 | |
371 // Now verify that a valid response may still be processed. | |
372 | |
373 ASSERT_TRUE(SendRequest(Connect(), &request, sizeof(request), &response)); | |
374 | |
375 ASSERT_EQ(1, delegate().registered_processes().size()); | |
376 VerifyRegistration(*delegate().registered_processes()[0], request, response); | |
377 } | |
378 | |
379 TEST_F(RegistrationServerTest, OverlappingRequests) { | |
380 server_thread().Start(); | |
381 ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread( | |
382 &server(), &server_thread()); | |
383 ASSERT_NO_FATAL_FAILURE(delegate().WaitForStart()); | |
384 | |
385 RegistrationRequest request = {0}; | |
386 RegistrationResponse response_1 = {0}; | |
387 RegistrationResponse response_2 = {0}; | |
388 RegistrationResponse response_3 = {0}; | |
389 CrashpadInfo crashpad_info; | |
390 request.client_process_id = GetCurrentProcessId(); | |
391 request.crashpad_info_address = | |
392 reinterpret_cast<WinVMAddress>(&crashpad_info); | |
393 | |
394 ScopedFileHANDLE connection_1 = Connect(); | |
395 ASSERT_TRUE(connection_1.is_valid()); | |
396 ScopedFileHANDLE connection_2 = Connect(); | |
397 ASSERT_TRUE(connection_2.is_valid()); | |
398 ScopedFileHANDLE connection_3 = Connect(); | |
399 ASSERT_TRUE(connection_3.is_valid()); | |
400 | |
401 ASSERT_FALSE(SendRequest(connection_1.Pass(), "a", 1, &response_1)); | |
402 | |
403 ASSERT_TRUE( | |
404 SendRequest(connection_2.Pass(), &request, sizeof(request), &response_2)); | |
405 | |
406 ASSERT_TRUE(Connect().is_valid()); | |
407 | |
408 ASSERT_TRUE( | |
409 SendRequest(connection_3.Pass(), &request, sizeof(request), &response_3)); | |
410 | |
411 ASSERT_EQ(2, delegate().registered_processes().size()); | |
412 VerifyRegistration( | |
413 *delegate().registered_processes()[0], request, response_2); | |
414 VerifyRegistration( | |
415 *delegate().registered_processes()[1], request, response_3); | |
416 } | |
417 | |
418 } // namespace | |
419 } // namespace test | |
420 } // namespace crashpad | |
OLD | NEW |