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

Side by Side Diff: handler/win/registration_server_test.cc

Issue 1126783004: Introduce RegistrationServer. (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Review comments. Created 5 years, 7 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 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
OLDNEW
« handler/win/registration_server.cc ('K') | « handler/win/registration_server.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698