crashpad/util/mach/exception_ports.cc

134 lines
4.3 KiB
C++
Raw Normal View History

// 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.
#include "util/mach/exception_ports.h"
#include "base/logging.h"
#include "base/mac/mach_logging.h"
namespace crashpad {
ExceptionPorts::ExceptionPorts(TargetType target_type, mach_port_t target_port)
: target_port_(target_port), dealloc_target_port_(false) {
switch (target_type) {
case kTargetTypeHost:
get_exception_ports_ = host_get_exception_ports;
set_exception_ports_ = host_set_exception_ports;
target_name_ = "host";
if (target_port_ == HOST_NULL) {
target_port_ = mach_host_self();
dealloc_target_port_ = true;
}
break;
case kTargetTypeTask:
get_exception_ports_ = task_get_exception_ports;
set_exception_ports_ = task_set_exception_ports;
target_name_ = "task";
if (target_port_ == TASK_NULL) {
target_port_ = mach_task_self();
// Dont deallocate mach_task_self().
}
break;
case kTargetTypeThread:
get_exception_ports_ = thread_get_exception_ports;
set_exception_ports_ = thread_set_exception_ports;
target_name_ = "thread";
if (target_port_ == THREAD_NULL) {
target_port_ = mach_thread_self();
dealloc_target_port_ = true;
}
break;
default:
NOTREACHED();
get_exception_ports_ = NULL;
set_exception_ports_ = NULL;
target_name_ = NULL;
target_port_ = MACH_PORT_NULL;
break;
}
}
ExceptionPorts::~ExceptionPorts() {
if (dealloc_target_port_) {
kern_return_t kr = mach_port_deallocate(mach_task_self(), target_port_);
MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_port_deallocate";
}
}
bool ExceptionPorts::GetExceptionPorts(
exception_mask_t mask,
std::vector<ExceptionHandler>* handlers) const {
// <mach/mach_types.defs> says that these arrays have room for 32 elements,
// despite EXC_TYPES_COUNT only being as low as 11 (in the 10.6 SDK), and
// later operating system versions have defined more exception types. The
// generated task_get_exception_ports() in taskUser.c expects there to be room
// for 32.
const int kMaxPorts = 32;
// task_get_exception_ports() doesnt actually use the initial value of
// handler_count, but 10.9.4
// xnu-2422.110.17/osfmk/man/task_get_exception_ports.html says it does. Humor
// the documentation.
mach_msg_type_number_t handler_count = kMaxPorts;
exception_mask_t masks[kMaxPorts];
exception_handler_t ports[kMaxPorts];
exception_behavior_t behaviors[kMaxPorts];
thread_state_flavor_t flavors[kMaxPorts];
kern_return_t kr = get_exception_ports_(
target_port_, mask, masks, &handler_count, ports, behaviors, flavors);
if (kr != KERN_SUCCESS) {
MACH_LOG(ERROR, kr) << TargetTypeName() << "_get_exception_ports";
return false;
}
handlers->clear();
for (mach_msg_type_number_t index = 0; index < handler_count; ++index) {
ExceptionPorts::GetExceptionPorts(): don’t return ExceptionHandler elements whose handler port would be MACH_PORT_NULL. For most exception targets, *_get_exception_ports() will normally return an exception port of MACH_PORT_NULL when no handler is registered. However, as of Mac OS X 10.9, thread_get_exception_ports() will return an empty list when no handler is registered for any exception type on a thread. Consequently, a caller would have to do additional processing to determine whether a specific exception port is registered: an unregistered port will either appear but have a handler port of MACH_PORT_NULL, or it will not appear at all. This is confusing for callers. The behaviors are unified, and when a handler port of MACH_PORT_NULL is found, it will not be returned to the caller. This is expected to be the simpler of the two possible behaviors for callers to make use of. The change in the kernel can be seen by comparing 10.8.5 xnu-2050.48.11/osfmk/kern/ipc_tt.c thread_get_exception_ports() to the same function in 10.9.4 xnu-2422.110.17. The 10.9 version has a special check for thread->exc_actions being NULL, which short-circuits the rest of the function without returning any exception ports. In 10.8.5, thread->exc_actions can never be NULL. This new check is only present for thread targets, presumably because it’s very common for threads to not have any exception ports set, and not having to initialize this data is an optimization. Typical user-level tasks in Mac OS X always have at least some exception ports set at the task level. TEST=util_test ExceptionPorts.TaskAndThreadExceptionPorts R=rsesek@chromium.org Review URL: https://codereview.chromium.org/584223002
2014-09-22 13:07:43 -04:00
if (ports[index] != MACH_PORT_NULL) {
ExceptionHandler handler;
handler.mask = masks[index];
handler.port = ports[index];
handler.behavior = behaviors[index];
handler.flavor = flavors[index];
handlers->push_back(handler);
}
}
return true;
}
bool ExceptionPorts::SetExceptionPort(exception_mask_t mask,
exception_handler_t port,
exception_behavior_t behavior,
thread_state_flavor_t flavor) const {
kern_return_t kr =
set_exception_ports_(target_port_, mask, port, behavior, flavor);
if (kr != KERN_SUCCESS) {
MACH_LOG(ERROR, kr) << TargetTypeName() << "_set_exception_ports";
return false;
}
return true;
}
const char* ExceptionPorts::TargetTypeName() const {
return target_name_;
}
} // namespace crashpad