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