admin
2023-03-07 8b06b1cbf112d55307ea8a6efe711db4e7506d89
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
// 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/macros.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 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();
 
    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 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_;
 
  DISALLOW_COPY_AND_ASSIGN(ExceptionPorts);
};
 
}  // namespace crashpad
 
#endif  // CRASHPAD_UTIL_MACH_EXCEPTION_PORTS_H_