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 "util/win/process_info.h"
16
17 #include <stddef.h>
18 #include <winternl.h>
19
20 #include <algorithm>
21 #include <limits>
22 #include <memory>
23 #include <new>
24 #include <type_traits>
25
26 #include "base/logging.h"
27 #include "base/memory/free_deleter.h"
28 #include "base/process/memory.h"
29 #include "base/strings/stringprintf.h"
30 #include "build/build_config.h"
31 #include "util/misc/from_pointer_cast.h"
32 #include "util/numeric/safe_assignment.h"
33 #include "util/win/get_function.h"
34 #include "util/win/handle.h"
35 #include "util/win/nt_internals.h"
36 #include "util/win/ntstatus_logging.h"
37 #include "util/win/process_structs.h"
38 #include "util/win/scoped_handle.h"
39
40 namespace crashpad {
41
42 namespace {
43
44 using UniqueMallocPtr = std::unique_ptr<uint8_t[], base::FreeDeleter>;
45
UncheckedAllocate(size_t size)46 UniqueMallocPtr UncheckedAllocate(size_t size) {
47 void* raw_ptr = nullptr;
48 if (!base::UncheckedMalloc(size, &raw_ptr))
49 return UniqueMallocPtr();
50
51 return UniqueMallocPtr(new (raw_ptr) uint8_t[size]);
52 }
53
NtQueryInformationProcess(HANDLE process_handle,PROCESSINFOCLASS process_information_class,PVOID process_information,ULONG process_information_length,PULONG return_length)54 NTSTATUS NtQueryInformationProcess(HANDLE process_handle,
55 PROCESSINFOCLASS process_information_class,
56 PVOID process_information,
57 ULONG process_information_length,
58 PULONG return_length) {
59 static const auto nt_query_information_process =
60 GET_FUNCTION_REQUIRED(L"ntdll.dll", ::NtQueryInformationProcess);
61 return nt_query_information_process(process_handle,
62 process_information_class,
63 process_information,
64 process_information_length,
65 return_length);
66 }
67
IsProcessWow64(HANDLE process_handle)68 bool IsProcessWow64(HANDLE process_handle) {
69 static const auto is_wow64_process =
70 GET_FUNCTION(L"kernel32.dll", ::IsWow64Process);
71 if (!is_wow64_process)
72 return false;
73 BOOL is_wow64;
74 if (!is_wow64_process(process_handle, &is_wow64)) {
75 PLOG(ERROR) << "IsWow64Process";
76 return false;
77 }
78 return !!is_wow64;
79 }
80
81 template <class T>
ReadUnicodeString(HANDLE process,const process_types::UNICODE_STRING<T> & us,std::wstring * result)82 bool ReadUnicodeString(HANDLE process,
83 const process_types::UNICODE_STRING<T>& us,
84 std::wstring* result) {
85 if (us.Length == 0) {
86 result->clear();
87 return true;
88 }
89 DCHECK_EQ(us.Length % sizeof(wchar_t), 0u);
90 result->resize(us.Length / sizeof(wchar_t));
91 SIZE_T bytes_read;
92 if (!ReadProcessMemory(
93 process,
94 reinterpret_cast<const void*>(static_cast<uintptr_t>(us.Buffer)),
95 &result->operator[](0),
96 us.Length,
97 &bytes_read)) {
98 PLOG(ERROR) << "ReadProcessMemory UNICODE_STRING";
99 return false;
100 }
101 if (bytes_read != us.Length) {
102 LOG(ERROR) << "ReadProcessMemory UNICODE_STRING incorrect size";
103 return false;
104 }
105 return true;
106 }
107
108 template <class T>
ReadStruct(HANDLE process,WinVMAddress at,T * into)109 bool ReadStruct(HANDLE process, WinVMAddress at, T* into) {
110 SIZE_T bytes_read;
111 if (!ReadProcessMemory(process,
112 reinterpret_cast<const void*>(at),
113 into,
114 sizeof(T),
115 &bytes_read)) {
116 // We don't have a name for the type we're reading, so include the signature
117 // to get the type of T.
118 PLOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__;
119 return false;
120 }
121 if (bytes_read != sizeof(T)) {
122 LOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__ << " incorrect size";
123 return false;
124 }
125 return true;
126 }
127
RegionIsAccessible(const MEMORY_BASIC_INFORMATION64 & memory_info)128 bool RegionIsAccessible(const MEMORY_BASIC_INFORMATION64& memory_info) {
129 return memory_info.State == MEM_COMMIT &&
130 (memory_info.Protect & PAGE_NOACCESS) == 0 &&
131 (memory_info.Protect & PAGE_GUARD) == 0;
132 }
133
MemoryBasicInformationToMemoryBasicInformation64(const MEMORY_BASIC_INFORMATION & mbi)134 MEMORY_BASIC_INFORMATION64 MemoryBasicInformationToMemoryBasicInformation64(
135 const MEMORY_BASIC_INFORMATION& mbi) {
136 MEMORY_BASIC_INFORMATION64 mbi64 = {0};
137 mbi64.BaseAddress = FromPointerCast<ULONGLONG>(mbi.BaseAddress);
138 mbi64.AllocationBase = reinterpret_cast<ULONGLONG>(mbi.AllocationBase);
139 mbi64.AllocationProtect = mbi.AllocationProtect;
140 mbi64.RegionSize = mbi.RegionSize;
141 mbi64.State = mbi.State;
142 mbi64.Protect = mbi.Protect;
143 mbi64.Type = mbi.Type;
144 return mbi64;
145 }
146
147 // NtQueryObject with a retry for size mismatch as well as a minimum size to
148 // retrieve (and expect).
QueryObject(HANDLE handle,OBJECT_INFORMATION_CLASS object_information_class,ULONG minimum_size)149 std::unique_ptr<uint8_t[]> QueryObject(
150 HANDLE handle,
151 OBJECT_INFORMATION_CLASS object_information_class,
152 ULONG minimum_size) {
153 ULONG size = minimum_size;
154 ULONG return_length;
155 std::unique_ptr<uint8_t[]> buffer(new uint8_t[size]);
156 NTSTATUS status = crashpad::NtQueryObject(
157 handle, object_information_class, buffer.get(), size, &return_length);
158 if (status == STATUS_INFO_LENGTH_MISMATCH) {
159 DCHECK_GT(return_length, size);
160 size = return_length;
161
162 // Free the old buffer before attempting to allocate a new one.
163 buffer.reset();
164
165 buffer.reset(new uint8_t[size]);
166 status = crashpad::NtQueryObject(
167 handle, object_information_class, buffer.get(), size, &return_length);
168 }
169
170 if (!NT_SUCCESS(status)) {
171 NTSTATUS_LOG(ERROR, status) << "NtQueryObject";
172 return nullptr;
173 }
174
175 DCHECK_LE(return_length, size);
176 DCHECK_GE(return_length, minimum_size);
177 return buffer;
178 }
179
180 } // namespace
181
182 template <class Traits>
GetProcessBasicInformation(HANDLE process,bool is_wow64,ProcessInfo * process_info,WinVMAddress * peb_address,WinVMSize * peb_size)183 bool GetProcessBasicInformation(HANDLE process,
184 bool is_wow64,
185 ProcessInfo* process_info,
186 WinVMAddress* peb_address,
187 WinVMSize* peb_size) {
188 ULONG bytes_returned;
189 process_types::PROCESS_BASIC_INFORMATION<Traits> process_basic_information;
190 NTSTATUS status =
191 crashpad::NtQueryInformationProcess(process,
192 ProcessBasicInformation,
193 &process_basic_information,
194 sizeof(process_basic_information),
195 &bytes_returned);
196 if (!NT_SUCCESS(status)) {
197 NTSTATUS_LOG(ERROR, status) << "NtQueryInformationProcess";
198 return false;
199 }
200 if (bytes_returned != sizeof(process_basic_information)) {
201 LOG(ERROR) << "NtQueryInformationProcess incorrect size";
202 return false;
203 }
204
205 // API functions (e.g. OpenProcess) take only a DWORD, so there's no sense in
206 // maintaining the top bits.
207 process_info->process_id_ =
208 static_cast<DWORD>(process_basic_information.UniqueProcessId);
209 process_info->inherited_from_process_id_ = static_cast<DWORD>(
210 process_basic_information.InheritedFromUniqueProcessId);
211
212 // We now want to read the PEB to gather the rest of our information. The
213 // PebBaseAddress as returned above is what we want for 64-on-64 and 32-on-32,
214 // but for Wow64, we want to read the 32 bit PEB (a Wow64 process has both).
215 // The address of this is found by a second call to NtQueryInformationProcess.
216 if (!is_wow64) {
217 *peb_address = process_basic_information.PebBaseAddress;
218 *peb_size = sizeof(process_types::PEB<Traits>);
219 } else {
220 ULONG_PTR wow64_peb_address;
221 status = crashpad::NtQueryInformationProcess(process,
222 ProcessWow64Information,
223 &wow64_peb_address,
224 sizeof(wow64_peb_address),
225 &bytes_returned);
226 if (!NT_SUCCESS(status)) {
227 NTSTATUS_LOG(ERROR, status) << "NtQueryInformationProcess";
228 return false;
229 }
230 if (bytes_returned != sizeof(wow64_peb_address)) {
231 LOG(ERROR) << "NtQueryInformationProcess incorrect size";
232 return false;
233 }
234 *peb_address = wow64_peb_address;
235 *peb_size = sizeof(process_types::PEB<process_types::internal::Traits32>);
236 }
237
238 return true;
239 }
240
241 template <class Traits>
ReadProcessData(HANDLE process,WinVMAddress peb_address_vmaddr,ProcessInfo * process_info)242 bool ReadProcessData(HANDLE process,
243 WinVMAddress peb_address_vmaddr,
244 ProcessInfo* process_info) {
245 typename Traits::Pointer peb_address;
246 if (!AssignIfInRange(&peb_address, peb_address_vmaddr)) {
247 LOG(ERROR) << base::StringPrintf("peb address 0x%llx out of range",
248 peb_address_vmaddr);
249 return false;
250 }
251
252 // Try to read the process environment block.
253 process_types::PEB<Traits> peb;
254 if (!ReadStruct(process, peb_address, &peb))
255 return false;
256
257 process_types::RTL_USER_PROCESS_PARAMETERS<Traits> process_parameters;
258 if (!ReadStruct(process, peb.ProcessParameters, &process_parameters))
259 return false;
260
261 if (!ReadUnicodeString(process,
262 process_parameters.CommandLine,
263 &process_info->command_line_)) {
264 return false;
265 }
266
267 process_types::PEB_LDR_DATA<Traits> peb_ldr_data;
268 if (!ReadStruct(process, peb.Ldr, &peb_ldr_data))
269 return false;
270
271 process_types::LDR_DATA_TABLE_ENTRY<Traits> ldr_data_table_entry;
272 ProcessInfo::Module module;
273
274 // Walk the PEB LDR structure (doubly-linked list) to get the list of loaded
275 // modules. We use this method rather than EnumProcessModules to get the
276 // modules in load order rather than memory order. Notably, this includes the
277 // main executable as the first element.
278 typename Traits::Pointer last = peb_ldr_data.InLoadOrderModuleList.Blink;
279 for (typename Traits::Pointer cur = peb_ldr_data.InLoadOrderModuleList.Flink;;
280 cur = ldr_data_table_entry.InLoadOrderLinks.Flink) {
281 // |cur| is the pointer to the LIST_ENTRY embedded in the
282 // LDR_DATA_TABLE_ENTRY, in the target process's address space. So we need
283 // to read from the target, and also offset back to the beginning of the
284 // structure.
285 if (!ReadStruct(process,
286 static_cast<WinVMAddress>(cur) -
287 offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>,
288 InLoadOrderLinks),
289 &ldr_data_table_entry)) {
290 break;
291 }
292 // TODO(scottmg): Capture Checksum, etc. too?
293 if (!ReadUnicodeString(
294 process, ldr_data_table_entry.FullDllName, &module.name)) {
295 module.name = L"???";
296 }
297 module.dll_base = ldr_data_table_entry.DllBase;
298 module.size = ldr_data_table_entry.SizeOfImage;
299 module.timestamp = ldr_data_table_entry.TimeDateStamp;
300 process_info->modules_.push_back(module);
301 if (cur == last)
302 break;
303 }
304
305 return true;
306 }
307
ReadMemoryInfo(HANDLE process,bool is_64_bit,ProcessInfo * process_info)308 bool ReadMemoryInfo(HANDLE process, bool is_64_bit, ProcessInfo* process_info) {
309 DCHECK(process_info->memory_info_.empty());
310
311 constexpr WinVMAddress min_address = 0;
312 // We can't use GetSystemInfo() to get the address space range for another
313 // process. VirtualQueryEx() will fail with ERROR_INVALID_PARAMETER if the
314 // address is above the highest memory address accessible to the process, so
315 // we just probe the entire potential range (2^32 for x86, or 2^64 for x64).
316 const WinVMAddress max_address = is_64_bit
317 ? std::numeric_limits<uint64_t>::max()
318 : std::numeric_limits<uint32_t>::max();
319 MEMORY_BASIC_INFORMATION memory_basic_information;
320 for (WinVMAddress address = min_address; address <= max_address;
321 address += memory_basic_information.RegionSize) {
322 size_t result = VirtualQueryEx(process,
323 reinterpret_cast<void*>(address),
324 &memory_basic_information,
325 sizeof(memory_basic_information));
326 if (result == 0) {
327 if (GetLastError() == ERROR_INVALID_PARAMETER)
328 break;
329 PLOG(ERROR) << "VirtualQueryEx";
330 return false;
331 }
332
333 process_info->memory_info_.push_back(
334 MemoryBasicInformationToMemoryBasicInformation64(
335 memory_basic_information));
336
337 if (memory_basic_information.RegionSize == 0) {
338 LOG(ERROR) << "RegionSize == 0";
339 return false;
340 }
341 }
342
343 return true;
344 }
345
BuildHandleVector(HANDLE process) const346 std::vector<ProcessInfo::Handle> ProcessInfo::BuildHandleVector(
347 HANDLE process) const {
348 ULONG buffer_size = 2 * 1024 * 1024;
349 // Typically if the buffer were too small, STATUS_INFO_LENGTH_MISMATCH would
350 // return the correct size in the final argument, but it does not for
351 // SystemExtendedHandleInformation, so we loop and attempt larger sizes.
352 NTSTATUS status;
353 ULONG returned_length;
354 UniqueMallocPtr buffer;
355 for (int tries = 0; tries < 5; ++tries) {
356 buffer.reset();
357 buffer = UncheckedAllocate(buffer_size);
358 if (!buffer) {
359 LOG(ERROR) << "UncheckedAllocate";
360 return std::vector<Handle>();
361 }
362
363 status = crashpad::NtQuerySystemInformation(
364 static_cast<SYSTEM_INFORMATION_CLASS>(SystemExtendedHandleInformation),
365 buffer.get(),
366 buffer_size,
367 &returned_length);
368 if (NT_SUCCESS(status) || status != STATUS_INFO_LENGTH_MISMATCH)
369 break;
370
371 buffer_size *= 2;
372 }
373
374 if (!NT_SUCCESS(status)) {
375 NTSTATUS_LOG(ERROR, status)
376 << "NtQuerySystemInformation SystemExtendedHandleInformation";
377 return std::vector<Handle>();
378 }
379
380 const auto& system_handle_information_ex =
381 *reinterpret_cast<process_types::SYSTEM_HANDLE_INFORMATION_EX*>(
382 buffer.get());
383
384 DCHECK_LE(offsetof(process_types::SYSTEM_HANDLE_INFORMATION_EX, Handles) +
385 system_handle_information_ex.NumberOfHandles *
386 sizeof(system_handle_information_ex.Handles[0]),
387 returned_length);
388
389 std::vector<Handle> handles;
390
391 for (size_t i = 0; i < system_handle_information_ex.NumberOfHandles; ++i) {
392 const auto& handle = system_handle_information_ex.Handles[i];
393 if (handle.UniqueProcessId != process_id_)
394 continue;
395
396 Handle result_handle;
397 result_handle.handle = HandleToInt(handle.HandleValue);
398 result_handle.attributes = handle.HandleAttributes;
399 result_handle.granted_access = handle.GrantedAccess;
400
401 // TODO(scottmg): Could special case for self.
402 HANDLE dup_handle;
403 if (DuplicateHandle(process,
404 handle.HandleValue,
405 GetCurrentProcess(),
406 &dup_handle,
407 0,
408 false,
409 DUPLICATE_SAME_ACCESS)) {
410 // Some handles cannot be duplicated, for example, handles of type
411 // EtwRegistration. If we fail to duplicate, then we can't gather any more
412 // information, but include the information that we do have already.
413 ScopedKernelHANDLE scoped_dup_handle(dup_handle);
414
415 std::unique_ptr<uint8_t[]> object_basic_information_buffer =
416 QueryObject(dup_handle,
417 ObjectBasicInformation,
418 sizeof(PUBLIC_OBJECT_BASIC_INFORMATION));
419 if (object_basic_information_buffer) {
420 PUBLIC_OBJECT_BASIC_INFORMATION* object_basic_information =
421 reinterpret_cast<PUBLIC_OBJECT_BASIC_INFORMATION*>(
422 object_basic_information_buffer.get());
423 // The Attributes and GrantedAccess sometimes differ slightly between
424 // the data retrieved in SYSTEM_HANDLE_INFORMATION_EX and
425 // PUBLIC_OBJECT_TYPE_INFORMATION. We prefer the values in
426 // SYSTEM_HANDLE_INFORMATION_EX because they were retrieved from the
427 // target process, rather than on the duplicated handle, so don't use
428 // them here.
429
430 // Subtract one to account for our DuplicateHandle() and another for
431 // NtQueryObject() while the query was being executed.
432 DCHECK_GT(object_basic_information->PointerCount, 2u);
433 result_handle.pointer_count =
434 object_basic_information->PointerCount - 2;
435
436 // Subtract one to account for our DuplicateHandle().
437 DCHECK_GT(object_basic_information->HandleCount, 1u);
438 result_handle.handle_count = object_basic_information->HandleCount - 1;
439 }
440
441 std::unique_ptr<uint8_t[]> object_type_information_buffer =
442 QueryObject(dup_handle,
443 ObjectTypeInformation,
444 sizeof(PUBLIC_OBJECT_TYPE_INFORMATION));
445 if (object_type_information_buffer) {
446 PUBLIC_OBJECT_TYPE_INFORMATION* object_type_information =
447 reinterpret_cast<PUBLIC_OBJECT_TYPE_INFORMATION*>(
448 object_type_information_buffer.get());
449
450 DCHECK_EQ(object_type_information->TypeName.Length %
451 sizeof(result_handle.type_name[0]),
452 0u);
453 result_handle.type_name =
454 std::wstring(object_type_information->TypeName.Buffer,
455 object_type_information->TypeName.Length /
456 sizeof(result_handle.type_name[0]));
457 }
458 }
459
460 handles.push_back(result_handle);
461 }
462 return handles;
463 }
464
Module()465 ProcessInfo::Module::Module() : name(), dll_base(0), size(0), timestamp() {
466 }
467
~Module()468 ProcessInfo::Module::~Module() {
469 }
470
Handle()471 ProcessInfo::Handle::Handle()
472 : type_name(),
473 handle(0),
474 attributes(0),
475 granted_access(0),
476 pointer_count(0),
477 handle_count(0) {
478 }
479
~Handle()480 ProcessInfo::Handle::~Handle() {
481 }
482
ProcessInfo()483 ProcessInfo::ProcessInfo()
484 : process_id_(),
485 inherited_from_process_id_(),
486 process_(),
487 command_line_(),
488 peb_address_(0),
489 peb_size_(0),
490 modules_(),
491 memory_info_(),
492 handles_(),
493 is_64_bit_(false),
494 is_wow64_(false),
495 initialized_() {
496 }
497
~ProcessInfo()498 ProcessInfo::~ProcessInfo() {
499 }
500
Initialize(HANDLE process)501 bool ProcessInfo::Initialize(HANDLE process) {
502 INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
503
504 process_ = process;
505
506 is_wow64_ = IsProcessWow64(process);
507
508 if (is_wow64_) {
509 // If it's WoW64, then it's 32-on-64.
510 is_64_bit_ = false;
511 } else {
512 // Otherwise, it's either 32 on 32, or 64 on 64. Use GetSystemInfo() to
513 // distinguish between these two cases.
514 SYSTEM_INFO system_info;
515 GetSystemInfo(&system_info);
516
517 #if defined(ARCH_CPU_X86_FAMILY)
518 constexpr uint16_t kNative64BitArchitecture = PROCESSOR_ARCHITECTURE_AMD64;
519 #elif defined(ARCH_CPU_ARM_FAMILY)
520 constexpr uint16_t kNative64BitArchitecture = PROCESSOR_ARCHITECTURE_ARM64;
521 #endif
522
523 is_64_bit_ = system_info.wProcessorArchitecture == kNative64BitArchitecture;
524 }
525
526 #if defined(ARCH_CPU_32_BITS)
527 if (is_64_bit_) {
528 LOG(ERROR) << "Reading x64 process from x86 process not supported";
529 return false;
530 }
531 #endif // ARCH_CPU_32_BITS
532
533 #if defined(ARCH_CPU_64_BITS)
534 bool result = GetProcessBasicInformation<process_types::internal::Traits64>(
535 process, is_wow64_, this, &peb_address_, &peb_size_);
536 #else
537 bool result = GetProcessBasicInformation<process_types::internal::Traits32>(
538 process, false, this, &peb_address_, &peb_size_);
539 #endif // ARCH_CPU_64_BITS
540
541 if (!result) {
542 LOG(ERROR) << "GetProcessBasicInformation failed";
543 return false;
544 }
545
546 result = is_64_bit_ ? ReadProcessData<process_types::internal::Traits64>(
547 process, peb_address_, this)
548 : ReadProcessData<process_types::internal::Traits32>(
549 process, peb_address_, this);
550 if (!result) {
551 LOG(ERROR) << "ReadProcessData failed";
552 return false;
553 }
554
555 if (!ReadMemoryInfo(process, is_64_bit_, this)) {
556 LOG(ERROR) << "ReadMemoryInfo failed";
557 return false;
558 }
559
560 INITIALIZATION_STATE_SET_VALID(initialized_);
561 return true;
562 }
563
Is64Bit() const564 bool ProcessInfo::Is64Bit() const {
565 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
566 return is_64_bit_;
567 }
568
IsWow64() const569 bool ProcessInfo::IsWow64() const {
570 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
571 return is_wow64_;
572 }
573
ProcessID() const574 crashpad::ProcessID ProcessInfo::ProcessID() const {
575 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
576 return process_id_;
577 }
578
ParentProcessID() const579 crashpad::ProcessID ProcessInfo::ParentProcessID() const {
580 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
581 return inherited_from_process_id_;
582 }
583
CommandLine(std::wstring * command_line) const584 bool ProcessInfo::CommandLine(std::wstring* command_line) const {
585 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
586 *command_line = command_line_;
587 return true;
588 }
589
Peb(WinVMAddress * peb_address,WinVMSize * peb_size) const590 void ProcessInfo::Peb(WinVMAddress* peb_address, WinVMSize* peb_size) const {
591 *peb_address = peb_address_;
592 *peb_size = peb_size_;
593 }
594
Modules(std::vector<Module> * modules) const595 bool ProcessInfo::Modules(std::vector<Module>* modules) const {
596 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
597 *modules = modules_;
598 return true;
599 }
600
MemoryInfo() const601 const ProcessInfo::MemoryBasicInformation64Vector& ProcessInfo::MemoryInfo()
602 const {
603 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
604 return memory_info_;
605 }
606
607 std::vector<CheckedRange<WinVMAddress, WinVMSize>>
GetReadableRanges(const CheckedRange<WinVMAddress,WinVMSize> & range) const608 ProcessInfo::GetReadableRanges(
609 const CheckedRange<WinVMAddress, WinVMSize>& range) const {
610 return GetReadableRangesOfMemoryMap(range, MemoryInfo());
611 }
612
LoggingRangeIsFullyReadable(const CheckedRange<WinVMAddress,WinVMSize> & range) const613 bool ProcessInfo::LoggingRangeIsFullyReadable(
614 const CheckedRange<WinVMAddress, WinVMSize>& range) const {
615 const auto ranges = GetReadableRanges(range);
616 if (ranges.empty()) {
617 LOG(ERROR) << base::StringPrintf(
618 "range at 0x%llx, size 0x%llx fully unreadable",
619 range.base(),
620 range.size());
621 return false;
622 }
623
624 if (ranges.size() != 1 ||
625 ranges[0].base() != range.base() || ranges[0].size() != range.size()) {
626 LOG(ERROR) << base::StringPrintf(
627 "range at 0x%llx, size 0x%llx partially unreadable",
628 range.base(),
629 range.size());
630 return false;
631 }
632
633 return true;
634 }
635
Handles() const636 const std::vector<ProcessInfo::Handle>& ProcessInfo::Handles() const {
637 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
638 if (handles_.empty())
639 handles_ = BuildHandleVector(process_);
640 return handles_;
641 }
642
GetReadableRangesOfMemoryMap(const CheckedRange<WinVMAddress,WinVMSize> & range,const ProcessInfo::MemoryBasicInformation64Vector & memory_info)643 std::vector<CheckedRange<WinVMAddress, WinVMSize>> GetReadableRangesOfMemoryMap(
644 const CheckedRange<WinVMAddress, WinVMSize>& range,
645 const ProcessInfo::MemoryBasicInformation64Vector& memory_info) {
646 using Range = CheckedRange<WinVMAddress, WinVMSize>;
647
648 // Constructing Ranges and using OverlapsRange() is very, very slow in Debug
649 // builds, so do a manual check in this loop. The ranges are still validated
650 // by a CheckedRange before being returned.
651 WinVMAddress range_base = range.base();
652 WinVMAddress range_end = range.end();
653
654 // Find all the ranges that overlap the target range, maintaining their order.
655 ProcessInfo::MemoryBasicInformation64Vector overlapping;
656 const size_t size = memory_info.size();
657
658 // This loop is written in an ugly fashion to make Debug performance
659 // reasonable.
660 const MEMORY_BASIC_INFORMATION64* begin = &memory_info[0];
661 for (size_t i = 0; i < size; ++i) {
662 const MEMORY_BASIC_INFORMATION64& mi = *(begin + i);
663 static_assert(std::is_same<decltype(mi.BaseAddress), WinVMAddress>::value,
664 "expected range address to be WinVMAddress");
665 static_assert(std::is_same<decltype(mi.RegionSize), WinVMSize>::value,
666 "expected range size to be WinVMSize");
667 WinVMAddress mi_end = mi.BaseAddress + mi.RegionSize;
668 if (range_base < mi_end && mi.BaseAddress < range_end)
669 overlapping.push_back(mi);
670 }
671 if (overlapping.empty())
672 return std::vector<Range>();
673
674 // For the first and last, trim to the boundary of the incoming range.
675 MEMORY_BASIC_INFORMATION64& front = overlapping.front();
676 WinVMAddress original_front_base_address = front.BaseAddress;
677 front.BaseAddress = std::max(front.BaseAddress, range.base());
678 front.RegionSize =
679 (original_front_base_address + front.RegionSize) - front.BaseAddress;
680
681 MEMORY_BASIC_INFORMATION64& back = overlapping.back();
682 WinVMAddress back_end = back.BaseAddress + back.RegionSize;
683 back.RegionSize = std::min(range.end(), back_end) - back.BaseAddress;
684
685 // Discard all non-accessible.
686 overlapping.erase(std::remove_if(overlapping.begin(),
687 overlapping.end(),
688 [](const MEMORY_BASIC_INFORMATION64& mbi) {
689 return !RegionIsAccessible(mbi);
690 }),
691 overlapping.end());
692 if (overlapping.empty())
693 return std::vector<Range>();
694
695 // Convert to return type.
696 std::vector<Range> as_ranges;
697 for (const auto& mi : overlapping) {
698 as_ranges.push_back(Range(mi.BaseAddress, mi.RegionSize));
699 DCHECK(as_ranges.back().IsValid());
700 }
701
702 // Coalesce remaining regions.
703 std::vector<Range> result;
704 result.push_back(as_ranges[0]);
705 for (size_t i = 1; i < as_ranges.size(); ++i) {
706 if (result.back().end() == as_ranges[i].base()) {
707 result.back().SetRange(result.back().base(),
708 result.back().size() + as_ranges[i].size());
709 } else {
710 result.push_back(as_ranges[i]);
711 }
712 DCHECK(result.back().IsValid());
713 }
714
715 return result;
716 }
717
718 } // namespace crashpad
719