1//===-- MachException.cpp ---------------------------------------*- C++ -*-===// 2// 3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4// See https://llvm.org/LICENSE.txt for license information. 5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6// 7//===----------------------------------------------------------------------===// 8// 9// Created by Greg Clayton on 6/18/07. 10// 11//===----------------------------------------------------------------------===// 12 13#include "MachException.h" 14 15// C includes 16#include <errno.h> 17#include <sys/ptrace.h> 18#include <sys/types.h> 19 20// C++ includes 21#include <mutex> 22 23// LLDB includes 24#include "lldb/Target/UnixSignals.h" 25#include "lldb/Utility/LLDBAssert.h" 26#include "lldb/Utility/Log.h" 27#include "lldb/Utility/Status.h" 28#include "lldb/Utility/Stream.h" 29 30using namespace lldb; 31using namespace lldb_private; 32using namespace lldb_private::process_darwin; 33 34// Routine mach_exception_raise 35extern "C" kern_return_t 36catch_mach_exception_raise(mach_port_t exception_port, mach_port_t thread, 37 mach_port_t task, exception_type_t exception, 38 mach_exception_data_t code, 39 mach_msg_type_number_t codeCnt); 40 41extern "C" kern_return_t catch_mach_exception_raise_state( 42 mach_port_t exception_port, exception_type_t exception, 43 const mach_exception_data_t code, mach_msg_type_number_t codeCnt, 44 int *flavor, const thread_state_t old_state, 45 mach_msg_type_number_t old_stateCnt, thread_state_t new_state, 46 mach_msg_type_number_t *new_stateCnt); 47 48// Routine mach_exception_raise_state_identity 49extern "C" kern_return_t catch_mach_exception_raise_state_identity( 50 mach_port_t exception_port, mach_port_t thread, mach_port_t task, 51 exception_type_t exception, mach_exception_data_t code, 52 mach_msg_type_number_t codeCnt, int *flavor, thread_state_t old_state, 53 mach_msg_type_number_t old_stateCnt, thread_state_t new_state, 54 mach_msg_type_number_t *new_stateCnt); 55 56extern "C" boolean_t mach_exc_server(mach_msg_header_t *InHeadP, 57 mach_msg_header_t *OutHeadP); 58 59static MachException::Data *g_message = NULL; 60 61extern "C" kern_return_t catch_mach_exception_raise_state( 62 mach_port_t exc_port, exception_type_t exc_type, 63 const mach_exception_data_t exc_data, mach_msg_type_number_t exc_data_count, 64 int *flavor, const thread_state_t old_state, 65 mach_msg_type_number_t old_stateCnt, thread_state_t new_state, 66 mach_msg_type_number_t *new_stateCnt) { 67 // TODO change to LIBLLDB_LOG_EXCEPTION 68 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); 69 if (log) { 70 LLDB_LOGF(log, 71 "::%s(exc_port = 0x%4.4x, exc_type = %d (%s), " 72 "exc_data = 0x%llx, exc_data_count = %d)", 73 __FUNCTION__, exc_port, exc_type, MachException::Name(exc_type), 74 (uint64_t)exc_data, exc_data_count); 75 } 76 return KERN_FAILURE; 77} 78 79extern "C" kern_return_t catch_mach_exception_raise_state_identity( 80 mach_port_t exc_port, mach_port_t thread_port, mach_port_t task_port, 81 exception_type_t exc_type, mach_exception_data_t exc_data, 82 mach_msg_type_number_t exc_data_count, int *flavor, 83 thread_state_t old_state, mach_msg_type_number_t old_stateCnt, 84 thread_state_t new_state, mach_msg_type_number_t *new_stateCnt) { 85 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); 86 if (log) { 87 LLDB_LOGF(log, 88 "::%s(exc_port = 0x%4.4x, thd_port = 0x%4.4x, " 89 "tsk_port = 0x%4.4x, exc_type = %d (%s), exc_data[%d] = " 90 "{ 0x%llx, 0x%llx })", 91 __FUNCTION__, exc_port, thread_port, task_port, exc_type, 92 MachException::Name(exc_type), exc_data_count, 93 (uint64_t)(exc_data_count > 0 ? exc_data[0] : 0xBADDBADD), 94 (uint64_t)(exc_data_count > 1 ? exc_data[1] : 0xBADDBADD)); 95 } 96 97 return KERN_FAILURE; 98} 99 100extern "C" kern_return_t 101catch_mach_exception_raise(mach_port_t exc_port, mach_port_t thread_port, 102 mach_port_t task_port, exception_type_t exc_type, 103 mach_exception_data_t exc_data, 104 mach_msg_type_number_t exc_data_count) { 105 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); 106 if (log) { 107 LLDB_LOGF(log, 108 "::%s(exc_port = 0x%4.4x, thd_port = 0x%4.4x, " 109 "tsk_port = 0x%4.4x, exc_type = %d (%s), exc_data[%d] " 110 "= { 0x%llx, 0x%llx })", 111 __FUNCTION__, exc_port, thread_port, task_port, exc_type, 112 MachException::Name(exc_type), exc_data_count, 113 (uint64_t)(exc_data_count > 0 ? exc_data[0] : 0xBADDBADD), 114 (uint64_t)(exc_data_count > 1 ? exc_data[1] : 0xBADDBADD)); 115 } 116 117 if (task_port == g_message->task_port) { 118 g_message->task_port = task_port; 119 g_message->thread_port = thread_port; 120 g_message->exc_type = exc_type; 121 g_message->exc_data.resize(exc_data_count); 122 ::memcpy(&g_message->exc_data[0], exc_data, 123 g_message->exc_data.size() * sizeof(mach_exception_data_type_t)); 124 return KERN_SUCCESS; 125 } 126 return KERN_FAILURE; 127} 128 129bool MachException::Data::GetStopInfo(struct ThreadStopInfo *stop_info, 130 const UnixSignals &signals, 131 Stream &stream) const { 132 if (!stop_info) 133 return false; 134 135 // Zero out the structure. 136 memset(stop_info, 0, sizeof(struct ThreadStopInfo)); 137 138 if (exc_type == 0) { 139 stop_info->reason = eStopReasonInvalid; 140 return true; 141 } 142 143 // We always stop with a mach exception. 144 stop_info->reason = eStopReasonException; 145 // Save the EXC_XXXX exception type. 146 stop_info->details.exception.type = exc_type; 147 148 // Fill in a text description 149 const char *exc_name = MachException::Name(exc_type); 150 if (exc_name) 151 stream.Printf("%s", exc_name); 152 else 153 stream.Printf("%i", exc_type); 154 155 stop_info->details.exception.data_count = exc_data.size(); 156 157 int soft_signal = SoftSignal(); 158 if (soft_signal) { 159 const char *sig_str = signals.GetSignalAsCString(soft_signal); 160 stream.Printf(" EXC_SOFT_SIGNAL( %i ( %s ))", soft_signal, 161 sig_str ? sig_str : "unknown signal"); 162 } else { 163 // No special disassembly for exception data, just print it. 164 size_t idx; 165 stream.Printf(" data[%llu] = {", 166 (uint64_t)stop_info->details.exception.data_count); 167 168 for (idx = 0; idx < stop_info->details.exception.data_count; ++idx) { 169 stream.Printf( 170 "0x%llx%c", (uint64_t)exc_data[idx], 171 ((idx + 1 == stop_info->details.exception.data_count) ? '}' : ',')); 172 } 173 } 174 175 // Copy the exception data 176 for (size_t i = 0; i < stop_info->details.exception.data_count; i++) 177 stop_info->details.exception.data[i] = exc_data[i]; 178 179 return true; 180} 181 182Status MachException::Message::Receive(mach_port_t port, 183 mach_msg_option_t options, 184 mach_msg_timeout_t timeout, 185 mach_port_t notify_port) { 186 Status error; 187 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); 188 189 mach_msg_timeout_t mach_msg_timeout = 190 options & MACH_RCV_TIMEOUT ? timeout : 0; 191 if (log && ((options & MACH_RCV_TIMEOUT) == 0)) { 192 // Dump this log message if we have no timeout in case it never returns 193 LLDB_LOGF(log, 194 "::mach_msg(msg->{bits = %#x, size = %u remote_port = %#x, " 195 "local_port = %#x, reserved = 0x%x, id = 0x%x}, " 196 "option = %#x, send_size = 0, rcv_size = %llu, " 197 "rcv_name = %#x, timeout = %u, notify = %#x)", 198 exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size, 199 exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port, 200 exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id, options, 201 (uint64_t)sizeof(exc_msg.data), port, mach_msg_timeout, 202 notify_port); 203 } 204 205 mach_msg_return_t mach_err = 206 ::mach_msg(&exc_msg.hdr, 207 options, // options 208 0, // Send size 209 sizeof(exc_msg.data), // Receive size 210 port, // exception port to watch for 211 // exception on 212 mach_msg_timeout, // timeout in msec (obeyed only 213 // if MACH_RCV_TIMEOUT is ORed 214 // into the options parameter) 215 notify_port); 216 error.SetError(mach_err, eErrorTypeMachKernel); 217 218 // Dump any errors we get 219 if (error.Fail() && log) { 220 LLDB_LOGF(log, 221 "::mach_msg(msg->{bits = %#x, size = %u remote_port = %#x, " 222 "local_port = %#x, reserved = 0x%x, id = 0x%x}, " 223 "option = %#x, send_size = %u, rcv_size = %lu, rcv_name " 224 "= %#x, timeout = %u, notify = %#x) failed: %s", 225 exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size, 226 exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port, 227 exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id, options, 0, 228 sizeof(exc_msg.data), port, mach_msg_timeout, notify_port, 229 error.AsCString()); 230 } 231 return error; 232} 233 234void MachException::Message::Dump(Stream &stream) const { 235 stream.Printf(" exc_msg { bits = 0x%8.8x size = 0x%8.8x remote-port = " 236 "0x%8.8x local-port = 0x%8.8x reserved = 0x%8.8x id = " 237 "0x%8.8x }\n", 238 exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size, 239 exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port, 240 exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id); 241 242 stream.Printf(" reply_msg { bits = 0x%8.8x size = 0x%8.8x remote-port = " 243 "0x%8.8x local-port = 0x%8.8x reserved = 0x%8.8x id = " 244 "0x%8.8x }", 245 reply_msg.hdr.msgh_bits, reply_msg.hdr.msgh_size, 246 reply_msg.hdr.msgh_remote_port, reply_msg.hdr.msgh_local_port, 247 reply_msg.hdr.msgh_reserved, reply_msg.hdr.msgh_id); 248} 249 250bool MachException::Message::CatchExceptionRaise(task_t task) { 251 bool success = false; 252 state.task_port = task; 253 g_message = &state; 254 // The exc_server function is the MIG generated server handling function to 255 // handle messages from the kernel relating to the occurrence of an exception 256 // in a thread. Such messages are delivered to the exception port set via 257 // thread_set_exception_ports or task_set_exception_ports. When an exception 258 // occurs in a thread, the thread sends an exception message to its exception 259 // port, blocking in the kernel waiting for the receipt of a reply. The 260 // exc_server function performs all necessary argument handling for this 261 // kernel message and calls catch_exception_raise, 262 // catch_exception_raise_state or catch_exception_raise_state_identity, which 263 // should handle the exception. If the called routine returns KERN_SUCCESS, a 264 // reply message will be sent, allowing the thread to continue from the point 265 // of the exception; otherwise, no reply message is sent and the called 266 // routine must have dealt with the exception thread directly. 267 if (mach_exc_server(&exc_msg.hdr, &reply_msg.hdr)) { 268 success = true; 269 } else { 270 Log *log( 271 GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); 272 LLDB_LOGF(log, 273 "MachException::Message::%s(): mach_exc_server " 274 "returned zero...", 275 __FUNCTION__); 276 } 277 g_message = NULL; 278 return success; 279} 280 281Status MachException::Message::Reply(::pid_t inferior_pid, task_t inferior_task, 282 int signal) { 283 // Reply to the exception... 284 Status error; 285 286 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); 287 288 // If we had a soft signal, we need to update the thread first so it can 289 // continue without signaling 290 int soft_signal = state.SoftSignal(); 291 if (soft_signal) { 292 int state_pid = -1; 293 if (inferior_task == state.task_port) { 294 // This is our task, so we can update the signal to send to it 295 state_pid = inferior_pid; 296 soft_signal = signal; 297 } else { 298 auto mach_err = ::pid_for_task(state.task_port, &state_pid); 299 if (mach_err) { 300 error.SetError(mach_err, eErrorTypeMachKernel); 301 LLDB_LOGF(log, 302 "MachException::Message::%s(): pid_for_task() " 303 "failed: %s", 304 __FUNCTION__, error.AsCString()); 305 return error; 306 } 307 } 308 309 lldbassert(state_pid != -1); 310 if (state_pid != -1) { 311 errno = 0; 312 caddr_t thread_port_caddr = (caddr_t)(uintptr_t)state.thread_port; 313 if (::ptrace(PT_THUPDATE, state_pid, thread_port_caddr, soft_signal) != 0) 314 error.SetError(errno, eErrorTypePOSIX); 315 316 if (!error.Success()) { 317 LLDB_LOGF(log, 318 "::ptrace(request = PT_THUPDATE, pid = " 319 "0x%4.4x, tid = 0x%4.4x, signal = %i)", 320 state_pid, state.thread_port, soft_signal); 321 return error; 322 } 323 } 324 } 325 326 LLDB_LOGF(log, 327 "::mach_msg ( msg->{bits = %#x, size = %u, remote_port " 328 "= %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, " 329 "option = %#x, send_size = %u, rcv_size = %u, rcv_name " 330 "= %#x, timeout = %u, notify = %#x)", 331 reply_msg.hdr.msgh_bits, reply_msg.hdr.msgh_size, 332 reply_msg.hdr.msgh_remote_port, reply_msg.hdr.msgh_local_port, 333 reply_msg.hdr.msgh_reserved, reply_msg.hdr.msgh_id, 334 MACH_SEND_MSG | MACH_SEND_INTERRUPT, reply_msg.hdr.msgh_size, 0, 335 MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 336 337 auto mach_err = 338 ::mach_msg(&reply_msg.hdr, MACH_SEND_MSG | MACH_SEND_INTERRUPT, 339 reply_msg.hdr.msgh_size, 0, MACH_PORT_NULL, 340 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 341 if (mach_err) 342 error.SetError(mach_err, eErrorTypeMachKernel); 343 344 // Log our error if we have one. 345 if (error.Fail() && log) { 346 if (error.GetError() == MACH_SEND_INTERRUPTED) { 347 log->PutCString("::mach_msg() - send interrupted"); 348 // TODO: keep retrying to reply??? 349 } else if (state.task_port == inferior_task) { 350 LLDB_LOGF(log, 351 "mach_msg(): returned an error when replying " 352 "to a mach exception: error = %u (%s)", 353 error.GetError(), error.AsCString()); 354 } else { 355 LLDB_LOGF(log, "::mach_msg() - failed (child of task): %u (%s)", 356 error.GetError(), error.AsCString()); 357 } 358 } 359 360 return error; 361} 362 363#define PREV_EXC_MASK_ALL \ 364 (EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | \ 365 EXC_MASK_EMULATION | EXC_MASK_SOFTWARE | EXC_MASK_BREAKPOINT | \ 366 EXC_MASK_SYSCALL | EXC_MASK_MACH_SYSCALL | EXC_MASK_RPC_ALERT | \ 367 EXC_MASK_MACHINE) 368 369// Don't listen for EXC_RESOURCE, it should really get handled by the system 370// handler. 371 372#ifndef EXC_RESOURCE 373#define EXC_RESOURCE 11 374#endif 375 376#ifndef EXC_MASK_RESOURCE 377#define EXC_MASK_RESOURCE (1 << EXC_RESOURCE) 378#endif 379 380#define LLDB_EXC_MASK (EXC_MASK_ALL & ~EXC_MASK_RESOURCE) 381 382Status MachException::PortInfo::Save(task_t task) { 383 Status error; 384 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); 385 386 LLDB_LOGF(log, "MachException::PortInfo::%s(task = 0x%4.4x)", __FUNCTION__, 387 task); 388 389 // Be careful to be able to have debugserver built on a newer OS than what it 390 // is currently running on by being able to start with all exceptions and 391 // back off to just what is supported on the current system 392 mask = LLDB_EXC_MASK; 393 394 count = (sizeof(ports) / sizeof(ports[0])); 395 auto mach_err = ::task_get_exception_ports(task, mask, masks, &count, ports, 396 behaviors, flavors); 397 if (mach_err) 398 error.SetError(mach_err, eErrorTypeMachKernel); 399 400 if (log) { 401 if (error.Success()) { 402 LLDB_LOGF(log, 403 "::task_get_exception_ports(task = 0x%4.4x, mask = " 404 "0x%x, maskCnt => %u, ports, behaviors, flavors)", 405 task, mask, count); 406 } else { 407 LLDB_LOGF(log, 408 "::task_get_exception_ports(task = 0x%4.4x, mask = 0x%x, " 409 "maskCnt => %u, ports, behaviors, flavors) error: %u (%s)", 410 task, mask, count, error.GetError(), error.AsCString()); 411 } 412 } 413 414 if ((error.GetError() == KERN_INVALID_ARGUMENT) && 415 (mask != PREV_EXC_MASK_ALL)) { 416 mask = PREV_EXC_MASK_ALL; 417 count = (sizeof(ports) / sizeof(ports[0])); 418 mach_err = ::task_get_exception_ports(task, mask, masks, &count, ports, 419 behaviors, flavors); 420 error.SetError(mach_err, eErrorTypeMachKernel); 421 if (log) { 422 if (error.Success()) { 423 LLDB_LOGF(log, 424 "::task_get_exception_ports(task = 0x%4.4x, " 425 "mask = 0x%x, maskCnt => %u, ports, behaviors, " 426 "flavors)", 427 task, mask, count); 428 } else { 429 LLDB_LOGF(log, 430 "::task_get_exception_ports(task = 0x%4.4x, mask = " 431 "0x%x, maskCnt => %u, ports, behaviors, flavors) " 432 "error: %u (%s)", 433 task, mask, count, error.GetError(), error.AsCString()); 434 } 435 } 436 } 437 if (error.Fail()) { 438 mask = 0; 439 count = 0; 440 } 441 return error; 442} 443 444Status MachException::PortInfo::Restore(task_t task) { 445 Status error; 446 447 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); 448 449 LLDB_LOGF(log, "MachException::PortInfo::Restore(task = 0x%4.4x)", task); 450 451 uint32_t i = 0; 452 if (count > 0) { 453 for (i = 0; i < count; i++) { 454 auto mach_err = ::task_set_exception_ports(task, masks[i], ports[i], 455 behaviors[i], flavors[i]); 456 if (mach_err) 457 error.SetError(mach_err, eErrorTypeMachKernel); 458 if (log) { 459 if (error.Success()) { 460 LLDB_LOGF(log, 461 "::task_set_exception_ports(task = 0x%4.4x, " 462 "exception_mask = 0x%8.8x, new_port = 0x%4.4x, " 463 "behavior = 0x%8.8x, new_flavor = 0x%8.8x)", 464 task, masks[i], ports[i], behaviors[i], flavors[i]); 465 } else { 466 LLDB_LOGF(log, 467 "::task_set_exception_ports(task = 0x%4.4x, " 468 "exception_mask = 0x%8.8x, new_port = 0x%4.4x, " 469 "behavior = 0x%8.8x, new_flavor = 0x%8.8x): " 470 "error %u (%s)", 471 task, masks[i], ports[i], behaviors[i], flavors[i], 472 error.GetError(), error.AsCString()); 473 } 474 } 475 476 // Bail if we encounter any errors 477 if (error.Fail()) 478 break; 479 } 480 } 481 482 count = 0; 483 return error; 484} 485 486const char *MachException::Name(exception_type_t exc_type) { 487 switch (exc_type) { 488 case EXC_BAD_ACCESS: 489 return "EXC_BAD_ACCESS"; 490 case EXC_BAD_INSTRUCTION: 491 return "EXC_BAD_INSTRUCTION"; 492 case EXC_ARITHMETIC: 493 return "EXC_ARITHMETIC"; 494 case EXC_EMULATION: 495 return "EXC_EMULATION"; 496 case EXC_SOFTWARE: 497 return "EXC_SOFTWARE"; 498 case EXC_BREAKPOINT: 499 return "EXC_BREAKPOINT"; 500 case EXC_SYSCALL: 501 return "EXC_SYSCALL"; 502 case EXC_MACH_SYSCALL: 503 return "EXC_MACH_SYSCALL"; 504 case EXC_RPC_ALERT: 505 return "EXC_RPC_ALERT"; 506#ifdef EXC_CRASH 507 case EXC_CRASH: 508 return "EXC_CRASH"; 509#endif 510 default: 511 break; 512 } 513 return NULL; 514} 515