OLD | NEW |
(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 |
OLD | NEW |