2014-08-25 17:51:09 -04:00
|
|
|
|
// Copyright 2014 The Crashpad Authors. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
|
//
|
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
//
|
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
2018-02-22 12:12:26 -08:00
|
|
|
|
#include "snapshot/mac/process_reader_mac.h"
|
2014-08-25 17:51:09 -04:00
|
|
|
|
|
|
|
|
|
#include <AvailabilityMacros.h>
|
|
|
|
|
#include <mach-o/loader.h>
|
2018-02-22 12:12:26 -08:00
|
|
|
|
#include <mach/mach_vm.h>
|
2014-08-25 17:51:09 -04:00
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
2017-10-19 00:26:38 -04:00
|
|
|
|
#include <utility>
|
2014-08-25 17:51:09 -04:00
|
|
|
|
|
|
|
|
|
#include "base/logging.h"
|
|
|
|
|
#include "base/mac/mach_logging.h"
|
|
|
|
|
#include "base/mac/scoped_mach_port.h"
|
|
|
|
|
#include "base/mac/scoped_mach_vm.h"
|
2014-09-22 13:08:57 -04:00
|
|
|
|
#include "base/strings/stringprintf.h"
|
2014-10-17 13:41:45 -04:00
|
|
|
|
#include "snapshot/mac/mach_o_image_reader.h"
|
|
|
|
|
#include "snapshot/mac/process_types.h"
|
2014-08-26 16:50:08 -04:00
|
|
|
|
#include "util/misc/scoped_forbid_return.h"
|
2014-08-25 17:51:09 -04:00
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
void MachTimeValueToTimeval(const time_value& mach, timeval* tv) {
|
|
|
|
|
tv->tv_sec = mach.seconds;
|
|
|
|
|
tv->tv_usec = mach.microseconds;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-18 13:53:43 -04:00
|
|
|
|
kern_return_t MachVMRegionRecurseDeepest(task_t task,
|
2014-08-25 17:51:09 -04:00
|
|
|
|
mach_vm_address_t* address,
|
|
|
|
|
mach_vm_size_t* size,
|
|
|
|
|
natural_t* depth,
|
|
|
|
|
vm_prot_t* protection,
|
|
|
|
|
unsigned int* user_tag) {
|
|
|
|
|
vm_region_submap_short_info_64 submap_info;
|
|
|
|
|
mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
|
|
|
|
|
while (true) {
|
|
|
|
|
kern_return_t kr = mach_vm_region_recurse(
|
|
|
|
|
task,
|
|
|
|
|
address,
|
|
|
|
|
size,
|
|
|
|
|
depth,
|
|
|
|
|
reinterpret_cast<vm_region_recurse_info_t>(&submap_info),
|
|
|
|
|
&count);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
return kr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!submap_info.is_submap) {
|
|
|
|
|
*protection = submap_info.protection;
|
|
|
|
|
*user_tag = submap_info.user_tag;
|
|
|
|
|
return KERN_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++*depth;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
namespace crashpad {
|
|
|
|
|
|
2018-02-22 12:12:26 -08:00
|
|
|
|
ProcessReaderMac::Thread::Thread()
|
2014-08-25 17:51:09 -04:00
|
|
|
|
: thread_context(),
|
|
|
|
|
float_context(),
|
|
|
|
|
debug_context(),
|
|
|
|
|
id(0),
|
|
|
|
|
stack_region_address(0),
|
|
|
|
|
stack_region_size(0),
|
|
|
|
|
thread_specific_data_address(0),
|
2014-10-13 12:59:21 -04:00
|
|
|
|
port(THREAD_NULL),
|
2014-08-25 17:51:09 -04:00
|
|
|
|
suspend_count(0),
|
2018-02-22 12:12:26 -08:00
|
|
|
|
priority(0) {}
|
2014-08-25 17:51:09 -04:00
|
|
|
|
|
2018-02-22 12:12:26 -08:00
|
|
|
|
ProcessReaderMac::Module::Module() : name(), reader(nullptr), timestamp(0) {}
|
2014-08-25 17:51:09 -04:00
|
|
|
|
|
2018-02-22 12:12:26 -08:00
|
|
|
|
ProcessReaderMac::Module::~Module() {}
|
2014-08-25 17:51:09 -04:00
|
|
|
|
|
2018-02-22 12:12:26 -08:00
|
|
|
|
ProcessReaderMac::ProcessReaderMac()
|
Move some parts of ProcessReader (in snapshot) to ProcessInfo (in util).
Also, move ProcessArgumentsForPID() into ProcessInfo.
This change prepares for a TaskForPID() implementation that’s capable of
operating correctly in a setuid root executable. TaskForPID() belongs in
util/mach, but for its permission checks, it must access some process
properties that were previously fetched by ProcessReader in snapshot.
util can’t depend on snapshot. The generic util-safe process information
bits (Is64Bit(), ProcessID(), ParentProcessID(), and StartTime()) are
moved from ProcessReader to ProcessInfo (in util), where the current
ProcessReader can use it (as it’s OK for snapshot to depend on util),
and the future TaskForPID() in util can also use it. ProcessInfo also
contains other methods that TaskForPID() will use, providing access to
the credentials that the target process holds. ProcessArgumentsForPID()
is related, and is also now a part of ProcessInfo.
TEST=snapshot_test, util_test
R=rsesek@chromium.org
Review URL: https://codereview.chromium.org/727973002
2014-11-14 17:54:42 -05:00
|
|
|
|
: process_info_(),
|
2014-08-25 17:51:09 -04:00
|
|
|
|
threads_(),
|
|
|
|
|
modules_(),
|
2014-09-22 13:08:57 -04:00
|
|
|
|
module_readers_(),
|
2018-12-20 13:12:55 -08:00
|
|
|
|
process_memory_(),
|
2014-10-13 12:59:21 -04:00
|
|
|
|
task_(TASK_NULL),
|
2014-08-25 17:51:09 -04:00
|
|
|
|
initialized_(),
|
|
|
|
|
is_64_bit_(false),
|
|
|
|
|
initialized_threads_(false),
|
2018-02-22 12:12:26 -08:00
|
|
|
|
initialized_modules_(false) {}
|
2014-08-25 17:51:09 -04:00
|
|
|
|
|
2018-02-22 12:12:26 -08:00
|
|
|
|
ProcessReaderMac::~ProcessReaderMac() {
|
2014-09-05 16:35:16 -04:00
|
|
|
|
for (const Thread& thread : threads_) {
|
2014-08-25 17:51:09 -04:00
|
|
|
|
kern_return_t kr = mach_port_deallocate(mach_task_self(), thread.port);
|
|
|
|
|
MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_port_deallocate";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-22 12:12:26 -08:00
|
|
|
|
bool ProcessReaderMac::Initialize(task_t task) {
|
2014-08-25 17:51:09 -04:00
|
|
|
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
|
|
|
|
|
2017-09-22 09:14:52 -07:00
|
|
|
|
if (!process_info_.InitializeWithTask(task)) {
|
2014-08-25 17:51:09 -04:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-20 13:12:55 -08:00
|
|
|
|
if (!process_memory_.Initialize(task)) {
|
2018-12-19 13:49:55 -08:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-08-25 17:51:09 -04:00
|
|
|
|
|
2018-12-19 13:49:55 -08:00
|
|
|
|
is_64_bit_ = process_info_.Is64Bit();
|
2014-08-25 17:51:09 -04:00
|
|
|
|
task_ = task;
|
|
|
|
|
|
|
|
|
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-22 12:12:26 -08:00
|
|
|
|
void ProcessReaderMac::StartTime(timeval* start_time) const {
|
2017-03-15 00:09:28 -04:00
|
|
|
|
bool rv = process_info_.StartTime(start_time);
|
|
|
|
|
DCHECK(rv);
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-22 12:12:26 -08:00
|
|
|
|
bool ProcessReaderMac::CPUTimes(timeval* user_time,
|
|
|
|
|
timeval* system_time) const {
|
2014-08-25 17:51:09 -04:00
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
|
|
|
|
|
// Calculate user and system time the same way the kernel does for
|
|
|
|
|
// getrusage(). See 10.9.2 xnu-2422.90.20/bsd/kern/kern_resource.c calcru().
|
|
|
|
|
timerclear(user_time);
|
|
|
|
|
timerclear(system_time);
|
|
|
|
|
|
|
|
|
|
// As of the 10.8 SDK, the preferred routine is MACH_TASK_BASIC_INFO.
|
2014-10-13 18:09:53 -04:00
|
|
|
|
// TASK_BASIC_INFO_64 is equivalent and works on earlier systems.
|
2014-08-25 17:51:09 -04:00
|
|
|
|
task_basic_info_64 task_basic_info;
|
|
|
|
|
mach_msg_type_number_t task_basic_info_count = TASK_BASIC_INFO_64_COUNT;
|
|
|
|
|
kern_return_t kr = task_info(task_,
|
|
|
|
|
TASK_BASIC_INFO_64,
|
|
|
|
|
reinterpret_cast<task_info_t>(&task_basic_info),
|
|
|
|
|
&task_basic_info_count);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
MACH_LOG(WARNING, kr) << "task_info TASK_BASIC_INFO_64";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
task_thread_times_info_data_t task_thread_times;
|
|
|
|
|
mach_msg_type_number_t task_thread_times_count = TASK_THREAD_TIMES_INFO_COUNT;
|
|
|
|
|
kr = task_info(task_,
|
|
|
|
|
TASK_THREAD_TIMES_INFO,
|
|
|
|
|
reinterpret_cast<task_info_t>(&task_thread_times),
|
|
|
|
|
&task_thread_times_count);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
MACH_LOG(WARNING, kr) << "task_info TASK_THREAD_TIMES";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MachTimeValueToTimeval(task_basic_info.user_time, user_time);
|
|
|
|
|
MachTimeValueToTimeval(task_basic_info.system_time, system_time);
|
|
|
|
|
|
|
|
|
|
timeval thread_user_time;
|
|
|
|
|
MachTimeValueToTimeval(task_thread_times.user_time, &thread_user_time);
|
|
|
|
|
timeval thread_system_time;
|
|
|
|
|
MachTimeValueToTimeval(task_thread_times.system_time, &thread_system_time);
|
|
|
|
|
|
|
|
|
|
timeradd(user_time, &thread_user_time, user_time);
|
|
|
|
|
timeradd(system_time, &thread_system_time, system_time);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-22 12:12:26 -08:00
|
|
|
|
const std::vector<ProcessReaderMac::Thread>& ProcessReaderMac::Threads() {
|
2014-08-25 17:51:09 -04:00
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
|
|
|
|
|
if (!initialized_threads_) {
|
|
|
|
|
InitializeThreads();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return threads_;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-22 12:12:26 -08:00
|
|
|
|
const std::vector<ProcessReaderMac::Module>& ProcessReaderMac::Modules() {
|
2014-08-25 17:51:09 -04:00
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
|
|
|
|
|
if (!initialized_modules_) {
|
|
|
|
|
InitializeModules();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return modules_;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-22 12:12:26 -08:00
|
|
|
|
mach_vm_address_t ProcessReaderMac::DyldAllImageInfo(
|
mac: Handle _dyld_get_all_image_infos() not being available on 10.13
_dyld_get_all_image_infos() was only used in test code in Crashpad.
This addresses two related problems.
When running on 10.13 or later, _dyld_get_all_image_infos() is not
available. It appears to still be implemented in dyld, but its symbol is
now private. This was always known to be an “internal” interface. When
it’s not available, fall back to obtaining the address of the process’
dyld_all_image_infos structure by calling task_info(…, TASK_DYLD_INFO,
…). Note that this is the same thing that the code being tested does,
although the tests are not rendered entirely pointless because the code
being tested consumes dyld_all_image_infos through its own
implementation of an out-of-process reader interface, while the
dyld_all_image_infos data obtained by _dyld_get_all_image_infos() is
handled strictly in-process by ordinary memory reads. This is covered by
bug 187.
When building with the 10.13 SDK, no _dyld_get_all_image_infos symbol is
available to link against. In this case, access the symbol strictly at
runtime via dlopen() if it may be available, or when expecting to only
run on 10.13 and later, don’t even bother looking for this symbol. This
is covered by part of bug 188.
Bug: crashpad:185, crashpad:187, crashpad:188
Change-Id: Ib283e070faf5d1ec35deee420213b53ec24fb1d3
Reviewed-on: https://chromium-review.googlesource.com/534633
Reviewed-by: Robert Sesek <rsesek@chromium.org>
2017-06-14 10:48:30 -04:00
|
|
|
|
mach_vm_size_t* all_image_info_size) {
|
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
|
|
|
|
|
task_dyld_info_data_t dyld_info;
|
|
|
|
|
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
|
|
|
|
|
kern_return_t kr = task_info(
|
|
|
|
|
task_, TASK_DYLD_INFO, reinterpret_cast<task_info_t>(&dyld_info), &count);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
MACH_LOG(WARNING, kr) << "task_info";
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-22 12:12:26 -08:00
|
|
|
|
// TODO(mark): Deal with statically linked executables which don’t use dyld.
|
|
|
|
|
// This may look for the module that matches the executable path in the same
|
|
|
|
|
// data set that vmmap uses.
|
mac: Handle _dyld_get_all_image_infos() not being available on 10.13
_dyld_get_all_image_infos() was only used in test code in Crashpad.
This addresses two related problems.
When running on 10.13 or later, _dyld_get_all_image_infos() is not
available. It appears to still be implemented in dyld, but its symbol is
now private. This was always known to be an “internal” interface. When
it’s not available, fall back to obtaining the address of the process’
dyld_all_image_infos structure by calling task_info(…, TASK_DYLD_INFO,
…). Note that this is the same thing that the code being tested does,
although the tests are not rendered entirely pointless because the code
being tested consumes dyld_all_image_infos through its own
implementation of an out-of-process reader interface, while the
dyld_all_image_infos data obtained by _dyld_get_all_image_infos() is
handled strictly in-process by ordinary memory reads. This is covered by
bug 187.
When building with the 10.13 SDK, no _dyld_get_all_image_infos symbol is
available to link against. In this case, access the symbol strictly at
runtime via dlopen() if it may be available, or when expecting to only
run on 10.13 and later, don’t even bother looking for this symbol. This
is covered by part of bug 188.
Bug: crashpad:185, crashpad:187, crashpad:188
Change-Id: Ib283e070faf5d1ec35deee420213b53ec24fb1d3
Reviewed-on: https://chromium-review.googlesource.com/534633
Reviewed-by: Robert Sesek <rsesek@chromium.org>
2017-06-14 10:48:30 -04:00
|
|
|
|
|
|
|
|
|
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
|
|
|
|
|
// The task_dyld_info_data_t struct grew in 10.7, adding the format field.
|
|
|
|
|
// Don’t check this field if it’s not present, which can happen when either
|
|
|
|
|
// the SDK used at compile time or the kernel at run time are too old and
|
|
|
|
|
// don’t know about it.
|
|
|
|
|
if (count >= TASK_DYLD_INFO_COUNT) {
|
|
|
|
|
const integer_t kExpectedFormat =
|
|
|
|
|
!Is64Bit() ? TASK_DYLD_ALL_IMAGE_INFO_32 : TASK_DYLD_ALL_IMAGE_INFO_64;
|
|
|
|
|
if (dyld_info.all_image_info_format != kExpectedFormat) {
|
|
|
|
|
LOG(WARNING) << "unexpected task_dyld_info_data_t::all_image_info_format "
|
|
|
|
|
<< dyld_info.all_image_info_format;
|
|
|
|
|
DCHECK_EQ(dyld_info.all_image_info_format, kExpectedFormat);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (all_image_info_size) {
|
|
|
|
|
*all_image_info_size = dyld_info.all_image_info_size;
|
|
|
|
|
}
|
|
|
|
|
return dyld_info.all_image_info_addr;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-22 12:12:26 -08:00
|
|
|
|
void ProcessReaderMac::InitializeThreads() {
|
2014-08-25 17:51:09 -04:00
|
|
|
|
DCHECK(!initialized_threads_);
|
|
|
|
|
DCHECK(threads_.empty());
|
|
|
|
|
|
|
|
|
|
initialized_threads_ = true;
|
|
|
|
|
|
|
|
|
|
thread_act_array_t threads;
|
|
|
|
|
mach_msg_type_number_t thread_count = 0;
|
|
|
|
|
kern_return_t kr = task_threads(task_, &threads, &thread_count);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
MACH_LOG(WARNING, kr) << "task_threads";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The send rights in the |threads| array won’t have their send rights managed
|
|
|
|
|
// by anything until they’re added to |threads_| by the loop below. Any early
|
|
|
|
|
// return (or exception) that happens between here and the completion of the
|
|
|
|
|
// loop below will leak thread port send rights.
|
2014-08-26 16:50:08 -04:00
|
|
|
|
ScopedForbidReturn threads_need_owners;
|
2014-08-25 17:51:09 -04:00
|
|
|
|
|
|
|
|
|
base::mac::ScopedMachVM threads_vm(
|
|
|
|
|
reinterpret_cast<vm_address_t>(threads),
|
|
|
|
|
mach_vm_round_page(thread_count * sizeof(*threads)));
|
|
|
|
|
|
|
|
|
|
for (size_t index = 0; index < thread_count; ++index) {
|
2014-09-05 16:35:16 -04:00
|
|
|
|
Thread thread;
|
2014-08-25 17:51:09 -04:00
|
|
|
|
thread.port = threads[index];
|
|
|
|
|
|
|
|
|
|
#if defined(ARCH_CPU_X86_FAMILY)
|
|
|
|
|
const thread_state_flavor_t kThreadStateFlavor =
|
|
|
|
|
Is64Bit() ? x86_THREAD_STATE64 : x86_THREAD_STATE32;
|
|
|
|
|
mach_msg_type_number_t thread_state_count =
|
|
|
|
|
Is64Bit() ? x86_THREAD_STATE64_COUNT : x86_THREAD_STATE32_COUNT;
|
|
|
|
|
|
2014-09-16 11:55:55 -04:00
|
|
|
|
// TODO(mark): Use the AVX variants instead of the FLOAT variants?
|
2014-08-25 17:51:09 -04:00
|
|
|
|
const thread_state_flavor_t kFloatStateFlavor =
|
|
|
|
|
Is64Bit() ? x86_FLOAT_STATE64 : x86_FLOAT_STATE32;
|
|
|
|
|
mach_msg_type_number_t float_state_count =
|
|
|
|
|
Is64Bit() ? x86_FLOAT_STATE64_COUNT : x86_FLOAT_STATE32_COUNT;
|
|
|
|
|
|
|
|
|
|
const thread_state_flavor_t kDebugStateFlavor =
|
|
|
|
|
Is64Bit() ? x86_DEBUG_STATE64 : x86_DEBUG_STATE32;
|
|
|
|
|
mach_msg_type_number_t debug_state_count =
|
|
|
|
|
Is64Bit() ? x86_DEBUG_STATE64_COUNT : x86_DEBUG_STATE32_COUNT;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
kr = thread_get_state(
|
|
|
|
|
thread.port,
|
|
|
|
|
kThreadStateFlavor,
|
|
|
|
|
reinterpret_cast<thread_state_t>(&thread.thread_context),
|
|
|
|
|
&thread_state_count);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
MACH_LOG(ERROR, kr) << "thread_get_state(" << kThreadStateFlavor << ")";
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
kr = thread_get_state(
|
|
|
|
|
thread.port,
|
|
|
|
|
kFloatStateFlavor,
|
|
|
|
|
reinterpret_cast<thread_state_t>(&thread.float_context),
|
|
|
|
|
&float_state_count);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
MACH_LOG(ERROR, kr) << "thread_get_state(" << kFloatStateFlavor << ")";
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
kr = thread_get_state(
|
|
|
|
|
thread.port,
|
|
|
|
|
kDebugStateFlavor,
|
|
|
|
|
reinterpret_cast<thread_state_t>(&thread.debug_context),
|
|
|
|
|
&debug_state_count);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
MACH_LOG(ERROR, kr) << "thread_get_state(" << kDebugStateFlavor << ")";
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
thread_basic_info basic_info;
|
|
|
|
|
mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
|
|
|
|
|
kr = thread_info(thread.port,
|
|
|
|
|
THREAD_BASIC_INFO,
|
|
|
|
|
reinterpret_cast<thread_info_t>(&basic_info),
|
|
|
|
|
&count);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
MACH_LOG(WARNING, kr) << "thread_info(THREAD_BASIC_INFO)";
|
|
|
|
|
} else {
|
|
|
|
|
thread.suspend_count = basic_info.suspend_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
thread_identifier_info identifier_info;
|
|
|
|
|
count = THREAD_IDENTIFIER_INFO_COUNT;
|
|
|
|
|
kr = thread_info(thread.port,
|
|
|
|
|
THREAD_IDENTIFIER_INFO,
|
|
|
|
|
reinterpret_cast<thread_info_t>(&identifier_info),
|
|
|
|
|
&count);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
MACH_LOG(WARNING, kr) << "thread_info(THREAD_IDENTIFIER_INFO)";
|
|
|
|
|
} else {
|
|
|
|
|
thread.id = identifier_info.thread_id;
|
|
|
|
|
|
|
|
|
|
// thread_identifier_info::thread_handle contains the base of the
|
|
|
|
|
// thread-specific data area, which on x86 and x86_64 is the thread’s base
|
|
|
|
|
// address of the %gs segment. 10.9.2 xnu-2422.90.20/osfmk/kern/thread.c
|
|
|
|
|
// thread_info_internal() gets the value from
|
|
|
|
|
// machine_thread::cthread_self, which is the same value used to set the
|
|
|
|
|
// %gs base in xnu-2422.90.20/osfmk/i386/pcb_native.c
|
|
|
|
|
// act_machine_switch_pcb().
|
|
|
|
|
//
|
|
|
|
|
// This address is the internal pthread’s _pthread::tsd[], an array of
|
|
|
|
|
// void* values that can be indexed by pthread_key_t values.
|
|
|
|
|
thread.thread_specific_data_address = identifier_info.thread_handle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
thread_precedence_policy precedence;
|
|
|
|
|
count = THREAD_PRECEDENCE_POLICY_COUNT;
|
|
|
|
|
boolean_t get_default = FALSE;
|
|
|
|
|
kr = thread_policy_get(thread.port,
|
|
|
|
|
THREAD_PRECEDENCE_POLICY,
|
|
|
|
|
reinterpret_cast<thread_policy_t>(&precedence),
|
|
|
|
|
&count,
|
|
|
|
|
&get_default);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
MACH_LOG(INFO, kr) << "thread_policy_get";
|
|
|
|
|
} else {
|
|
|
|
|
thread.priority = precedence.importance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined(ARCH_CPU_X86_FAMILY)
|
|
|
|
|
mach_vm_address_t stack_pointer = Is64Bit()
|
|
|
|
|
? thread.thread_context.t64.__rsp
|
|
|
|
|
: thread.thread_context.t32.__esp;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
thread.stack_region_address =
|
|
|
|
|
CalculateStackRegion(stack_pointer, &thread.stack_region_size);
|
|
|
|
|
|
|
|
|
|
threads_.push_back(thread);
|
|
|
|
|
}
|
2014-08-26 16:50:08 -04:00
|
|
|
|
|
|
|
|
|
threads_need_owners.Disarm();
|
2014-08-25 17:51:09 -04:00
|
|
|
|
}
|
|
|
|
|
|
2018-02-22 12:12:26 -08:00
|
|
|
|
void ProcessReaderMac::InitializeModules() {
|
2014-08-25 17:51:09 -04:00
|
|
|
|
DCHECK(!initialized_modules_);
|
|
|
|
|
DCHECK(modules_.empty());
|
|
|
|
|
|
|
|
|
|
initialized_modules_ = true;
|
|
|
|
|
|
mac: Handle _dyld_get_all_image_infos() not being available on 10.13
_dyld_get_all_image_infos() was only used in test code in Crashpad.
This addresses two related problems.
When running on 10.13 or later, _dyld_get_all_image_infos() is not
available. It appears to still be implemented in dyld, but its symbol is
now private. This was always known to be an “internal” interface. When
it’s not available, fall back to obtaining the address of the process’
dyld_all_image_infos structure by calling task_info(…, TASK_DYLD_INFO,
…). Note that this is the same thing that the code being tested does,
although the tests are not rendered entirely pointless because the code
being tested consumes dyld_all_image_infos through its own
implementation of an out-of-process reader interface, while the
dyld_all_image_infos data obtained by _dyld_get_all_image_infos() is
handled strictly in-process by ordinary memory reads. This is covered by
bug 187.
When building with the 10.13 SDK, no _dyld_get_all_image_infos symbol is
available to link against. In this case, access the symbol strictly at
runtime via dlopen() if it may be available, or when expecting to only
run on 10.13 and later, don’t even bother looking for this symbol. This
is covered by part of bug 188.
Bug: crashpad:185, crashpad:187, crashpad:188
Change-Id: Ib283e070faf5d1ec35deee420213b53ec24fb1d3
Reviewed-on: https://chromium-review.googlesource.com/534633
Reviewed-by: Robert Sesek <rsesek@chromium.org>
2017-06-14 10:48:30 -04:00
|
|
|
|
mach_vm_size_t all_image_info_size;
|
|
|
|
|
mach_vm_address_t all_image_info_addr =
|
|
|
|
|
DyldAllImageInfo(&all_image_info_size);
|
2014-09-05 13:43:51 -04:00
|
|
|
|
|
|
|
|
|
process_types::dyld_all_image_infos all_image_infos;
|
mac: Handle _dyld_get_all_image_infos() not being available on 10.13
_dyld_get_all_image_infos() was only used in test code in Crashpad.
This addresses two related problems.
When running on 10.13 or later, _dyld_get_all_image_infos() is not
available. It appears to still be implemented in dyld, but its symbol is
now private. This was always known to be an “internal” interface. When
it’s not available, fall back to obtaining the address of the process’
dyld_all_image_infos structure by calling task_info(…, TASK_DYLD_INFO,
…). Note that this is the same thing that the code being tested does,
although the tests are not rendered entirely pointless because the code
being tested consumes dyld_all_image_infos through its own
implementation of an out-of-process reader interface, while the
dyld_all_image_infos data obtained by _dyld_get_all_image_infos() is
handled strictly in-process by ordinary memory reads. This is covered by
bug 187.
When building with the 10.13 SDK, no _dyld_get_all_image_infos symbol is
available to link against. In this case, access the symbol strictly at
runtime via dlopen() if it may be available, or when expecting to only
run on 10.13 and later, don’t even bother looking for this symbol. This
is covered by part of bug 188.
Bug: crashpad:185, crashpad:187, crashpad:188
Change-Id: Ib283e070faf5d1ec35deee420213b53ec24fb1d3
Reviewed-on: https://chromium-review.googlesource.com/534633
Reviewed-by: Robert Sesek <rsesek@chromium.org>
2017-06-14 10:48:30 -04:00
|
|
|
|
if (!all_image_infos.Read(this, all_image_info_addr)) {
|
2014-09-05 13:43:51 -04:00
|
|
|
|
LOG(WARNING) << "could not read dyld_all_image_infos";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-13 12:55:41 -04:00
|
|
|
|
if (all_image_infos.version < 1) {
|
|
|
|
|
LOG(WARNING) << "unexpected dyld_all_image_infos version "
|
|
|
|
|
<< all_image_infos.version;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t expected_size =
|
|
|
|
|
process_types::dyld_all_image_infos::ExpectedSizeForVersion(
|
|
|
|
|
this, all_image_infos.version);
|
mac: Handle _dyld_get_all_image_infos() not being available on 10.13
_dyld_get_all_image_infos() was only used in test code in Crashpad.
This addresses two related problems.
When running on 10.13 or later, _dyld_get_all_image_infos() is not
available. It appears to still be implemented in dyld, but its symbol is
now private. This was always known to be an “internal” interface. When
it’s not available, fall back to obtaining the address of the process’
dyld_all_image_infos structure by calling task_info(…, TASK_DYLD_INFO,
…). Note that this is the same thing that the code being tested does,
although the tests are not rendered entirely pointless because the code
being tested consumes dyld_all_image_infos through its own
implementation of an out-of-process reader interface, while the
dyld_all_image_infos data obtained by _dyld_get_all_image_infos() is
handled strictly in-process by ordinary memory reads. This is covered by
bug 187.
When building with the 10.13 SDK, no _dyld_get_all_image_infos symbol is
available to link against. In this case, access the symbol strictly at
runtime via dlopen() if it may be available, or when expecting to only
run on 10.13 and later, don’t even bother looking for this symbol. This
is covered by part of bug 188.
Bug: crashpad:185, crashpad:187, crashpad:188
Change-Id: Ib283e070faf5d1ec35deee420213b53ec24fb1d3
Reviewed-on: https://chromium-review.googlesource.com/534633
Reviewed-by: Robert Sesek <rsesek@chromium.org>
2017-06-14 10:48:30 -04:00
|
|
|
|
if (all_image_info_size < expected_size) {
|
|
|
|
|
LOG(WARNING) << "small dyld_all_image_infos size " << all_image_info_size
|
|
|
|
|
<< " < " << expected_size << " for version "
|
|
|
|
|
<< all_image_infos.version;
|
2015-08-13 12:55:41 -04:00
|
|
|
|
return;
|
|
|
|
|
}
|
2014-09-22 13:08:57 -04:00
|
|
|
|
|
2014-09-05 13:43:51 -04:00
|
|
|
|
// Note that all_image_infos.infoArrayCount may be 0 if a crash occurred while
|
|
|
|
|
// dyld was loading the executable. This can happen if a required dynamic
|
2014-10-14 11:10:45 -04:00
|
|
|
|
// library was not found. Similarly, all_image_infos.infoArray may be nullptr
|
|
|
|
|
// if a crash occurred while dyld was updating it.
|
2014-09-22 13:08:57 -04:00
|
|
|
|
//
|
|
|
|
|
// TODO(mark): It may be possible to recover from these situations by looking
|
|
|
|
|
// through memory mappings for Mach-O images.
|
2014-10-17 13:47:02 -04:00
|
|
|
|
//
|
|
|
|
|
// Continue along when this situation is detected, because even without any
|
|
|
|
|
// images in infoArray, dyldImageLoadAddress may be set, and it may be
|
|
|
|
|
// possible to recover some information from dyld.
|
2014-09-22 13:08:57 -04:00
|
|
|
|
if (all_image_infos.infoArrayCount == 0) {
|
|
|
|
|
LOG(WARNING) << "all_image_infos.infoArrayCount is zero";
|
2014-10-17 13:47:02 -04:00
|
|
|
|
} else if (!all_image_infos.infoArray) {
|
2014-10-14 11:10:45 -04:00
|
|
|
|
LOG(WARNING) << "all_image_infos.infoArray is nullptr";
|
2014-09-22 13:08:57 -04:00
|
|
|
|
}
|
2014-09-05 13:43:51 -04:00
|
|
|
|
|
|
|
|
|
std::vector<process_types::dyld_image_info> image_info_vector(
|
|
|
|
|
all_image_infos.infoArrayCount);
|
|
|
|
|
if (!process_types::dyld_image_info::ReadArrayInto(this,
|
|
|
|
|
all_image_infos.infoArray,
|
|
|
|
|
image_info_vector.size(),
|
|
|
|
|
&image_info_vector[0])) {
|
|
|
|
|
LOG(WARNING) << "could not read dyld_image_info array";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-22 13:08:57 -04:00
|
|
|
|
size_t main_executable_count = 0;
|
2014-09-05 13:43:51 -04:00
|
|
|
|
bool found_dyld = false;
|
2014-09-22 13:08:57 -04:00
|
|
|
|
modules_.reserve(image_info_vector.size());
|
2014-09-05 13:43:51 -04:00
|
|
|
|
for (const process_types::dyld_image_info& image_info : image_info_vector) {
|
2014-09-05 16:35:16 -04:00
|
|
|
|
Module module;
|
2014-09-05 13:43:51 -04:00
|
|
|
|
module.timestamp = image_info.imageFileModDate;
|
2014-09-22 13:08:57 -04:00
|
|
|
|
|
2018-12-20 13:12:55 -08:00
|
|
|
|
if (!process_memory_.ReadCString(image_info.imageFilePath, &module.name)) {
|
2014-09-05 13:43:51 -04:00
|
|
|
|
LOG(WARNING) << "could not read dyld_image_info::imageFilePath";
|
|
|
|
|
// Proceed anyway with an empty module name.
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-25 12:13:07 -07:00
|
|
|
|
std::unique_ptr<MachOImageReader> reader(new MachOImageReader());
|
2014-09-22 13:08:57 -04:00
|
|
|
|
if (!reader->Initialize(this, image_info.imageLoadAddress, module.name)) {
|
|
|
|
|
reader.reset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module.reader = reader.get();
|
|
|
|
|
|
|
|
|
|
uint32_t file_type = reader ? reader->FileType() : 0;
|
|
|
|
|
|
2017-10-19 00:26:38 -04:00
|
|
|
|
module_readers_.push_back(std::move(reader));
|
2014-09-05 13:43:51 -04:00
|
|
|
|
modules_.push_back(module);
|
|
|
|
|
|
|
|
|
|
if (all_image_infos.version >= 2 && all_image_infos.dyldImageLoadAddress &&
|
|
|
|
|
image_info.imageLoadAddress == all_image_infos.dyldImageLoadAddress) {
|
|
|
|
|
found_dyld = true;
|
2016-06-17 22:03:01 -04:00
|
|
|
|
LOG(WARNING) << base::StringPrintf(
|
2018-02-22 12:12:26 -08:00
|
|
|
|
"found dylinker (%s) in dyld_all_image_infos::infoArray",
|
|
|
|
|
module.name.c_str());
|
2014-09-22 13:08:57 -04:00
|
|
|
|
|
|
|
|
|
LOG_IF(WARNING, file_type != MH_DYLINKER)
|
|
|
|
|
<< base::StringPrintf("dylinker (%s) has unexpected Mach-O type %d",
|
|
|
|
|
module.name.c_str(),
|
|
|
|
|
file_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (file_type == MH_EXECUTE) {
|
|
|
|
|
// On Mac OS X 10.6, the main executable does not normally show up at
|
|
|
|
|
// index 0. This is because of how 10.6.8 dyld-132.13/src/dyld.cpp
|
|
|
|
|
// notifyGDB(), the function resposible for causing
|
|
|
|
|
// dyld_all_image_infos::infoArray to be updated, is called. It is
|
|
|
|
|
// registered to be called when all dependents of an image have been
|
|
|
|
|
// mapped (dyld_image_state_dependents_mapped), meaning that the main
|
|
|
|
|
// executable won’t be added to the list until all of the libraries it
|
|
|
|
|
// depends on are, even though dyld begins looking at the main executable
|
|
|
|
|
// first. This changed in later versions of dyld, including those present
|
|
|
|
|
// in 10.7. 10.9.4 dyld-239.4/src/dyld.cpp updateAllImages() (renamed from
|
|
|
|
|
// notifyGDB()) is registered to be called when an image itself has been
|
|
|
|
|
// mapped (dyld_image_state_mapped), regardless of the libraries that it
|
|
|
|
|
// depends on.
|
|
|
|
|
//
|
|
|
|
|
// The interface requires that the main executable be first in the list,
|
|
|
|
|
// so swap it into the right position.
|
|
|
|
|
size_t index = modules_.size() - 1;
|
|
|
|
|
if (main_executable_count == 0) {
|
|
|
|
|
std::swap(modules_[0], modules_[index]);
|
|
|
|
|
} else {
|
|
|
|
|
LOG(WARNING) << base::StringPrintf(
|
|
|
|
|
"multiple MH_EXECUTE modules (%s, %s)",
|
|
|
|
|
modules_[0].name.c_str(),
|
|
|
|
|
modules_[index].name.c_str());
|
|
|
|
|
}
|
|
|
|
|
++main_executable_count;
|
2014-09-05 13:43:51 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-22 13:08:57 -04:00
|
|
|
|
LOG_IF(WARNING, main_executable_count == 0) << "no MH_EXECUTE modules";
|
|
|
|
|
|
2014-09-05 13:43:51 -04:00
|
|
|
|
// all_image_infos.infoArray doesn’t include an entry for dyld, but dyld is
|
|
|
|
|
// loaded into the process’ address space as a module. Its load address is
|
|
|
|
|
// easily known given a sufficiently recent all_image_infos.version, but the
|
|
|
|
|
// timestamp and pathname are not given as they are for other modules.
|
|
|
|
|
//
|
|
|
|
|
// The timestamp is a lost cause, because the kernel doesn’t record the
|
|
|
|
|
// timestamp of the dynamic linker at the time it’s loaded in the same way
|
|
|
|
|
// that dyld records the timestamps of other modules when they’re loaded. (The
|
|
|
|
|
// timestamp for the main executable is also not reported and appears as 0
|
|
|
|
|
// even when accessed via dyld APIs, because it’s loaded by the kernel, not by
|
|
|
|
|
// dyld.)
|
|
|
|
|
//
|
|
|
|
|
// The name can be determined, but it’s not as simple as hardcoding the
|
|
|
|
|
// default "/usr/lib/dyld" because an executable could have specified anything
|
|
|
|
|
// in its LC_LOAD_DYLINKER command.
|
|
|
|
|
if (!found_dyld && all_image_infos.version >= 2 &&
|
|
|
|
|
all_image_infos.dyldImageLoadAddress) {
|
2014-09-05 16:35:16 -04:00
|
|
|
|
Module module;
|
2014-09-05 13:43:51 -04:00
|
|
|
|
module.timestamp = 0;
|
|
|
|
|
|
|
|
|
|
// Examine the executable’s LC_LOAD_DYLINKER load command to find the path
|
|
|
|
|
// used to load dyld.
|
2014-09-22 13:08:57 -04:00
|
|
|
|
if (all_image_infos.infoArrayCount >= 1 && main_executable_count >= 1) {
|
|
|
|
|
module.name = modules_[0].reader->DylinkerName();
|
|
|
|
|
}
|
|
|
|
|
std::string module_name = !module.name.empty() ? module.name : "(dyld)";
|
|
|
|
|
|
2016-04-25 12:13:07 -07:00
|
|
|
|
std::unique_ptr<MachOImageReader> reader(new MachOImageReader());
|
2014-09-22 13:08:57 -04:00
|
|
|
|
if (!reader->Initialize(
|
|
|
|
|
this, all_image_infos.dyldImageLoadAddress, module_name)) {
|
|
|
|
|
reader.reset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module.reader = reader.get();
|
|
|
|
|
|
|
|
|
|
uint32_t file_type = reader ? reader->FileType() : 0;
|
|
|
|
|
|
|
|
|
|
LOG_IF(WARNING, file_type != MH_DYLINKER)
|
|
|
|
|
<< base::StringPrintf("dylinker (%s) has unexpected Mach-O type %d",
|
|
|
|
|
module.name.c_str(),
|
|
|
|
|
file_type);
|
|
|
|
|
|
|
|
|
|
if (module.name.empty() && file_type == MH_DYLINKER) {
|
2014-09-05 13:43:51 -04:00
|
|
|
|
// Look inside dyld directly to find its preferred path.
|
2014-09-22 13:08:57 -04:00
|
|
|
|
module.name = reader->DylinkerName();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (module.name.empty()) {
|
|
|
|
|
module.name = "(dyld)";
|
2014-09-05 13:43:51 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// dyld is loaded in the process even if its path can’t be determined.
|
2017-10-19 00:26:38 -04:00
|
|
|
|
module_readers_.push_back(std::move(reader));
|
2014-09-05 13:43:51 -04:00
|
|
|
|
modules_.push_back(module);
|
|
|
|
|
}
|
2014-08-25 17:51:09 -04:00
|
|
|
|
}
|
|
|
|
|
|
2018-02-22 12:12:26 -08:00
|
|
|
|
mach_vm_address_t ProcessReaderMac::CalculateStackRegion(
|
2014-08-25 17:51:09 -04:00
|
|
|
|
mach_vm_address_t stack_pointer,
|
|
|
|
|
mach_vm_size_t* stack_region_size) {
|
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
|
|
|
|
|
// For pthreads, it may be possible to compute the stack region based on the
|
|
|
|
|
// internal _pthread::stackaddr and _pthread::stacksize. The _pthread struct
|
|
|
|
|
// for a thread can be located at TSD slot 0, or the known offsets of
|
|
|
|
|
// stackaddr and stacksize from the TSD area could be used.
|
|
|
|
|
mach_vm_address_t region_base = stack_pointer;
|
|
|
|
|
mach_vm_size_t region_size;
|
|
|
|
|
natural_t depth = 0;
|
|
|
|
|
vm_prot_t protection;
|
|
|
|
|
unsigned int user_tag;
|
|
|
|
|
kern_return_t kr = MachVMRegionRecurseDeepest(
|
|
|
|
|
task_, ®ion_base, ®ion_size, &depth, &protection, &user_tag);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
MACH_LOG(INFO, kr) << "mach_vm_region_recurse";
|
|
|
|
|
*stack_region_size = 0;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (region_base > stack_pointer) {
|
|
|
|
|
// There’s nothing mapped at the stack pointer’s address. Something may have
|
|
|
|
|
// trashed the stack pointer. Note that this shouldn’t happen for a normal
|
|
|
|
|
// stack guard region violation because the guard region is mapped but has
|
|
|
|
|
// VM_PROT_NONE protection.
|
|
|
|
|
*stack_region_size = 0;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mach_vm_address_t start_address = stack_pointer;
|
|
|
|
|
|
|
|
|
|
if ((protection & VM_PROT_READ) == 0) {
|
|
|
|
|
// If the region isn’t readable, the stack pointer probably points to the
|
|
|
|
|
// guard region. Don’t include it as part of the stack, and don’t include
|
|
|
|
|
// anything at any lower memory address. The code below may still possibly
|
|
|
|
|
// find the real stack region at a memory address higher than this region.
|
|
|
|
|
start_address = region_base + region_size;
|
|
|
|
|
} else {
|
|
|
|
|
// If the ABI requires a red zone, adjust the region to include it if
|
|
|
|
|
// possible.
|
|
|
|
|
LocateRedZone(&start_address, ®ion_base, ®ion_size, user_tag);
|
|
|
|
|
|
|
|
|
|
// Regardless of whether the ABI requires a red zone, capture up to
|
|
|
|
|
// kExtraCaptureSize additional bytes of stack, but only if present in the
|
|
|
|
|
// region that was already found.
|
2017-07-25 19:15:48 -04:00
|
|
|
|
constexpr mach_vm_size_t kExtraCaptureSize = 128;
|
2014-08-25 17:51:09 -04:00
|
|
|
|
start_address = std::max(start_address >= kExtraCaptureSize
|
|
|
|
|
? start_address - kExtraCaptureSize
|
|
|
|
|
: start_address,
|
|
|
|
|
region_base);
|
|
|
|
|
|
|
|
|
|
// Align start_address to a 16-byte boundary, which can help readers by
|
|
|
|
|
// ensuring that data is aligned properly. This could page-align instead,
|
|
|
|
|
// but that might be wasteful.
|
2017-07-25 19:15:48 -04:00
|
|
|
|
constexpr mach_vm_size_t kDesiredAlignment = 16;
|
2014-08-25 17:51:09 -04:00
|
|
|
|
start_address &= ~(kDesiredAlignment - 1);
|
|
|
|
|
DCHECK_GE(start_address, region_base);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
region_size -= (start_address - region_base);
|
|
|
|
|
region_base = start_address;
|
|
|
|
|
|
|
|
|
|
mach_vm_size_t total_region_size = region_size;
|
|
|
|
|
|
|
|
|
|
// The stack region may have gotten split up into multiple abutting regions.
|
|
|
|
|
// Try to coalesce them. This frequently happens for the main thread’s stack
|
|
|
|
|
// when setrlimit(RLIMIT_STACK, …) is called. It may also happen if a region
|
|
|
|
|
// is split up due to an mprotect() or vm_protect() call.
|
|
|
|
|
//
|
|
|
|
|
// Stack regions created by the kernel and the pthreads library will be marked
|
|
|
|
|
// with the VM_MEMORY_STACK user tag. Scanning for multiple adjacent regions
|
|
|
|
|
// with the same tag should find an entire stack region. Checking that the
|
|
|
|
|
// protection on individual regions is not VM_PROT_NONE should guarantee that
|
|
|
|
|
// this algorithm doesn’t collect map entries belonging to another thread’s
|
|
|
|
|
// stack: well-behaved stacks (such as those created by the kernel and the
|
|
|
|
|
// pthreads library) have VM_PROT_NONE guard regions at their low-address
|
|
|
|
|
// ends.
|
|
|
|
|
//
|
|
|
|
|
// Other stack regions may not be so well-behaved and thus if user_tag is not
|
|
|
|
|
// VM_MEMORY_STACK, the single region that was found is used as-is without
|
|
|
|
|
// trying to merge it with other adjacent regions.
|
|
|
|
|
if (user_tag == VM_MEMORY_STACK) {
|
|
|
|
|
mach_vm_address_t try_address = region_base;
|
|
|
|
|
mach_vm_address_t original_try_address;
|
|
|
|
|
|
|
|
|
|
while (try_address += region_size,
|
|
|
|
|
original_try_address = try_address,
|
|
|
|
|
(kr = MachVMRegionRecurseDeepest(task_,
|
|
|
|
|
&try_address,
|
|
|
|
|
®ion_size,
|
|
|
|
|
&depth,
|
|
|
|
|
&protection,
|
|
|
|
|
&user_tag) == KERN_SUCCESS) &&
|
|
|
|
|
try_address == original_try_address &&
|
|
|
|
|
(protection & VM_PROT_READ) != 0 &&
|
|
|
|
|
user_tag == VM_MEMORY_STACK) {
|
|
|
|
|
total_region_size += region_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (kr != KERN_SUCCESS && kr != KERN_INVALID_ADDRESS) {
|
|
|
|
|
// Tolerate KERN_INVALID_ADDRESS because it will be returned when there
|
|
|
|
|
// are no more regions in the map at or above the specified |try_address|.
|
|
|
|
|
MACH_LOG(INFO, kr) << "mach_vm_region_recurse";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*stack_region_size = total_region_size;
|
|
|
|
|
return region_base;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-22 12:12:26 -08:00
|
|
|
|
void ProcessReaderMac::LocateRedZone(mach_vm_address_t* const start_address,
|
|
|
|
|
mach_vm_address_t* const region_base,
|
|
|
|
|
mach_vm_address_t* const region_size,
|
|
|
|
|
const unsigned int user_tag) {
|
2014-08-25 17:51:09 -04:00
|
|
|
|
#if defined(ARCH_CPU_X86_FAMILY)
|
|
|
|
|
if (Is64Bit()) {
|
2017-11-20 16:57:43 -05:00
|
|
|
|
// x86_64 has a red zone. See AMD64 ABI 0.99.8,
|
|
|
|
|
// https://raw.githubusercontent.com/wiki/hjl-tools/x86-psABI/x86-64-psABI-r252.pdf#page=19,
|
|
|
|
|
// section 3.2.2, “The Stack Frame”.
|
2017-07-25 19:15:48 -04:00
|
|
|
|
constexpr mach_vm_size_t kRedZoneSize = 128;
|
2014-08-25 17:51:09 -04:00
|
|
|
|
mach_vm_address_t red_zone_base =
|
|
|
|
|
*start_address >= kRedZoneSize ? *start_address - kRedZoneSize : 0;
|
|
|
|
|
bool red_zone_ok = false;
|
|
|
|
|
if (red_zone_base >= *region_base) {
|
|
|
|
|
// The red zone is within the region already discovered.
|
|
|
|
|
red_zone_ok = true;
|
|
|
|
|
} else if (red_zone_base < *region_base && user_tag == VM_MEMORY_STACK) {
|
|
|
|
|
// Probe to see if there’s a region immediately below the one already
|
|
|
|
|
// discovered.
|
|
|
|
|
mach_vm_address_t red_zone_region_base = red_zone_base;
|
|
|
|
|
mach_vm_size_t red_zone_region_size;
|
|
|
|
|
natural_t red_zone_depth = 0;
|
|
|
|
|
vm_prot_t red_zone_protection;
|
|
|
|
|
unsigned int red_zone_user_tag;
|
|
|
|
|
kern_return_t kr = MachVMRegionRecurseDeepest(task_,
|
|
|
|
|
&red_zone_region_base,
|
|
|
|
|
&red_zone_region_size,
|
|
|
|
|
&red_zone_depth,
|
|
|
|
|
&red_zone_protection,
|
|
|
|
|
&red_zone_user_tag);
|
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
|
|
|
MACH_LOG(INFO, kr) << "mach_vm_region_recurse";
|
|
|
|
|
*start_address = *region_base;
|
|
|
|
|
} else if (red_zone_region_base + red_zone_region_size == *region_base &&
|
|
|
|
|
(red_zone_protection & VM_PROT_READ) != 0 &&
|
|
|
|
|
red_zone_user_tag == user_tag) {
|
|
|
|
|
// The region containing the red zone is immediately below the region
|
|
|
|
|
// already found, it’s readable (not the guard region), and it has the
|
|
|
|
|
// same user tag as the region already found, so merge them.
|
|
|
|
|
red_zone_ok = true;
|
|
|
|
|
*region_base -= red_zone_region_size;
|
|
|
|
|
*region_size += red_zone_region_size;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (red_zone_ok) {
|
|
|
|
|
// Begin capturing from the base of the red zone (but not the entire
|
|
|
|
|
// region that encompasses the red zone).
|
|
|
|
|
*start_address = red_zone_base;
|
|
|
|
|
} else {
|
|
|
|
|
// The red zone would go lower into another region in memory, but no
|
|
|
|
|
// region was found. Memory can only be captured to an address as low as
|
|
|
|
|
// the base address of the region already found.
|
|
|
|
|
*start_address = *region_base;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace crashpad
|