Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(514)

Side by Side Diff: util/win/process_info.cc

Issue 1400413002: win: Add Handles() to ProcessInfo (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: . Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698