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 |