Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(74)

Side by Side Diff: client/simulate_crash_mac.cc

Issue 658853002: Add CRASHPAD_SIMULATE_CRASH() and supporting client code (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "client/simulate_crash_mac.h"
16
17 #include <string.h>
18
19 #include <vector>
20
21 #include "base/basictypes.h"
22 #include "base/logging.h"
23 #include "base/mac/mach_logging.h"
24 #include "base/mac/scoped_mach_port.h"
25 #include "base/strings/stringprintf.h"
26 #include "build/build_config.h"
27 #include "util/mach/exc_client_variants.h"
28 #include "util/mach/exception_behaviors.h"
29 #include "util/mach/exception_ports.h"
30 #include "util/mach/mach_extensions.h"
31
32 namespace crashpad {
33
34 namespace {
35
36 //! \brief Sends an exception message to an exception port in accordance with
37 //! the behavior and thread state flavor it’s registered to receive.
38 //!
39 //! \param[in] thread, task, exception, code, code_count These parameters will
40 //! be passed to the exception handler as appropriate.
41 //! \param[in] cpu_context The value to use for the thread state, if \a behavior
42 //! indicates that the handler should receive a thread state and if the
43 //! supplied thread state matches or can be converted to \a flavor. If \a
44 //! behavior requires a thread state but this argument cannot be converted
45 //! to match \a flavor, `thread_get_state()` will be called to obtain a
46 //! suitable thread state value.
47 //! \param[in] handler The Mach exception handler to deliver the exception to.
48 //! \param[in] set_state If `true` and \a behavior indicates that the handler
49 //! should receive and return a thread state, a new thread state will be set
50 //! by `thread_set_state()` upon successful completion of the exception
51 //! handler. If `false`, this will be suppressed, even when \a behavior
52 //! indicates that the handler receives and returns a thread state.
53 //!
54 //! \return `true` if the exception was delivered to the handler and the handler
55 //! indicated success. `false` otherwise, with a warning message logged.
56 bool DeliverException(thread_t thread,
57 task_t task,
58 exception_type_t exception,
59 const mach_exception_data_t code,
60 mach_msg_type_number_t code_count,
61 const NativeCPUContext* cpu_context,
62 const ExceptionPorts::ExceptionHandler& handler,
63 bool set_state) {
64 kern_return_t kr;
65
66 bool handler_wants_state = ExceptionBehaviorHasState(handler.behavior);
67 if (!handler_wants_state) {
68 // Regardless of the passed-in value of |set_state|, if the handler won’t be
69 // dealing with any state at all, no state should be set.
70 set_state = false;
71 }
72
73 // A const version of thread_state_t.
74 typedef const natural_t* ConstThreadState;
75
76 // old_state is only used if the context already captured doesn’t match (or
77 // can’t be converted to) what’s registered for the handler.
78 thread_state_data_t old_state;
79
80 thread_state_flavor_t flavor = handler.flavor;
81 ConstThreadState state;
82 mach_msg_type_number_t state_count;
83 switch (flavor) {
84 #if defined(ARCH_CPU_X86_FAMILY)
85 case x86_THREAD_STATE:
86 state = reinterpret_cast<ConstThreadState>(cpu_context);
87 state_count = x86_THREAD_STATE_COUNT;
88 break;
89 #if defined(ARCH_CPU_X86)
90 case x86_THREAD_STATE32:
91 state = reinterpret_cast<ConstThreadState>(&cpu_context->uts.ts32);
92 state_count = cpu_context->tsh.count;
93 break;
94 #elif defined(ARCH_CPU_X86_64)
95 case x86_THREAD_STATE64:
96 state = reinterpret_cast<ConstThreadState>(&cpu_context->uts.ts64);
97 state_count = cpu_context->tsh.count;
98 break;
99 #endif
100 #else
101 #error Port to your CPU architecture
102 #endif
103
104 case THREAD_STATE_NONE:
105 // This is only acceptable if the handler doesn’t have one of the “state”
106 // behaviors. Otherwise, if the kernel were attempting to send an
107 // exception message to this port, it would call thread_getstatus() (known
108 // outside the kernel as thread_get_state()) which would fail because
109 // THREAD_STATE_NONE is not a valid state to get. See 10.9.5
110 // xnu-2422.115.4/osfmk/kern/exception.c exception_deliver() and
111 // xnu-2422.115.4/osfmk/i386/pcb.c machine_thread_get_state().
112 if (!handler_wants_state) {
113 state = nullptr;
114 state_count = 0;
115 break;
116 }
117
118 LOG(WARNING) << "exception handler has unexpected state flavor" << flavor;
119 return false;
120
121 default:
122 if (!handler_wants_state) {
123 // Don’t bother getting any thread state if the handler’s not actually
124 // going to use it.
125 state = nullptr;
126 state_count = 0;
127 } else {
128 state = old_state;
129 state_count = THREAD_STATE_MAX;
130 kr = thread_get_state(thread, flavor, old_state, &state_count);
131 if (kr != KERN_SUCCESS) {
132 MACH_LOG(WARNING, kr) << "thread_get_state";
133 return false;
134 }
135 }
136 break;
137 }
138
139 // new_state is supposed to be an out parameter only, but in case the handler
140 // doesn't touch it, make sure it's initialized to a valid thread state.
141 // Otherwise, the thread_set_state() call below would set a garbage thread
142 // state.
143 thread_state_data_t new_state;
144 size_t state_size =
145 sizeof(natural_t) *
146 std::min(state_count, static_cast<unsigned int>(THREAD_STATE_MAX));
147 memcpy(new_state, state, state_size);
148 mach_msg_type_number_t new_state_count = THREAD_STATE_MAX;
149
150 kr = UniversalExceptionRaise(handler.behavior,
151 handler.port,
152 thread,
153 task,
154 exception,
155 code,
156 code_count,
157 &flavor,
158 state,
159 state_count,
160 new_state,
161 &new_state_count);
162
163 // The kernel treats a return value of MACH_RCV_PORT_DIED as successful,
164 // although it will not set a new thread state in that case. See 10.9.5
165 // xnu-2422.115.4/osfmk/kern/exception.c exception_deliver(), and the more
166 // elaborate comment at util/mach/exc_server_variants.h
167 // ExcServerSuccessfulReturnValue(). Duplicate that behavior.
168 bool success = kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED;
169 MACH_LOG_IF(WARNING, !success, kr) << "UniversalExceptionRaise";
170
171 if (kr == KERN_SUCCESS && set_state) {
172 kr = thread_set_state(thread, flavor, new_state, new_state_count);
173 MACH_LOG_IF(WARNING, kr != KERN_SUCCESS, kr) << "thread_set_state";
174 }
175
176 return success;
177 }
178
179 } // namespace
180
181 void SimulateCrash(const NativeCPUContext* cpu_context) {
182 #if defined(ARCH_CPU_X86)
183 DCHECK_EQ(cpu_context->tsh.flavor,
184 static_cast<thread_state_flavor_t>(x86_THREAD_STATE32));
185 DCHECK_EQ(static_cast<mach_msg_type_number_t>(cpu_context->tsh.count),
186 x86_THREAD_STATE32_COUNT);
187 #elif defined(ARCH_CPU_X86_64)
188 DCHECK_EQ(cpu_context->tsh.flavor,
189 static_cast<thread_state_flavor_t>(x86_THREAD_STATE64));
190 DCHECK_EQ(static_cast<mach_msg_type_number_t>(cpu_context->tsh.count),
191 x86_THREAD_STATE64_COUNT);
192 #endif
193
194 base::mac::ScopedMachSendRight thread(mach_thread_self());
195 exception_type_t exception = kMachExceptionSimulated;
196 mach_exception_data_type_t codes[] = {0, 0};
197 mach_msg_type_number_t code_count = arraysize(codes);
198
199 // Look up the handler for EXC_CRASH exceptions in the same way that the
200 // kernel would: try a thread handler, then a task handler, and finally a host
201 // handler. 10.9.5 xnu-2422.115.4/osfmk/kern/exception.c exception_triage().
202 const ExceptionPorts::TargetType kTargetTypes[] = {
203 ExceptionPorts::kTargetTypeThread,
204 ExceptionPorts::kTargetTypeTask,
205
206 // This is not expected to succeed, because mach_host_self() doesn’t
207 // return the host_priv port to non-root users, and this is the port
208 // that’s required for host_get_exception_ports().
209 //
210 // See 10.9.5 xnu-2422.115.4/bsd/kern/kern_prot.c set_security_token(),
211 // xnu-2422.115.4/osfmk/kern/task.c host_security_set_task_token(), and
212 // xnu-2422.115.4/osfmk/kern/ipc_host.c host_get_exception_ports().
213 ExceptionPorts::kTargetTypeHost,
214 };
215
216 bool success = false;
217
218 for (size_t target_type_index = 0;
219 !success && target_type_index < arraysize(kTargetTypes);
220 ++target_type_index) {
221 std::vector<ExceptionPorts::ExceptionHandler> handlers;
222 ExceptionPorts exception_ports(kTargetTypes[target_type_index],
223 MACH_PORT_NULL);
224 if (exception_ports.GetExceptionPorts(EXC_MASK_CRASH, &handlers)) {
225 DCHECK_LE(handlers.size(), 1u);
226 if (handlers.size() == 1) {
227 DCHECK(handlers[0].mask & EXC_MASK_CRASH);
228 success = DeliverException(thread,
229 mach_task_self(),
230 exception,
231 codes,
232 code_count,
233 cpu_context,
234 handlers[0],
235 false);
236 }
237 }
238 }
239
240 LOG_IF(WARNING, !success)
241 << "SimulateCrash did not find an appropriate exception handler";
242 }
243
244 } // namespace crashpad
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698