crashpad/util/mach/exception_ports.h
Mark Mentovai 5f7eda87a6 mac: Don’t leak send rights from ExceptionPorts::GetExceptionPorts()
ExceptionPorts::GetExceptionPorts() returned a
std::vector<ExceptionPorts::ExceptionHandler>, which contained send
rights to Mach ports. The interface required callers to assume ownership
of each send right contained within the vector. This was cumbersome and
error-prone, and despite the care taken in Crashpad, port right leaks
did occur:

 - SimulateCrash() didn’t make any attempt to release these resources at
   all.
 - Neither did crashpad_util_test ExceptionPorts.HostExceptionPorts,
   which also reused a vector.

This replaces the vector with the interface-compatible (as far as
necessary) ExceptionPorts::ExceptionHandlerVector, which deallocates
collected port rights on destruction or clear().

R=rsesek@chromium.org

Review URL: https://codereview.chromium.org/1381023007 .
2015-10-06 16:14:29 -04:00

219 lines
9.7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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>
#include "base/basictypes.h"
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 systems 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()` doesnt return the privileged
//! `host_priv` port to non-root users, and this is the target port
//! thats 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 elements 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();
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_;
DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerVector);
};
//! \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();
//! \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 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);
GetExceptionPortsType get_exception_ports_;
SetExceptionPortsType set_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_;
DISALLOW_COPY_AND_ASSIGN(ExceptionPorts);
};
} // namespace crashpad
#endif // CRASHPAD_UTIL_MACH_EXCEPTION_PORTS_H_