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 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 |
OLD | NEW |