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

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