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 ULONG 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); | |
|
Mark Mentovai
2015/10/15 05:25:17
DCHECK_GT, not DCHECK_GE.
scottmg
2015/10/15 21:38:45
Done.
| |
| 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 } | |
| 149 | |
| 150 if (!NT_SUCCESS(status)) { | |
| 151 NTSTATUS_LOG(ERROR, status) << "NtQueryObject"; | |
| 152 return scoped_ptr<uint8_t[]>(); | |
| 153 } | |
| 154 | |
| 155 return buffer.Pass(); | |
|
Mark Mentovai
2015/10/15 05:25:17
buffer.resize(return_length) to trim any excess. (
Mark Mentovai
2015/10/15 15:07:22
Mark Mentovai wrote:
Mark Mentovai
2015/10/15 15:28:53
I wrote:
scottmg
2015/10/15 21:38:45
Done.
| |
| 156 } | |
| 157 | |
| 130 } // namespace | 158 } // namespace |
| 131 | 159 |
| 132 template <class Traits> | 160 template <class Traits> |
| 133 bool GetProcessBasicInformation(HANDLE process, | 161 bool GetProcessBasicInformation(HANDLE process, |
| 134 bool is_wow64, | 162 bool is_wow64, |
| 135 ProcessInfo* process_info, | 163 ProcessInfo* process_info, |
| 136 WinVMAddress* peb_address, | 164 WinVMAddress* peb_address, |
| 137 WinVMSize* peb_size) { | 165 WinVMSize* peb_size) { |
| 138 ULONG bytes_returned; | 166 ULONG bytes_returned; |
| 139 process_types::PROCESS_BASIC_INFORMATION<Traits> process_basic_information; | 167 process_types::PROCESS_BASIC_INFORMATION<Traits> process_basic_information; |
| (...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 307 | 335 |
| 308 if (memory_basic_information.RegionSize == 0) { | 336 if (memory_basic_information.RegionSize == 0) { |
| 309 LOG(ERROR) << "RegionSize == 0"; | 337 LOG(ERROR) << "RegionSize == 0"; |
| 310 return false; | 338 return false; |
| 311 } | 339 } |
| 312 } | 340 } |
| 313 | 341 |
| 314 return true; | 342 return true; |
| 315 } | 343 } |
| 316 | 344 |
| 345 template <class Traits> | |
| 346 std::vector<ProcessInfo::Handle> ProcessInfo::BuildHandleVector( | |
| 347 HANDLE process) const { | |
| 348 ULONG buffer_size = 2 * 1024 * 1024; | |
|
Mark Mentovai
2015/10/15 05:25:17
Pretty huge buffer. At its minimum size, it’s room
scottmg
2015/10/15 21:38:44
Unfortunately yes. I have ~110k handles on my syst
| |
| 349 scoped_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]); | |
| 350 | |
| 351 // Typically if the buffer were too small, STATUS_INFO_LENGTH_MISMATCH would | |
| 352 // return the correct size in the final argument, but it does not in for this | |
|
Mark Mentovai
2015/10/15 05:25:17
“in for this” → “for”
scottmg
2015/10/15 21:38:45
Done.
| |
| 353 // SYSTEM_INFORMATION_CLASS, so we loop and attempt larger sizes. | |
|
Mark Mentovai
2015/10/15 05:25:17
Is the returned size correct when this returns suc
scottmg
2015/10/15 21:38:45
Not doing anything here per vector/scoped_ptr.
| |
| 354 NTSTATUS status; | |
| 355 for (int tries = 0; tries < 5; ++tries) { | |
| 356 status = crashpad::NtQuerySystemInformation( | |
| 357 static_cast<SYSTEM_INFORMATION_CLASS>(SystemExtendedHandleInformation), | |
| 358 buffer.get(), | |
| 359 buffer_size, | |
| 360 nullptr); | |
| 361 if (NT_SUCCESS(status)) { | |
| 362 break; | |
| 363 } else if (status == STATUS_INFO_LENGTH_MISMATCH) { | |
|
Mark Mentovai
2015/10/15 05:25:17
If status is not this and not success, I don’t thi
scottmg
2015/10/15 21:38:45
Done.
| |
| 364 buffer_size *= 2; | |
| 365 buffer.reset(new uint8_t[buffer_size]); | |
| 366 } | |
| 367 } | |
| 368 | |
| 369 if (!NT_SUCCESS(status)) { | |
| 370 NTSTATUS_LOG(ERROR, status) | |
| 371 << "NtQuerySystemInformation SystemExtendedHandleInformation"; | |
| 372 return std::vector<Handle>(); | |
| 373 } | |
| 374 | |
| 375 const auto* system_handle_information_ex = | |
|
Mark Mentovai
2015/10/15 05:25:17
Could be & instead of *.
scottmg
2015/10/15 21:38:45
Done.
| |
| 376 reinterpret_cast<process_types::SYSTEM_HANDLE_INFORMATION_EX<Traits>*>( | |
|
Mark Mentovai
2015/10/15 05:25:17
Since this is system-specific and not process-spec
scottmg
2015/10/15 21:38:45
Done.
| |
| 377 buffer.get()); | |
| 378 | |
| 379 std::vector<Handle> handles; | |
| 380 | |
| 381 for (size_t i = 0; i < system_handle_information_ex->NumberOfHandles; ++i) { | |
|
Mark Mentovai
2015/10/15 05:25:17
Before entering the loop, make sure that offsetof(
Mark Mentovai
2015/10/15 15:28:53
I wrote:
scottmg
2015/10/15 21:38:45
Done.
| |
| 382 const auto& handle = system_handle_information_ex->Handles[i]; | |
| 383 if (handle.UniqueProcessId != process_id_) | |
|
Mark Mentovai
2015/10/15 05:25:16
Noticing that handle.UniqueProcessId might be 64 b
scottmg
2015/10/15 21:38:45
Yeah, good point, it's a bit weird. Since they're
| |
| 384 continue; | |
| 385 | |
| 386 // TODO(scottmg): Could special case for self. | |
| 387 HANDLE dup_handle; | |
| 388 if (!DuplicateHandle(process, | |
| 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. If we fail to duplicate, then we can't gather any more | |
| 397 // information, so just include the handle the list with what we do have. | |
| 398 Handle result_handle; | |
| 399 result_handle.handle = static_cast<uint32_t>(handle.HandleValue); | |
| 400 result_handle.attributes = handle.HandleAttributes; | |
| 401 result_handle.granted_access = handle.GrantedAccess; | |
| 402 handles.push_back(result_handle); | |
| 403 continue; | |
| 404 } | |
| 405 ScopedKernelHANDLE scoped_dup_handle(dup_handle); | |
| 406 | |
| 407 scoped_ptr<uint8_t[]> object_basic_information_buffer = | |
| 408 QueryObject(dup_handle, | |
| 409 ObjectBasicInformation, | |
| 410 sizeof(PUBLIC_OBJECT_BASIC_INFORMATION)); | |
| 411 if (!object_basic_information_buffer) | |
| 412 continue; | |
|
Mark Mentovai
2015/10/15 05:25:17
Worthwhile to add the data we could get from SYSTE
scottmg
2015/10/15 21:38:45
Done.
| |
| 413 | |
| 414 scoped_ptr<uint8_t[]> object_type_information_buffer = | |
| 415 QueryObject(dup_handle, | |
| 416 ObjectTypeInformation, | |
| 417 sizeof(PUBLIC_OBJECT_TYPE_INFORMATION)); | |
| 418 if (!object_type_information_buffer) | |
| 419 continue; | |
| 420 | |
| 421 PUBLIC_OBJECT_BASIC_INFORMATION* object_basic_information = | |
| 422 reinterpret_cast<PUBLIC_OBJECT_BASIC_INFORMATION*>( | |
| 423 object_basic_information_buffer.get()); | |
| 424 PUBLIC_OBJECT_TYPE_INFORMATION* object_type_information = | |
| 425 reinterpret_cast<PUBLIC_OBJECT_TYPE_INFORMATION*>( | |
| 426 object_type_information_buffer.get()); | |
| 427 | |
| 428 Handle result_handle; | |
|
Mark Mentovai
2015/10/15 05:25:17
Didn’t you find that MSVS 2015 didn’t like shadowi
scottmg
2015/10/15 21:38:45
With your better version above, this disappears.
| |
| 429 DCHECK_EQ(object_type_information->TypeName.Length % | |
| 430 sizeof(result_handle.type_name[0]), | |
| 431 0u); | |
| 432 result_handle.type_name = | |
| 433 std::wstring(object_type_information->TypeName.Buffer, | |
| 434 object_type_information->TypeName.Length / | |
| 435 sizeof(result_handle.type_name[0])); | |
| 436 | |
| 437 result_handle.handle = static_cast<uint32_t>(handle.HandleValue); | |
| 438 // The Attributes and GrantedAccess sometimes differ slightly between the | |
|
Mark Mentovai
2015/10/15 05:25:17
Blank before. And also a blank before pointer_coun
scottmg
2015/10/15 21:38:44
Done.
| |
| 439 // data retrieved in SYSTEM_HANDLE_INFORMATION_EX and | |
| 440 // PUBLIC_OBJECT_TYPE_INFORMATION. We prefer the values in | |
| 441 // SYSTEM_HANDLE_INFORMATION_EX because they were retrieved from the target | |
| 442 // process, rather than on the duplicated handle. | |
| 443 result_handle.attributes = handle.HandleAttributes; | |
| 444 result_handle.granted_access = handle.GrantedAccess; | |
| 445 result_handle.pointer_count = object_basic_information->PointerCount; | |
|
Mark Mentovai
2015/10/15 05:25:17
Just confirming: making a duplicate doesn’t actual
scottmg
2015/10/15 21:38:45
That one's harder to test, but I believe it's actu
| |
| 446 | |
| 447 // Subtract one to account for our DuplicateHandle(). | |
| 448 DCHECK_GT(object_basic_information->HandleCount, 1u); | |
| 449 result_handle.handle_count = object_basic_information->HandleCount - 1; | |
| 450 handles.push_back(result_handle); | |
|
Mark Mentovai
2015/10/15 05:25:16
Blank before this, too.
scottmg
2015/10/15 21:38:45
Done.
| |
| 451 } | |
| 452 return handles; | |
| 453 } | |
| 454 | |
| 317 ProcessInfo::Module::Module() : name(), dll_base(0), size(0), timestamp() { | 455 ProcessInfo::Module::Module() : name(), dll_base(0), size(0), timestamp() { |
| 318 } | 456 } |
| 319 | 457 |
| 320 ProcessInfo::Module::~Module() { | 458 ProcessInfo::Module::~Module() { |
| 321 } | 459 } |
| 322 | 460 |
| 461 ProcessInfo::Handle::Handle() | |
| 462 : type_name(), | |
| 463 handle(0), | |
| 464 attributes(0), | |
| 465 granted_access(0), | |
| 466 pointer_count(0), | |
| 467 handle_count(0) { | |
| 468 } | |
| 469 | |
| 470 ProcessInfo::Handle::~Handle() { | |
| 471 } | |
| 472 | |
| 323 ProcessInfo::ProcessInfo() | 473 ProcessInfo::ProcessInfo() |
| 324 : process_id_(), | 474 : process_id_(), |
| 325 inherited_from_process_id_(), | 475 inherited_from_process_id_(), |
| 326 command_line_(), | 476 command_line_(), |
| 327 peb_address_(0), | 477 peb_address_(0), |
| 328 peb_size_(0), | 478 peb_size_(0), |
| 329 modules_(), | 479 modules_(), |
| 330 memory_info_(), | 480 memory_info_(), |
| 481 handles_(), | |
| 331 is_64_bit_(false), | 482 is_64_bit_(false), |
| 332 is_wow64_(false), | 483 is_wow64_(false), |
| 333 initialized_() { | 484 initialized_() { |
| 334 } | 485 } |
| 335 | 486 |
| 336 ProcessInfo::~ProcessInfo() { | 487 ProcessInfo::~ProcessInfo() { |
| 337 } | 488 } |
| 338 | 489 |
| 339 bool ProcessInfo::Initialize(HANDLE process) { | 490 bool ProcessInfo::Initialize(HANDLE process) { |
| 340 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); | 491 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 380 if (!result) { | 531 if (!result) { |
| 381 LOG(ERROR) << "ReadProcessData failed"; | 532 LOG(ERROR) << "ReadProcessData failed"; |
| 382 return false; | 533 return false; |
| 383 } | 534 } |
| 384 | 535 |
| 385 if (!ReadMemoryInfo(process, is_64_bit_, this)) { | 536 if (!ReadMemoryInfo(process, is_64_bit_, this)) { |
| 386 LOG(ERROR) << "ReadMemoryInfo failed"; | 537 LOG(ERROR) << "ReadMemoryInfo failed"; |
| 387 return false; | 538 return false; |
| 388 } | 539 } |
| 389 | 540 |
| 541 if (is_64_bit_) | |
| 542 handles_ = BuildHandleVector<process_types::internal::Traits64>(process); | |
|
Mark Mentovai
2015/10/15 05:25:17
Since this can’t do anything but get all handles s
scottmg
2015/10/15 21:38:45
Done.
| |
| 543 else | |
| 544 handles_ = BuildHandleVector<process_types::internal::Traits32>(process); | |
| 545 | |
| 390 INITIALIZATION_STATE_SET_VALID(initialized_); | 546 INITIALIZATION_STATE_SET_VALID(initialized_); |
| 391 return true; | 547 return true; |
| 392 } | 548 } |
| 393 | 549 |
| 394 bool ProcessInfo::Is64Bit() const { | 550 bool ProcessInfo::Is64Bit() const { |
| 395 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | 551 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 396 return is_64_bit_; | 552 return is_64_bit_; |
| 397 } | 553 } |
| 398 | 554 |
| 399 bool ProcessInfo::IsWow64() const { | 555 bool ProcessInfo::IsWow64() const { |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 495 } else { | 651 } else { |
| 496 result.push_back(as_ranges[i]); | 652 result.push_back(as_ranges[i]); |
| 497 } | 653 } |
| 498 DCHECK(result.back().IsValid()); | 654 DCHECK(result.back().IsValid()); |
| 499 } | 655 } |
| 500 | 656 |
| 501 return result; | 657 return result; |
| 502 } | 658 } |
| 503 | 659 |
| 504 } // namespace crashpad | 660 } // namespace crashpad |
| OLD | NEW |