Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Crashpad Authors. All rights reserved. | 1 // Copyright 2015 The Crashpad Authors. All rights reserved. |
| 2 // | 2 // |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 // you may not use this file except in compliance with 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 | 5 // You may obtain a copy of the License at |
| 6 // | 6 // |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 // | 8 // |
| 9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 // See the License for the specific language governing permissions and | 12 // See the License for the specific language governing permissions and |
| 13 // limitations under the License. | 13 // limitations under the License. |
| 14 | 14 |
| 15 #include "util/win/process_info.h" | 15 #include "util/win/process_info.h" |
| 16 | 16 |
| 17 #include <winternl.h> | 17 #include <winternl.h> |
| 18 | 18 |
| 19 #include <algorithm> | 19 #include <algorithm> |
| 20 #include <limits> | 20 #include <limits> |
| 21 | 21 |
| 22 #include "base/logging.h" | 22 #include "base/logging.h" |
| 23 #include "base/memory/scoped_ptr.h" | |
| 23 #include "base/strings/stringprintf.h" | 24 #include "base/strings/stringprintf.h" |
| 24 #include "base/template_util.h" | 25 #include "base/template_util.h" |
| 25 #include "build/build_config.h" | 26 #include "build/build_config.h" |
| 26 #include "util/numeric/safe_assignment.h" | 27 #include "util/numeric/safe_assignment.h" |
| 28 #include "util/win/nt_internals.h" | |
| 27 #include "util/win/ntstatus_logging.h" | 29 #include "util/win/ntstatus_logging.h" |
| 28 #include "util/win/process_structs.h" | 30 #include "util/win/process_structs.h" |
| 31 #include "util/win/scoped_handle.h" | |
| 29 | 32 |
| 30 namespace crashpad { | 33 namespace crashpad { |
| 31 | 34 |
| 32 namespace { | 35 namespace { |
| 33 | 36 |
| 34 NTSTATUS NtQueryInformationProcess(HANDLE process_handle, | 37 NTSTATUS NtQueryInformationProcess(HANDLE process_handle, |
| 35 PROCESSINFOCLASS process_information_class, | 38 PROCESSINFOCLASS process_information_class, |
| 36 PVOID process_information, | 39 PVOID process_information, |
| 37 ULONG process_information_length, | 40 ULONG process_information_length, |
| 38 PULONG return_length) { | 41 PULONG return_length) { |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 120 mbi64.BaseAddress = reinterpret_cast<ULONGLONG>(mbi.BaseAddress); | 123 mbi64.BaseAddress = reinterpret_cast<ULONGLONG>(mbi.BaseAddress); |
| 121 mbi64.AllocationBase = reinterpret_cast<ULONGLONG>(mbi.AllocationBase); | 124 mbi64.AllocationBase = reinterpret_cast<ULONGLONG>(mbi.AllocationBase); |
| 122 mbi64.AllocationProtect = mbi.AllocationProtect; | 125 mbi64.AllocationProtect = mbi.AllocationProtect; |
| 123 mbi64.RegionSize = mbi.RegionSize; | 126 mbi64.RegionSize = mbi.RegionSize; |
| 124 mbi64.State = mbi.State; | 127 mbi64.State = mbi.State; |
| 125 mbi64.Protect = mbi.Protect; | 128 mbi64.Protect = mbi.Protect; |
| 126 mbi64.Type = mbi.Type; | 129 mbi64.Type = mbi.Type; |
| 127 return mbi64; | 130 return mbi64; |
| 128 } | 131 } |
| 129 | 132 |
| 133 // NtQueryObject with a retry for size mismatch and a hint for initial size. | |
| 134 scoped_ptr<uint8_t[]> QueryObject( | |
| 135 HANDLE handle, | |
| 136 OBJECT_INFORMATION_CLASS object_information_class, | |
| 137 size_t size) { | |
| 138 ULONG return_length; | |
| 139 scoped_ptr<uint8_t[]> buffer(new uint8_t[size]); | |
| 140 NTSTATUS status = crashpad::NtQueryObject( | |
| 141 handle, object_information_class, buffer.get(), size, &return_length); | |
| 142 if (status == STATUS_INFO_LENGTH_MISMATCH) { | |
| 143 DCHECK_GE(return_length, size); | |
| 144 size = return_length; | |
| 145 buffer.reset(new uint8_t[size]); | |
| 146 status = crashpad::NtQueryObject( | |
| 147 handle, object_information_class, buffer.get(), size, &return_length); | |
| 148 if (!NT_SUCCESS(status)) { | |
| 149 NTSTATUS_LOG(ERROR, status) << "NtQueryObject"; | |
| 150 return scoped_ptr<uint8_t[]>(); | |
| 151 } | |
| 152 } | |
|
Mark Mentovai
2015/10/14 22:49:55
} else if (!NT_SUCCESS(status)) {
NTSTATUS_LOG
scottmg
2015/10/15 00:17:41
Done.
| |
| 153 | |
| 154 return buffer.Pass(); | |
| 155 } | |
| 156 | |
| 130 } // namespace | 157 } // namespace |
| 131 | 158 |
| 132 template <class Traits> | 159 template <class Traits> |
| 133 bool GetProcessBasicInformation(HANDLE process, | 160 bool GetProcessBasicInformation(HANDLE process, |
| 134 bool is_wow64, | 161 bool is_wow64, |
| 135 ProcessInfo* process_info, | 162 ProcessInfo* process_info, |
| 136 WinVMAddress* peb_address, | 163 WinVMAddress* peb_address, |
| 137 WinVMSize* peb_size) { | 164 WinVMSize* peb_size) { |
| 138 ULONG bytes_returned; | 165 ULONG bytes_returned; |
| 139 process_types::PROCESS_BASIC_INFORMATION<Traits> process_basic_information; | 166 process_types::PROCESS_BASIC_INFORMATION<Traits> process_basic_information; |
| (...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 307 | 334 |
| 308 if (memory_basic_information.RegionSize == 0) { | 335 if (memory_basic_information.RegionSize == 0) { |
| 309 LOG(ERROR) << "RegionSize == 0"; | 336 LOG(ERROR) << "RegionSize == 0"; |
| 310 return false; | 337 return false; |
| 311 } | 338 } |
| 312 } | 339 } |
| 313 | 340 |
| 314 return true; | 341 return true; |
| 315 } | 342 } |
| 316 | 343 |
| 344 template <class Traits> | |
| 345 std::vector<ProcessInfo::Handle> ProcessInfo::BuildHandleVector( | |
| 346 HANDLE process) const { | |
| 347 size_t buffer_size = 2 * 1024 * 1024; | |
| 348 scoped_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]); | |
| 349 | |
| 350 // Typically if the buffer were too small, STATUS_INFO_LENGTH_MISMATCH would | |
| 351 // return the correct size in the final argument, but it does not in for this | |
| 352 // SYSTEM_INFORMATION_CLASS, so we loop and attempt larger sizes. | |
| 353 NTSTATUS status; | |
| 354 for (int tries = 0; tries < 5; ++tries) { | |
| 355 status = crashpad::NtQuerySystemInformation( | |
|
Mark Mentovai
2015/10/14 22:49:55
Do you want a QuerySystemInformation() wrapper for
scottmg
2015/10/15 00:17:41
It's only done once at the moment, so I think it's
| |
| 356 static_cast<SYSTEM_INFORMATION_CLASS>(SystemExtendedHandleInformation), | |
| 357 buffer.get(), | |
| 358 buffer_size, | |
| 359 nullptr); | |
| 360 if (NT_SUCCESS(status)) { | |
| 361 break; | |
| 362 } else if (status == STATUS_INFO_LENGTH_MISMATCH) { | |
| 363 buffer_size *= 2; | |
|
Mark Mentovai
2015/10/14 22:49:55
Why don’t you use the ReturnLength argument to tel
scottmg
2015/10/15 00:17:41
That's the comment above the loop -- it doesn't pr
| |
| 364 buffer.reset(new uint8_t[buffer_size]); | |
| 365 } | |
| 366 } | |
| 367 | |
| 368 if (!NT_SUCCESS(status)) { | |
| 369 NTSTATUS_LOG(ERROR, status) | |
| 370 << "NtQuerySystemInformation SystemExtendedHandleInformation"; | |
| 371 return std::vector<Handle>(); | |
| 372 } | |
| 373 | |
| 374 process_types::SYSTEM_HANDLE_INFORMATION_EX< | |
| 375 Traits>* system_handle_information_ex = | |
|
Mark Mentovai
2015/10/14 22:49:55
Weird formatting (did clang-format do this?!)
scottmg
2015/10/15 00:17:41
Yup. A little better with an auto*?
| |
| 376 reinterpret_cast<process_types::SYSTEM_HANDLE_INFORMATION_EX<Traits>*>( | |
| 377 buffer.get()); | |
| 378 | |
| 379 std::vector<Handle> handles; | |
| 380 | |
| 381 for (size_t i = 0; i < system_handle_information_ex->NumberOfHandles; ++i) { | |
| 382 const auto& handle = system_handle_information_ex->Handles[i]; | |
| 383 if (handle.UniqueProcessId != process_id_) | |
| 384 continue; | |
| 385 | |
| 386 // TODO(scottmg): Could special case for self. | |
| 387 HANDLE dup_handle; | |
| 388 if (!DuplicateHandle(process, | |
|
Mark Mentovai
2015/10/14 22:49:55
In https://code.google.com/p/crashpad/issues/detai
scottmg
2015/10/15 00:17:41
DuplicateHandle()ing into ourselves scared me at f
| |
| 389 reinterpret_cast<HANDLE>(handle.HandleValue), | |
| 390 GetCurrentProcess(), | |
| 391 &dup_handle, | |
| 392 0, | |
| 393 false, | |
| 394 DUPLICATE_SAME_ACCESS)) { | |
| 395 // Some handles cannot be duplicated, for example handles of type | |
| 396 // EtwRegistration, so we simply drop these. (We also do not strictly know | |
|
Mark Mentovai
2015/10/14 22:49:55
Does MiniDumpWriteDump() lose these too?
Is there
scottmg
2015/10/15 00:17:41
Just confirmed with MiniDumpWriteDump() via https:
| |
| 397 // that we're suspended here, so there's a race between here and | |
| 398 // collecting them, though in practice the target process will always be | |
| 399 // suspended.) | |
| 400 continue; | |
| 401 } | |
| 402 ScopedKernelHANDLE scoped_dup_handle(dup_handle); | |
| 403 | |
| 404 scoped_ptr<uint8_t[]> object_basic_information_buffer = | |
| 405 QueryObject(dup_handle, | |
| 406 ObjectBasicInformation, | |
| 407 sizeof(PUBLIC_OBJECT_BASIC_INFORMATION)); | |
| 408 if (!object_basic_information_buffer) | |
| 409 continue; | |
| 410 | |
| 411 scoped_ptr<uint8_t[]> object_type_information_buffer = | |
| 412 QueryObject(dup_handle, | |
| 413 ObjectTypeInformation, | |
| 414 sizeof(PUBLIC_OBJECT_TYPE_INFORMATION)); | |
| 415 if (!object_type_information_buffer) | |
| 416 continue; | |
| 417 | |
| 418 PUBLIC_OBJECT_BASIC_INFORMATION* object_basic_information = | |
| 419 reinterpret_cast<PUBLIC_OBJECT_BASIC_INFORMATION*>( | |
| 420 object_basic_information_buffer.get()); | |
| 421 PUBLIC_OBJECT_TYPE_INFORMATION* object_type_information = | |
| 422 reinterpret_cast<PUBLIC_OBJECT_TYPE_INFORMATION*>( | |
| 423 object_type_information_buffer.get()); | |
| 424 | |
| 425 Handle result_handle; | |
| 426 result_handle.type_name = | |
| 427 std::wstring(object_type_information->TypeName.Buffer, | |
| 428 object_type_information->TypeName.Length / | |
|
Mark Mentovai
2015/10/14 22:49:55
DCHECK that Length % sizeof(result_handle.type_nam
scottmg
2015/10/15 00:17:41
Done.
| |
| 429 sizeof(result_handle.type_name[0])); | |
| 430 result_handle.handle = static_cast<uint32_t>(handle.HandleValue); | |
| 431 // The Attributes and GrantedAccess sometimes differ slightly between the | |
|
Mark Mentovai
2015/10/14 22:49:55
Blank before. Same on 439.
scottmg
2015/10/15 00:17:41
Done.
| |
| 432 // data retrieved in SYSTEM_HANDLE_INFORMATION_EX and | |
| 433 // PUBLIC_OBJECT_TYPE_INFORMATION. We prefer the values in | |
| 434 // SYSTEM_HANDLE_INFORMATION_EX because they were retrieved from the target | |
| 435 // process, rather than on the duplicated handle. | |
| 436 result_handle.attributes = handle.HandleAttributes; | |
| 437 result_handle.granted_access = handle.GrantedAccess; | |
| 438 result_handle.pointer_count = object_basic_information->PointerCount; | |
| 439 // Subtract one to account for our DuplicateHandle(). | |
| 440 DCHECK_GT(object_basic_information->HandleCount, 0u); | |
|
Mark Mentovai
2015/10/14 22:49:55
Should this be > 1? At least one original referenc
scottmg
2015/10/15 00:17:41
Yeah, done.
| |
| 441 result_handle.handle_count = object_basic_information->HandleCount - 1; | |
|
Mark Mentovai
2015/10/14 22:49:55
Happy to see this accounted for!
| |
| 442 handles.push_back(result_handle); | |
| 443 } | |
| 444 return handles; | |
| 445 } | |
| 446 | |
| 317 ProcessInfo::Module::Module() : name(), dll_base(0), size(0), timestamp() { | 447 ProcessInfo::Module::Module() : name(), dll_base(0), size(0), timestamp() { |
| 318 } | 448 } |
| 319 | 449 |
| 320 ProcessInfo::Module::~Module() { | 450 ProcessInfo::Module::~Module() { |
| 321 } | 451 } |
| 322 | 452 |
| 453 ProcessInfo::Handle::Handle() | |
| 454 : type_name(), | |
| 455 handle(), | |
|
Mark Mentovai
2015/10/14 22:49:55
Zeroes in the integer fields.
scottmg
2015/10/15 00:17:41
Done.
| |
| 456 attributes(), | |
| 457 granted_access(), | |
| 458 pointer_count(), | |
| 459 handle_count() { | |
| 460 } | |
| 461 | |
| 462 ProcessInfo::Handle::~Handle() { | |
| 463 } | |
| 464 | |
| 323 ProcessInfo::ProcessInfo() | 465 ProcessInfo::ProcessInfo() |
| 324 : process_id_(), | 466 : process_id_(), |
| 325 inherited_from_process_id_(), | 467 inherited_from_process_id_(), |
| 326 command_line_(), | 468 command_line_(), |
| 327 peb_address_(0), | 469 peb_address_(0), |
| 328 peb_size_(0), | 470 peb_size_(0), |
| 329 modules_(), | 471 modules_(), |
| 330 memory_info_(), | 472 memory_info_(), |
| 331 is_64_bit_(false), | 473 is_64_bit_(false), |
|
Mark Mentovai
2015/10/14 22:49:55
handles_()
scottmg
2015/10/15 00:17:41
Done.
| |
| 332 is_wow64_(false), | 474 is_wow64_(false), |
| 333 initialized_() { | 475 initialized_() { |
| 334 } | 476 } |
| 335 | 477 |
| 336 ProcessInfo::~ProcessInfo() { | 478 ProcessInfo::~ProcessInfo() { |
| 337 } | 479 } |
| 338 | 480 |
| 339 bool ProcessInfo::Initialize(HANDLE process) { | 481 bool ProcessInfo::Initialize(HANDLE process) { |
| 340 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); | 482 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); |
| 341 | 483 |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 380 if (!result) { | 522 if (!result) { |
| 381 LOG(ERROR) << "ReadProcessData failed"; | 523 LOG(ERROR) << "ReadProcessData failed"; |
| 382 return false; | 524 return false; |
| 383 } | 525 } |
| 384 | 526 |
| 385 if (!ReadMemoryInfo(process, is_64_bit_, this)) { | 527 if (!ReadMemoryInfo(process, is_64_bit_, this)) { |
| 386 LOG(ERROR) << "ReadMemoryInfo failed"; | 528 LOG(ERROR) << "ReadMemoryInfo failed"; |
| 387 return false; | 529 return false; |
| 388 } | 530 } |
| 389 | 531 |
| 532 if (is_64_bit_) | |
| 533 handles_ = BuildHandleVector<process_types::internal::Traits64>(process); | |
| 534 else | |
| 535 handles_ = BuildHandleVector<process_types::internal::Traits32>(process); | |
| 536 | |
| 390 INITIALIZATION_STATE_SET_VALID(initialized_); | 537 INITIALIZATION_STATE_SET_VALID(initialized_); |
| 391 return true; | 538 return true; |
| 392 } | 539 } |
| 393 | 540 |
| 394 bool ProcessInfo::Is64Bit() const { | 541 bool ProcessInfo::Is64Bit() const { |
| 395 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | 542 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 396 return is_64_bit_; | 543 return is_64_bit_; |
| 397 } | 544 } |
| 398 | 545 |
| 399 bool ProcessInfo::IsWow64() const { | 546 bool ProcessInfo::IsWow64() const { |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 495 } else { | 642 } else { |
| 496 result.push_back(as_ranges[i]); | 643 result.push_back(as_ranges[i]); |
| 497 } | 644 } |
| 498 DCHECK(result.back().IsValid()); | 645 DCHECK(result.back().IsValid()); |
| 499 } | 646 } |
| 500 | 647 |
| 501 return result; | 648 return result; |
| 502 } | 649 } |
| 503 | 650 |
| 504 } // namespace crashpad | 651 } // namespace crashpad |
| OLD | NEW |