// Copyright 2014 The Crashpad Authors // // 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 #include 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`, 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`. Note that non-`const` //! mutators are not provided to avoid accidental Mach right leaks. class ExceptionHandlerVector { public: using VectorType = std::vector; 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_