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