// 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" #include "base/notreached.h" namespace crashpad { ExceptionPorts::ExceptionHandlerVector::ExceptionHandlerVector() : vector_() { } ExceptionPorts::ExceptionHandlerVector::~ExceptionHandlerVector() { Deallocate(); } void ExceptionPorts::ExceptionHandlerVector::clear() { Deallocate(); vector_.clear(); } void ExceptionPorts::ExceptionHandlerVector::Deallocate() { for (ExceptionHandler& exception_handler : vector_) { if (exception_handler.port != MACH_PORT_NULL) { kern_return_t kr = mach_port_deallocate(mach_task_self(), exception_handler.port); MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_port_deallocate"; } } } 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; swap_exception_ports_ = host_swap_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; swap_exception_ports_ = task_swap_exception_ports; target_name_ = "task"; if (target_port_ == TASK_NULL) { target_port_ = mach_task_self(); // Don’t deallocate mach_task_self(). } break; case kTargetTypeThread: get_exception_ports_ = thread_get_exception_ports; set_exception_ports_ = thread_set_exception_ports; swap_exception_ports_ = thread_swap_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_ = nullptr; set_exception_ports_ = nullptr; target_name_ = nullptr; 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, ExceptionHandlerVector* handlers) const { // 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. constexpr int kMaxPorts = 32; // task_get_exception_ports() doesn’t 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) { 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; } bool ExceptionPorts::SwapExceptionPorts( exception_mask_t mask, exception_handler_t new_port, exception_behavior_t new_behavior, thread_state_flavor_t new_flavor, ExceptionHandlerVector* old_handlers) const { // 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_swap_exception_ports() in taskUser.c expects there to be // room for 32. constexpr int kMaxPorts = 32; // task_swap_exception_ports() doesn’t actually use the initial value of // handler_count, but 10.15.1 // xnu-6153.41.3/osfmk/man/task_get_exception_ports.html says it does. Humor // the documentation. mach_msg_type_number_t old_handler_count = kMaxPorts; exception_mask_t old_masks[kMaxPorts]; exception_handler_t old_ports[kMaxPorts]; exception_behavior_t old_behaviors[kMaxPorts]; thread_state_flavor_t old_flavors[kMaxPorts]; kern_return_t kr = swap_exception_ports_(target_port_, mask, new_port, new_behavior, new_flavor, old_masks, &old_handler_count, old_ports, old_behaviors, old_flavors); if (kr != KERN_SUCCESS) { MACH_LOG(ERROR, kr) << TargetTypeName() << "_swap_exception_ports"; return false; } old_handlers->clear(); for (mach_msg_type_number_t index = 0; index < old_handler_count; ++index) { if (old_ports[index] != MACH_PORT_NULL) { ExceptionHandler old_handler; old_handler.mask = old_masks[index]; old_handler.port = old_ports[index]; old_handler.behavior = old_behaviors[index]; old_handler.flavor = old_flavors[index]; old_handlers->push_back(old_handler); } } return true; } const char* ExceptionPorts::TargetTypeName() const { return target_name_; } } // namespace crashpad