265 lines
12 KiB
C
265 lines
12 KiB
C
|
// 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.
|
|||
|
|
|||
|
#ifndef CRASHPAD_UTIL_MACH_EXCEPTION_PORTS_H_
|
|||
|
#define CRASHPAD_UTIL_MACH_EXCEPTION_PORTS_H_
|
|||
|
|
|||
|
#include <mach/mach.h>
|
|||
|
|
|||
|
#include <vector>
|
|||
|
|
|||
|
|
|||
|
namespace crashpad {
|
|||
|
|
|||
|
//! \brief A better interface to `*_get_exception_ports()` and
|
|||
|
//! `*_set_exception_ports()`.
|
|||
|
//!
|
|||
|
//! The same generic interface can be used to operate on host, task, and thread
|
|||
|
//! exception ports. The “get” interface is superior to the system’s native
|
|||
|
//! interface because it keeps related data about a single exception handler
|
|||
|
//! together in one struct, rather than separating it into four parallel arrays.
|
|||
|
class ExceptionPorts {
|
|||
|
public:
|
|||
|
//! \brief Various entities which can have their own exception ports set.
|
|||
|
enum TargetType {
|
|||
|
//! \brief The host exception target.
|
|||
|
//!
|
|||
|
//! `host_get_exception_ports()` and `host_set_exception_ports()` will be
|
|||
|
//! used. If no target port is explicitly provided, `mach_host_self()` will
|
|||
|
//! be used as the target port. `mach_host_self()` is the only target port
|
|||
|
//! for this type that is expected to function properly.
|
|||
|
//!
|
|||
|
//! \note Operations on this target type are not expected to succeed as
|
|||
|
//! non-root, because `mach_host_self()` doesn’t return the privileged
|
|||
|
//! `host_priv` port to non-root users, and this is the target port
|
|||
|
//! that’s required for `host_get_exception_ports()` and
|
|||
|
//! `host_set_exception_ports()`.
|
|||
|
kTargetTypeHost = 0,
|
|||
|
|
|||
|
//! \brief A task exception target.
|
|||
|
//!
|
|||
|
//! `task_get_exception_ports()` and `task_set_exception_ports()` will be
|
|||
|
//! used. If no target port is explicitly provided, `mach_task_self()` will
|
|||
|
//! be used as the target port.
|
|||
|
kTargetTypeTask,
|
|||
|
|
|||
|
//! \brief A thread exception target.
|
|||
|
//!
|
|||
|
//! `thread_get_exception_ports()` and `thread_set_exception_ports()` will
|
|||
|
//! be used. If no target port is explicitly provided, `mach_thread_self()`
|
|||
|
//! will be used as the target port.
|
|||
|
kTargetTypeThread,
|
|||
|
};
|
|||
|
|
|||
|
//! \brief Information about a registered exception handler.
|
|||
|
struct ExceptionHandler {
|
|||
|
//! \brief A mask specifying the exception types to direct to \a port,
|
|||
|
//! containing `EXC_MASK_*` values.
|
|||
|
exception_mask_t mask;
|
|||
|
|
|||
|
//! \brief A send right to a Mach port that will handle exceptions of the
|
|||
|
//! types indicated in \a mask.
|
|||
|
exception_handler_t port;
|
|||
|
|
|||
|
//! \brief The “behavior” that the exception handler at \a port implements:
|
|||
|
//! `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`, or
|
|||
|
//! `EXCEPTION_STATE_IDENTITY`, possibly combined with
|
|||
|
//! `MACH_EXCEPTION_CODES`.
|
|||
|
exception_behavior_t behavior;
|
|||
|
|
|||
|
//! \brief The thread state flavor that the exception handler at \a port
|
|||
|
//! will receive and possibly modify. This member has no effect for \a
|
|||
|
//! \a behavior values that indicate a “default” behavior.
|
|||
|
thread_state_flavor_t flavor;
|
|||
|
};
|
|||
|
|
|||
|
//! \brief Wraps `std::vector<ExceptionHandler>`, providing proper cleanup of
|
|||
|
//! the send rights contained in each element’s ExceptionHandler::port.
|
|||
|
//!
|
|||
|
//! Upon destruction or clear(), an object of this class will deallocate all
|
|||
|
//! send rights it contains. Otherwise, it is an interface-compatible drop-in
|
|||
|
//! replacement for `std::vector<ExceptionHandler>`. Note that non-`const`
|
|||
|
//! mutators are not provided to avoid accidental Mach right leaks.
|
|||
|
class ExceptionHandlerVector {
|
|||
|
public:
|
|||
|
using VectorType = std::vector<ExceptionHandler>;
|
|||
|
|
|||
|
ExceptionHandlerVector();
|
|||
|
|
|||
|
ExceptionHandlerVector(const ExceptionHandlerVector&) = delete;
|
|||
|
ExceptionHandlerVector& operator=(const ExceptionHandlerVector&) = delete;
|
|||
|
|
|||
|
~ExceptionHandlerVector();
|
|||
|
|
|||
|
VectorType::const_iterator begin() const { return vector_.begin(); }
|
|||
|
VectorType::const_iterator end() const { return vector_.end(); }
|
|||
|
VectorType::size_type size() const { return vector_.size(); }
|
|||
|
bool empty() const { return vector_.empty(); }
|
|||
|
VectorType::const_reference operator[](VectorType::size_type index) const {
|
|||
|
return vector_[index];
|
|||
|
}
|
|||
|
void push_back(VectorType::value_type& value) { vector_.push_back(value); }
|
|||
|
void clear();
|
|||
|
|
|||
|
private:
|
|||
|
void Deallocate();
|
|||
|
|
|||
|
VectorType vector_;
|
|||
|
};
|
|||
|
|
|||
|
//! \brief Constructs an interface object to get or set exception ports on a
|
|||
|
//! host, task, or thread port.
|
|||
|
//!
|
|||
|
//! \param[in] target_type The type of target on which the exception ports are
|
|||
|
//! to be get or set: #kTargetTypeHost, #kTargetTypeTask, or or
|
|||
|
//! #kTargetTypeThread. The correct functions for
|
|||
|
//! `*_get_exception_ports()` and `*_set_exception_ports()` will be used.
|
|||
|
//! \param[in] target_port The target on which to call
|
|||
|
//! `*_get_exception_ports()` or `*_set_exception_ports()`. The target
|
|||
|
//! port must be a send right to a port of the type specified in \a
|
|||
|
//! target_type. In this case, ownership of \a target_port is not given to
|
|||
|
//! the new ExceptionPorts object. \a target_port may also be
|
|||
|
//! `HOST_NULL`, `TASK_NULL`, or `THREAD_NULL`, in which case
|
|||
|
//! `mach_host_self()`, `mach_task_self()`, or `mach_thread_self()` will
|
|||
|
//! be used as the target port depending on the value of \a target_type.
|
|||
|
//! In this case, ownership of the target port will be managed
|
|||
|
//! appropriately for \a target_type.
|
|||
|
ExceptionPorts(TargetType target_type, mach_port_t target_port);
|
|||
|
|
|||
|
ExceptionPorts(const ExceptionPorts&) = delete;
|
|||
|
ExceptionPorts& operator=(const ExceptionPorts&) = delete;
|
|||
|
|
|||
|
~ExceptionPorts();
|
|||
|
|
|||
|
//! \brief Calls `*_get_exception_ports()` on the target.
|
|||
|
//!
|
|||
|
//! \param[in] mask The exception mask, containing the `EXC_MASK_*` values to
|
|||
|
//! be looked up and returned in \a handlers.
|
|||
|
//! \param[out] handlers The exception handlers registered for \a target_port
|
|||
|
//! to handle exceptions indicated in \a mask. If no execption port is
|
|||
|
//! registered for a bit in \a mask, \a handlers will not contain an entry
|
|||
|
//! corresponding to that bit. This is a departure from the
|
|||
|
//! `*_get_exception_ports()` functions, which may return a handler whose
|
|||
|
//! port is set to `EXCEPTION_PORT_NULL` in this case. On failure, this
|
|||
|
//! argument is untouched.
|
|||
|
//!
|
|||
|
//! \return `true` if `*_get_exception_ports()` returned `KERN_SUCCESS`, with
|
|||
|
//! \a handlers set appropriately. `false` otherwise, with an appropriate
|
|||
|
//! message logged.
|
|||
|
bool GetExceptionPorts(exception_mask_t mask,
|
|||
|
ExceptionHandlerVector* handlers) const;
|
|||
|
|
|||
|
//! \brief Calls `*_set_exception_ports()` on the target.
|
|||
|
//!
|
|||
|
//! \param[in] mask A mask specifying the exception types to direct to \a
|
|||
|
//! port, containing `EXC_MASK_*` values.
|
|||
|
//! \param[in] port A send right to a Mach port that will handle exceptions
|
|||
|
//! sustained by \a target_port of the types indicated in \a mask. The
|
|||
|
//! send right is copied, not consumed, by this call.
|
|||
|
//! \param[in] behavior The “behavior” that the exception handler at \a port
|
|||
|
//! implements: `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`, or
|
|||
|
//! `EXCEPTION_STATE_IDENTITY`, possibly combined with
|
|||
|
//! `MACH_EXCEPTION_CODES`.
|
|||
|
//! \param[in] flavor The thread state flavor that the exception handler at \a
|
|||
|
//! port expects to receive and possibly modify. This argument has no
|
|||
|
//! effect for \a behavior values that indicate a “default” behavior.
|
|||
|
//!
|
|||
|
//! \return `true` if `*_set_exception_ports()` returned `KERN_SUCCESS`.
|
|||
|
//! `false` otherwise, with an appropriate message logged.
|
|||
|
bool SetExceptionPort(exception_mask_t mask,
|
|||
|
exception_handler_t port,
|
|||
|
exception_behavior_t behavior,
|
|||
|
thread_state_flavor_t flavor) const;
|
|||
|
|
|||
|
//! \brief Calls `*_swap_exception_ports()` on the target.
|
|||
|
//!
|
|||
|
//! \param[in] mask A mask specifying the exception types to direct to \a
|
|||
|
//! port, containing `EXC_MASK_*` values.
|
|||
|
//! \param[in] new_port A send right to a Mach port that will handle
|
|||
|
//! exceptions sustained by \a target_port of the types indicated in \a
|
|||
|
//! mask. The send right is copied, not consumed, by this call.
|
|||
|
//! \param[in] new_behavior The “behavior” that the exception handler at \a
|
|||
|
//! port implements: `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`, or
|
|||
|
//! `EXCEPTION_STATE_IDENTITY`, possibly combined with
|
|||
|
//! `MACH_EXCEPTION_CODES`.
|
|||
|
//! \param[in] new_flavor The thread state flavor that the exception handler
|
|||
|
//! at \a port expects to receive and possibly modify. This argument has
|
|||
|
//! no effect for \a new_behavior values that indicate a “default”
|
|||
|
//! behavior.
|
|||
|
//! \param[out] old_handlers The exception handlers registered for \a
|
|||
|
//! target_port to handle exceptions indicated in \a mask. If no execption
|
|||
|
//! port is registered for a bit in \a mask, \a old_handlers will not
|
|||
|
//! contain an entry corresponding to that bit. This is a departure from
|
|||
|
//! the `*_get_exception_ports()` functions, which may return a handler
|
|||
|
//! whose port is set to `EXCEPTION_PORT_NULL` in this case. On failure,
|
|||
|
//! this argument is untouched.
|
|||
|
//!
|
|||
|
//! \return `true` if `*_swap_exception_ports()` returned `KERN_SUCCESS`, with
|
|||
|
//! \a old_handlers set appropriately. . `false` otherwise, with an
|
|||
|
//! appropriate message logged.
|
|||
|
bool 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;
|
|||
|
|
|||
|
//! \brief Returns a string identifying the target type.
|
|||
|
//!
|
|||
|
//! \return `"host"`, `"task"`, or `"thread"`, as appropriate.
|
|||
|
const char* TargetTypeName() const;
|
|||
|
|
|||
|
private:
|
|||
|
using GetExceptionPortsType = kern_return_t(*)(mach_port_t,
|
|||
|
exception_mask_t,
|
|||
|
exception_mask_array_t,
|
|||
|
mach_msg_type_number_t*,
|
|||
|
exception_handler_array_t,
|
|||
|
exception_behavior_array_t,
|
|||
|
exception_flavor_array_t);
|
|||
|
|
|||
|
using SetExceptionPortsType = kern_return_t(*)(mach_port_t,
|
|||
|
exception_mask_t,
|
|||
|
exception_handler_t,
|
|||
|
exception_behavior_t,
|
|||
|
thread_state_flavor_t);
|
|||
|
|
|||
|
using SwapExceptionPortsType = kern_return_t(*)(mach_port_t,
|
|||
|
exception_mask_t,
|
|||
|
exception_handler_t,
|
|||
|
exception_behavior_t,
|
|||
|
thread_state_flavor_t,
|
|||
|
exception_mask_array_t,
|
|||
|
mach_msg_type_number_t*,
|
|||
|
exception_handler_array_t,
|
|||
|
exception_behavior_array_t,
|
|||
|
exception_flavor_array_t);
|
|||
|
|
|||
|
GetExceptionPortsType get_exception_ports_;
|
|||
|
SetExceptionPortsType set_exception_ports_;
|
|||
|
SwapExceptionPortsType swap_exception_ports_;
|
|||
|
const char* target_name_;
|
|||
|
mach_port_t target_port_;
|
|||
|
|
|||
|
// If true, target_port_ will be deallocated in the destructor. This will
|
|||
|
// always be false when the user provides a non-null target_port to the
|
|||
|
// constructor. It will also be false when target_type is kTargetTypeTask,
|
|||
|
// even with a TASK_NULL target_port, because it is incorrect to deallocate
|
|||
|
// the result of mach_task_self().
|
|||
|
bool dealloc_target_port_;
|
|||
|
};
|
|||
|
|
|||
|
} // namespace crashpad
|
|||
|
|
|||
|
#endif // CRASHPAD_UTIL_MACH_EXCEPTION_PORTS_H_
|