Chromium Code Reviews| 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 |