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 "snapshot/win/process_reader_win.h"
16 
17 #include <string.h>
18 #include <winternl.h>
19 
20 #include <memory>
21 
22 #include "base/numerics/safe_conversions.h"
23 #include "base/strings/stringprintf.h"
24 #include "util/misc/capture_context.h"
25 #include "util/misc/time.h"
26 #include "util/win/nt_internals.h"
27 #include "util/win/ntstatus_logging.h"
28 #include "util/win/process_structs.h"
29 #include "util/win/scoped_handle.h"
30 
31 namespace crashpad {
32 
33 namespace {
34 
35 // Gets a pointer to the process information structure after a given one, or
36 // null when iteration is complete, assuming they've been retrieved in a block
37 // via NtQuerySystemInformation().
38 template <class Traits>
NextProcess(process_types::SYSTEM_PROCESS_INFORMATION<Traits> * process)39 process_types::SYSTEM_PROCESS_INFORMATION<Traits>* NextProcess(
40     process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process) {
41   ULONG offset = process->NextEntryOffset;
42   if (offset == 0)
43     return nullptr;
44   return reinterpret_cast<process_types::SYSTEM_PROCESS_INFORMATION<Traits>*>(
45       reinterpret_cast<uint8_t*>(process) + offset);
46 }
47 
48 //! \brief Retrieves the SYSTEM_PROCESS_INFORMATION for a given process.
49 //!
50 //! The returned pointer points into the memory block stored by \a buffer.
51 //! Ownership of \a buffer is transferred to the caller.
52 //!
53 //! \return Pointer to the process' data, or nullptr if it was not found or on
54 //!     error. On error, a message will be logged.
55 template <class Traits>
GetProcessInformation(HANDLE process_handle,std::unique_ptr<uint8_t[]> * buffer)56 process_types::SYSTEM_PROCESS_INFORMATION<Traits>* GetProcessInformation(
57     HANDLE process_handle,
58     std::unique_ptr<uint8_t[]>* buffer) {
59   ULONG buffer_size = 16384;
60   ULONG actual_size;
61   buffer->reset(new uint8_t[buffer_size]);
62   NTSTATUS status;
63   // This must be in retry loop, as we're racing with process creation on the
64   // system to find a buffer large enough to hold all process information.
65   for (int tries = 0; tries < 20; ++tries) {
66     status = crashpad::NtQuerySystemInformation(
67         SystemProcessInformation,
68         reinterpret_cast<void*>(buffer->get()),
69         buffer_size,
70         &actual_size);
71     if (status == STATUS_BUFFER_TOO_SMALL ||
72         status == STATUS_INFO_LENGTH_MISMATCH) {
73       DCHECK_GT(actual_size, buffer_size);
74 
75       // Add a little extra to try to avoid an additional loop iteration. We're
76       // racing with system-wide process creation between here and the next call
77       // to NtQuerySystemInformation().
78       buffer_size = actual_size + 4096;
79 
80       // Free the old buffer before attempting to allocate a new one.
81       buffer->reset();
82 
83       buffer->reset(new uint8_t[buffer_size]);
84     } else {
85       break;
86     }
87   }
88 
89   if (!NT_SUCCESS(status)) {
90     NTSTATUS_LOG(ERROR, status) << "NtQuerySystemInformation";
91     return nullptr;
92   }
93 
94   DCHECK_LE(actual_size, buffer_size);
95 
96   process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process =
97       reinterpret_cast<process_types::SYSTEM_PROCESS_INFORMATION<Traits>*>(
98           buffer->get());
99   DWORD process_id = GetProcessId(process_handle);
100   for (;;) {
101     if (process->UniqueProcessId == process_id)
102       return process;
103     process = NextProcess(process);
104     if (!process)
105       break;
106   }
107 
108   LOG(ERROR) << "process " << process_id << " not found";
109   return nullptr;
110 }
111 
112 template <class Traits>
OpenThread(const process_types::SYSTEM_THREAD_INFORMATION<Traits> & thread_info)113 HANDLE OpenThread(
114     const process_types::SYSTEM_THREAD_INFORMATION<Traits>& thread_info) {
115   HANDLE handle;
116   ACCESS_MASK query_access =
117       THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION;
118   OBJECT_ATTRIBUTES object_attributes;
119   InitializeObjectAttributes(&object_attributes, nullptr, 0, nullptr, nullptr);
120   NTSTATUS status = crashpad::NtOpenThread(
121       &handle, query_access, &object_attributes, &thread_info.ClientId);
122   if (!NT_SUCCESS(status)) {
123     NTSTATUS_LOG(ERROR, status) << "NtOpenThread";
124     return nullptr;
125   }
126   return handle;
127 }
128 
129 // It's necessary to suspend the thread to grab CONTEXT. SuspendThread has a
130 // side-effect of returning the SuspendCount of the thread on success, so we
131 // fill out these two pieces of semi-unrelated data in the same function.
132 template <class Traits>
FillThreadContextAndSuspendCount(HANDLE thread_handle,ProcessReaderWin::Thread * thread,ProcessSuspensionState suspension_state,bool is_64_reading_32)133 bool FillThreadContextAndSuspendCount(HANDLE thread_handle,
134                                       ProcessReaderWin::Thread* thread,
135                                       ProcessSuspensionState suspension_state,
136                                       bool is_64_reading_32) {
137   // Don't suspend the thread if it's this thread. This is really only for test
138   // binaries, as we won't be walking ourselves, in general.
139   bool is_current_thread = thread->id ==
140                            reinterpret_cast<process_types::TEB<Traits>*>(
141                                NtCurrentTeb())->ClientId.UniqueThread;
142 
143   if (is_current_thread) {
144     DCHECK(suspension_state == ProcessSuspensionState::kRunning);
145     thread->suspend_count = 0;
146     DCHECK(!is_64_reading_32);
147     CaptureContext(&thread->context.native);
148   } else {
149     DWORD previous_suspend_count = SuspendThread(thread_handle);
150     if (previous_suspend_count == static_cast<DWORD>(-1)) {
151       PLOG(ERROR) << "SuspendThread";
152       return false;
153     }
154     if (previous_suspend_count <= 0 &&
155         suspension_state == ProcessSuspensionState::kSuspended) {
156       LOG(WARNING) << "Thread " << thread->id
157                    << " should be suspended, but previous_suspend_count is "
158                    << previous_suspend_count;
159       thread->suspend_count = 0;
160     } else {
161       thread->suspend_count =
162           previous_suspend_count -
163           (suspension_state == ProcessSuspensionState::kSuspended ? 1 : 0);
164     }
165 
166     memset(&thread->context, 0, sizeof(thread->context));
167 #if defined(ARCH_CPU_32_BITS)
168     const bool is_native = true;
169 #elif defined(ARCH_CPU_64_BITS)
170     const bool is_native = !is_64_reading_32;
171     if (is_64_reading_32) {
172       thread->context.wow64.ContextFlags = CONTEXT_ALL;
173       if (!Wow64GetThreadContext(thread_handle, &thread->context.wow64)) {
174         PLOG(ERROR) << "Wow64GetThreadContext";
175         return false;
176       }
177     }
178 #endif
179     if (is_native) {
180       thread->context.native.ContextFlags = CONTEXT_ALL;
181       if (!GetThreadContext(thread_handle, &thread->context.native)) {
182         PLOG(ERROR) << "GetThreadContext";
183         return false;
184       }
185     }
186 
187     if (!ResumeThread(thread_handle)) {
188       PLOG(ERROR) << "ResumeThread";
189       return false;
190     }
191   }
192 
193   return true;
194 }
195 
196 }  // namespace
197 
Thread()198 ProcessReaderWin::Thread::Thread()
199     : context(),
200       id(0),
201       teb_address(0),
202       teb_size(0),
203       stack_region_address(0),
204       stack_region_size(0),
205       suspend_count(0),
206       priority_class(0),
207       priority(0) {
208 }
209 
ProcessReaderWin()210 ProcessReaderWin::ProcessReaderWin()
211     : process_(INVALID_HANDLE_VALUE),
212       process_info_(),
213       process_memory_(),
214       threads_(),
215       modules_(),
216       suspension_state_(),
217       initialized_threads_(false),
218       initialized_() {
219 }
220 
~ProcessReaderWin()221 ProcessReaderWin::~ProcessReaderWin() {
222 }
223 
Initialize(HANDLE process,ProcessSuspensionState suspension_state)224 bool ProcessReaderWin::Initialize(HANDLE process,
225                                   ProcessSuspensionState suspension_state) {
226   INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
227 
228   process_ = process;
229   suspension_state_ = suspension_state;
230   if (!process_info_.Initialize(process))
231     return false;
232   if (!process_memory_.Initialize(process))
233     return false;
234 
235   INITIALIZATION_STATE_SET_VALID(initialized_);
236   return true;
237 }
238 
StartTime(timeval * start_time) const239 bool ProcessReaderWin::StartTime(timeval* start_time) const {
240   FILETIME creation, exit, kernel, user;
241   if (!GetProcessTimes(process_, &creation, &exit, &kernel, &user)) {
242     PLOG(ERROR) << "GetProcessTimes";
243     return false;
244   }
245   *start_time = FiletimeToTimevalEpoch(creation);
246   return true;
247 }
248 
CPUTimes(timeval * user_time,timeval * system_time) const249 bool ProcessReaderWin::CPUTimes(timeval* user_time,
250                                 timeval* system_time) const {
251   FILETIME creation, exit, kernel, user;
252   if (!GetProcessTimes(process_, &creation, &exit, &kernel, &user)) {
253     PLOG(ERROR) << "GetProcessTimes";
254     return false;
255   }
256   *user_time = FiletimeToTimevalInterval(user);
257   *system_time = FiletimeToTimevalInterval(kernel);
258   return true;
259 }
260 
Threads()261 const std::vector<ProcessReaderWin::Thread>& ProcessReaderWin::Threads() {
262   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
263 
264   if (initialized_threads_)
265     return threads_;
266 
267   initialized_threads_ = true;
268 
269 #if defined(ARCH_CPU_64_BITS)
270   ReadThreadData<process_types::internal::Traits64>(process_info_.IsWow64());
271 #else
272   ReadThreadData<process_types::internal::Traits32>(false);
273 #endif
274 
275   return threads_;
276 }
277 
Modules()278 const std::vector<ProcessInfo::Module>& ProcessReaderWin::Modules() {
279   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
280 
281   if (!process_info_.Modules(&modules_)) {
282     LOG(ERROR) << "couldn't retrieve modules";
283   }
284 
285   return modules_;
286 }
287 
GetProcessInfo() const288 const ProcessInfo& ProcessReaderWin::GetProcessInfo() const {
289   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
290   return process_info_;
291 }
292 
DecrementThreadSuspendCounts(uint64_t except_thread_id)293 void ProcessReaderWin::DecrementThreadSuspendCounts(uint64_t except_thread_id) {
294   Threads();
295   for (auto& thread : threads_) {
296     if (thread.id != except_thread_id) {
297       DCHECK_GT(thread.suspend_count, 0u);
298       --thread.suspend_count;
299     }
300   }
301 }
302 
303 template <class Traits>
ReadThreadData(bool is_64_reading_32)304 void ProcessReaderWin::ReadThreadData(bool is_64_reading_32) {
305   DCHECK(threads_.empty());
306 
307   std::unique_ptr<uint8_t[]> buffer;
308   process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process_information =
309       GetProcessInformation<Traits>(process_, &buffer);
310   if (!process_information)
311     return;
312 
313   for (unsigned long i = 0; i < process_information->NumberOfThreads; ++i) {
314     const process_types::SYSTEM_THREAD_INFORMATION<Traits>& thread_info =
315         process_information->Threads[i];
316     ProcessReaderWin::Thread thread;
317     thread.id = thread_info.ClientId.UniqueThread;
318 
319     ScopedKernelHANDLE thread_handle(OpenThread(thread_info));
320     if (!thread_handle.is_valid())
321       continue;
322 
323     if (!FillThreadContextAndSuspendCount<Traits>(thread_handle.get(),
324                                                   &thread,
325                                                   suspension_state_,
326                                                   is_64_reading_32)) {
327       continue;
328     }
329 
330     // TODO(scottmg): I believe we could reverse engineer the PriorityClass from
331     // the Priority, BasePriority, and
332     // https://msdn.microsoft.com/library/ms685100.aspx. MinidumpThreadWriter
333     // doesn't handle it yet in any case, so investigate both of those at the
334     // same time if it's useful.
335     thread.priority_class = NORMAL_PRIORITY_CLASS;
336 
337     thread.priority = thread_info.Priority;
338 
339     process_types::THREAD_BASIC_INFORMATION<Traits> thread_basic_info;
340     NTSTATUS status = crashpad::NtQueryInformationThread(
341         thread_handle.get(),
342         static_cast<THREADINFOCLASS>(ThreadBasicInformation),
343         &thread_basic_info,
344         sizeof(thread_basic_info),
345         nullptr);
346     if (!NT_SUCCESS(status)) {
347       NTSTATUS_LOG(ERROR, status) << "NtQueryInformationThread";
348       continue;
349     }
350 
351     // Read the TIB (Thread Information Block) which is the first element of the
352     // TEB, for its stack fields.
353     process_types::NT_TIB<Traits> tib;
354     thread.teb_address = thread_basic_info.TebBaseAddress;
355     thread.teb_size = sizeof(process_types::TEB<Traits>);
356     if (process_memory_.Read(thread.teb_address, sizeof(tib), &tib)) {
357       WinVMAddress base = 0;
358       WinVMAddress limit = 0;
359       // If we're reading a WOW64 process, then the TIB we just retrieved is the
360       // x64 one. The first word of the x64 TIB points at the x86 TIB. See
361       // https://msdn.microsoft.com/library/dn424783.aspx.
362       if (is_64_reading_32) {
363         process_types::NT_TIB<process_types::internal::Traits32> tib32;
364         thread.teb_address = tib.Wow64Teb;
365         thread.teb_size =
366             sizeof(process_types::TEB<process_types::internal::Traits32>);
367         if (process_memory_.Read(thread.teb_address, sizeof(tib32), &tib32)) {
368           base = tib32.StackBase;
369           limit = tib32.StackLimit;
370         }
371       } else {
372         base = tib.StackBase;
373         limit = tib.StackLimit;
374       }
375 
376       // Note, "backwards" because of direction of stack growth.
377       thread.stack_region_address = limit;
378       if (limit > base) {
379         LOG(ERROR) << "invalid stack range: " << base << " - " << limit;
380         thread.stack_region_size = 0;
381       } else {
382         thread.stack_region_size = base - limit;
383       }
384     }
385     threads_.push_back(thread);
386   }
387 }
388 
389 }  // namespace crashpad
390