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