Rizin
unix-like reverse engineering framework and cli tools
xnu_excthreads.c File Reference
#include "xnu_debug.h"
#include "xnu_threads.h"

Go to the source code of this file.

Functions

RZ_IPI bool xnu_restore_exception_ports (int pid)
 
static void encode_reply (mig_reply_error_t *reply, mach_msg_header_t *hdr, int code)
 
static bool validate_mach_message (RzDebug *dbg, exc_msg *msg)
 
static bool handle_dead_notify (RzDebug *dbg, exc_msg *msg)
 
static int handle_exception_message (RzDebug *dbg, exc_msg *msg, int *ret_code, bool quiet_signal)
 
RZ_IPI RzDebugReasonType xnu_wait_for_exception (RzDebug *dbg, int pid, ut32 timeout_ms, bool quiet_signal)
 
RZ_IPI bool xnu_create_exception_thread (RzDebug *dbg)
 

Variables

static xnu_exception_info ex = { { 0 } }
 

Function Documentation

◆ encode_reply()

static void encode_reply ( mig_reply_error_t *  reply,
mach_msg_header_t *  hdr,
int  code 
)
static

Definition at line 249 of file xnu_excthreads.c.

249  {
250  mach_msg_header_t *rh = &reply->Head;
251  rh->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(hdr->msgh_bits), 0);
252  rh->msgh_remote_port = hdr->msgh_remote_port;
253  rh->msgh_size = (mach_msg_size_t)sizeof(mig_reply_error_t);
254  rh->msgh_local_port = MACH_PORT_NULL;
255  rh->msgh_id = hdr->msgh_id + 100;
256  reply->NDR = NDR_record;
257  reply->RetCode = code;
258 }
const char * code
Definition: pal.c:98

References code.

Referenced by xnu_wait_for_exception().

◆ handle_dead_notify()

static bool handle_dead_notify ( RzDebug dbg,
exc_msg msg 
)
static

Definition at line 314 of file xnu_excthreads.c.

314  {
315  if (msg->hdr.msgh_id == 0x48) {
316  dbg->pid = -1;
317  return true;
318  }
319  return false;
320 }
RzDebug * dbg
Definition: desil.c:30
static struct sockaddr static addrlen static backlog const void msg
Definition: sfsocketcall.h:119

References dbg, msg, and rz_debug_t::pid.

Referenced by xnu_wait_for_exception().

◆ handle_exception_message()

static int handle_exception_message ( RzDebug dbg,
exc_msg msg,
int ret_code,
bool  quiet_signal 
)
static

Definition at line 322 of file xnu_excthreads.c.

322  {
323  int ret = RZ_DEBUG_REASON_UNKNOWN;
324  kern_return_t kr;
325  *ret_code = KERN_SUCCESS;
326  ut64 code = (ut64)msg->code[0] | ((ut64)msg->code[1] << 32);
327  ut64 subcode = (ut64)msg->code[2] | ((ut64)msg->code[3] << 32);
328  switch (msg->exception) {
329  case EXC_BAD_ACCESS:
331  *ret_code = KERN_FAILURE;
332  kr = task_suspend(msg->task.name);
333  if (kr != KERN_SUCCESS) {
334  eprintf("failed to suspend task bad access\n");
335  }
336  eprintf("EXC_BAD_ACCESS\n");
337  break;
338  case EXC_BAD_INSTRUCTION:
340  *ret_code = KERN_FAILURE;
341  kr = task_suspend(msg->task.name);
342  if (kr != KERN_SUCCESS) {
343  eprintf("failed to suspend task bad instruction\n");
344  }
345  eprintf("EXC_BAD_INSTRUCTION\n");
346  break;
347  case EXC_ARITHMETIC:
348  eprintf("EXC_ARITHMETIC\n");
349  break;
350  case EXC_EMULATION:
351  eprintf("EXC_EMULATION\n");
352  break;
353  case EXC_SOFTWARE:
354  // TODO: make these eprintfs RZ_LOG_INFO
355  // Right now we can't because the default log level is < info and the info about the
356  // signal is important to the user.
357  if (!quiet_signal) {
358  eprintf("EXC_SOFTWARE: ");
359  }
360  if (code == EXC_SOFT_SIGNAL) {
361  // standard unix signal
363  dbg->reason.signum = subcode;
364  if (!quiet_signal) {
365  eprintf(" EXC_SOFT_SIGNAL %" PFMT64u, subcode);
366  const char *signame = rz_signal_to_string((int)subcode);
367  if (signame) {
368  eprintf(" = %s", signame);
369  }
370  eprintf("\n");
371  }
372  } else {
373  eprintf("code = 0x%" PFMT64u ", subcode = 0x%" PFMT64u "\n", code, subcode);
374  }
375  // We want to stop and examine when getting signals
376  kr = task_suspend(msg->task.name);
377  if (kr != KERN_SUCCESS) {
378  RZ_LOG_ERROR("Failed to suspend after EXC_SOFTWARE");
379  }
380  break;
381  case EXC_BREAKPOINT:
382  kr = task_suspend(msg->task.name);
383  if (kr != KERN_SUCCESS) {
384  eprintf("failed to suspend task breakpoint\n");
385  }
387  break;
388  default:
389  eprintf("UNKNOWN\n");
390  break;
391  }
392  kr = mach_port_deallocate(mach_task_self(), msg->task.name);
393  if (kr != KERN_SUCCESS) {
394  eprintf("failed to deallocate task port\n");
395  }
396  kr = mach_port_deallocate(mach_task_self(), msg->thread.name);
397  if (kr != KERN_SUCCESS) {
398  eprintf("failed to deallocated task port\n");
399  }
400  return ret;
401 }
#define eprintf(x, y...)
Definition: rlcc.c:7
@ RZ_DEBUG_REASON_ILLEGAL
Definition: rz_debug.h:102
@ RZ_DEBUG_REASON_UNKNOWN
Definition: rz_debug.h:103
@ RZ_DEBUG_REASON_BREAKPOINT
Definition: rz_debug.h:94
@ RZ_DEBUG_REASON_SEGFAULT
Definition: rz_debug.h:93
@ RZ_DEBUG_REASON_SIGNAL
Definition: rz_debug.h:92
#define RZ_LOG_ERROR(fmtstr,...)
Definition: rz_log.h:58
RZ_API const char * rz_signal_to_string(int code)
Definition: signal.c:63
#define PFMT64u
Definition: rz_types.h:395
Definition: inftree9.h:24
RzDebugReason reason
Definition: rz_debug.h:276
ut64(WINAPI *w32_GetEnabledXStateFeatures)()

References dbg, eprintf, msg, PFMT64u, rz_debug_t::reason, RZ_DEBUG_REASON_BREAKPOINT, RZ_DEBUG_REASON_ILLEGAL, RZ_DEBUG_REASON_SEGFAULT, RZ_DEBUG_REASON_SIGNAL, RZ_DEBUG_REASON_UNKNOWN, RZ_LOG_ERROR, rz_signal_to_string(), rz_debug_reason_t::signum, and ut64().

Referenced by xnu_wait_for_exception().

◆ validate_mach_message()

static bool validate_mach_message ( RzDebug dbg,
exc_msg msg 
)
static

Definition at line 260 of file xnu_excthreads.c.

260  {
261  kern_return_t kr;
262 #if __POWERPC__
263  return false;
264 #else
265  /*check if the message is for us*/
266  if (msg->hdr.msgh_local_port != ex.exception_port) {
267  return false;
268  }
269  /*gdb from apple check this so why not us*/
270  if (!(msg->hdr.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
271  return false;
272  }
273  /*Mach exception we are interested*/
274  // XXX for i386 this id seems to be different
275  if (msg->hdr.msgh_id > 2405 || msg->hdr.msgh_id < 2401) {
276  return false;
277  }
278  /* check descriptors. */
279  if (msg->hdr.msgh_size <
280  sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t) +
281  2 * sizeof(mach_msg_port_descriptor_t) +
282  sizeof(NDR_record_t) + sizeof(exception_type_t) +
283  sizeof(mach_msg_type_number_t) +
284  sizeof(mach_exception_data_t))
285  return false;
286  /* check data representation. */
287  if (msg->NDR.mig_vers != NDR_PROTOCOL_2_0 ||
288  msg->NDR.if_vers != NDR_PROTOCOL_2_0 ||
289  msg->NDR.mig_encoding != NDR_record.mig_encoding ||
290  msg->NDR.int_rep != NDR_record.int_rep ||
291  msg->NDR.char_rep != NDR_record.char_rep ||
292  msg->NDR.float_rep != NDR_record.float_rep) {
293  return false;
294  }
295  if (pid_to_task(dbg->pid) != msg->task.name) {
296  // we receive a exception from an unknown process this could
297  // happen if the child fork, as the created process will inherit
298  // its exception port
299  /*we got new rights to the task, get rid of it.*/
300  kr = mach_port_deallocate(mach_task_self(), msg->task.name);
301  if (kr != KERN_SUCCESS) {
302  eprintf("validate_mach_message: failed to deallocate task port\n");
303  }
304  kr = mach_port_deallocate(mach_task_self(), msg->thread.name);
305  if (kr != KERN_SUCCESS) {
306  eprintf("validate_mach_message2: failed to deallocated task port\n");
307  }
308  return false;
309  }
310  return true;
311 #endif
312 }
mach_port_t exception_port
Definition: xnu_threads.h:46
task_t pid_to_task(int pid)
Definition: xnu_debug.c:498
static xnu_exception_info ex

References dbg, eprintf, ex, _exception_info::exception_port, msg, rz_debug_t::pid, and pid_to_task().

Referenced by xnu_wait_for_exception().

◆ xnu_create_exception_thread()

RZ_IPI bool xnu_create_exception_thread ( RzDebug dbg)

Definition at line 479 of file xnu_excthreads.c.

479  {
480 #if __POWERPC__
481  return false;
482 #else
483  kern_return_t kr;
484  mach_port_t exception_port = MACH_PORT_NULL;
485  mach_port_t req_port;
486  // Got the Mach port for the current process
487  mach_port_t task_self = mach_task_self();
488  task_t task = pid_to_task(dbg->pid);
489  if (!task) {
490  eprintf("error to get task for the debuggee process"
491  " xnu_start_exception_thread\n");
492  return false;
493  }
494  if (!MACH_PORT_VALID(task_self)) {
495  eprintf("error to get the task for the current process"
496  " xnu_start_exception_thread\n");
497  return false;
498  }
499  // Allocate an exception port that we will use to track our child process
500  kr = mach_port_allocate(task_self, MACH_PORT_RIGHT_RECEIVE,
501  &exception_port);
502  RETURN_ON_MACH_ERROR("error to allocate mach_port exception\n", false);
503  // Add the ability to send messages on the new exception port
504  kr = mach_port_insert_right(task_self, exception_port, exception_port,
505  MACH_MSG_TYPE_MAKE_SEND);
506  RETURN_ON_MACH_ERROR("error to allocate insert right\n", false);
507  // Atomically swap out (and save) the child process's exception ports
508  // for the one we just created. We'll want to receive all exceptions.
509  ex.count = (sizeof(ex.ports) / sizeof(*ex.ports));
510  kr = task_swap_exception_ports(task, EXC_MASK_ALL, exception_port,
511  EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE,
513  RETURN_ON_MACH_ERROR("failed to swap exception ports\n", false);
514  // get notification when process die
515  kr = mach_port_request_notification(task_self, task, MACH_NOTIFY_DEAD_NAME,
516  0, exception_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &req_port);
517  if (kr != KERN_SUCCESS) {
518  eprintf("Termination notification request failed\n");
519  }
520  ex.exception_port = exception_port;
521  return true;
522 #endif
523 }
mach_msg_type_number_t count
Definition: xnu_threads.h:44
mach_port_t ports[EXC_TYPES_COUNT]
Definition: xnu_threads.h:41
thread_state_flavor_t flavors[EXC_TYPES_COUNT]
Definition: xnu_threads.h:43
exception_mask_t masks[EXC_TYPES_COUNT]
Definition: xnu_threads.h:40
exception_behavior_t behaviors[EXC_TYPES_COUNT]
Definition: xnu_threads.h:42
#define RETURN_ON_MACH_ERROR(msg, retval)
Definition: xnu_threads.h:33

References _exception_info::behaviors, _exception_info::count, dbg, eprintf, ex, _exception_info::exception_port, _exception_info::flavors, _exception_info::masks, rz_debug_t::pid, pid_to_task(), _exception_info::ports, and RETURN_ON_MACH_ERROR.

Referenced by xnu_attach().

◆ xnu_restore_exception_ports()

RZ_IPI bool xnu_restore_exception_ports ( int  pid)

Definition at line 226 of file xnu_excthreads.c.

226  {
227  kern_return_t kr;
228  int i;
229  task_t task = pid_to_task(pid);
230  if (!task)
231  return false;
232  for (i = 0; i < ex.count; i++) {
233  kr = task_set_exception_ports(task, ex.masks[i], ex.ports[i],
234  ex.behaviors[i], ex.flavors[i]);
235  if (kr != KERN_SUCCESS) {
236  eprintf("fail to restore exception ports\n");
237  return false;
238  }
239  }
240  kr = mach_port_deallocate(mach_task_self(), ex.exception_port);
241  if (kr != KERN_SUCCESS) {
242  eprintf("failed to deallocate exception port\n");
243  return false;
244  }
245  return true;
246 }
lzma_index ** i
Definition: index.h:629
static static fork const void static count static fd const char const char static newpath char char char static envp time_t static t const char static mode static whence const char static dir time_t static t unsigned static seconds const char struct utimbuf static buf static inc pid
Definition: sflib.h:64

References _exception_info::behaviors, _exception_info::count, eprintf, ex, _exception_info::exception_port, _exception_info::flavors, i, _exception_info::masks, pid, pid_to_task(), and _exception_info::ports.

Referenced by xnu_detach().

◆ xnu_wait_for_exception()

RZ_IPI RzDebugReasonType xnu_wait_for_exception ( RzDebug dbg,
int  pid,
ut32  timeout_ms,
bool  quiet_signal 
)

Wait for a Mach exception, reply to it and handle it.

Parameters
timeout_msif zero, wait infinitely, otherwise specifies a timeout for receiving
quiet_signaldon't print when receiving a standard unix signal

Definition at line 409 of file xnu_excthreads.c.

409  {
411  kern_return_t kr;
412  int ret_code;
414  mig_reply_error_t reply;
415  bool ret;
416  exc_msg msg = { 0 };
417  if (!dbg) {
418  return reason;
419  }
420  msg.hdr.msgh_local_port = ex.exception_port;
421  msg.hdr.msgh_size = sizeof(exc_msg);
422  for (;;) {
423  kr = mach_msg(
424  &msg.hdr,
425  MACH_RCV_MSG | MACH_RCV_INTERRUPT | (timeout_ms ? MACH_RCV_TIMEOUT : 0), 0,
426  sizeof(exc_msg), ex.exception_port,
427  timeout_ms ? timeout_ms : MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
428  if (kr == MACH_RCV_INTERRUPTED) {
430  break;
431  } else if (kr == MACH_RCV_TIMED_OUT) {
432  RZ_LOG_ERROR("Waiting for Mach exception timed out");
433  reason = RZ_DEBUG_REASON_UNKNOWN;
434  break;
435  } else if (kr != MACH_MSG_SUCCESS) {
436  RZ_LOG_ERROR("message didn't succeeded\n");
437  break;
438  }
439  ret = validate_mach_message(dbg, &msg);
440  if (!ret) {
441  ret = handle_dead_notify(dbg, &msg);
442  if (ret) {
443  reason = RZ_DEBUG_REASON_DEAD;
444  break;
445  }
446  }
447  if (!ret) {
448  encode_reply(&reply, &msg.hdr, KERN_FAILURE);
449  kr = mach_msg(&reply.Head, MACH_SEND_MSG | MACH_SEND_INTERRUPT,
450  reply.Head.msgh_size, 0,
451  MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE,
452  MACH_PORT_NULL);
453  if (reply.Head.msgh_remote_port != 0 && kr != MACH_MSG_SUCCESS) {
454  kr = mach_port_deallocate(mach_task_self(), reply.Head.msgh_remote_port);
455  if (kr != KERN_SUCCESS) {
456  eprintf("failed to deallocate reply port\n");
457  }
458  }
459  continue;
460  }
461 
462  reason = handle_exception_message(dbg, &msg, &ret_code, quiet_signal);
463  encode_reply(&reply, &msg.hdr, ret_code);
464  kr = mach_msg(&reply.Head, MACH_SEND_MSG | MACH_SEND_INTERRUPT,
465  reply.Head.msgh_size, 0,
466  MACH_PORT_NULL, 0,
467  MACH_PORT_NULL);
468  if (reply.Head.msgh_remote_port != 0 && kr != MACH_MSG_SUCCESS) {
469  kr = mach_port_deallocate(mach_task_self(), reply.Head.msgh_remote_port);
470  if (kr != KERN_SUCCESS)
471  eprintf("failed to deallocate reply port\n");
472  }
473  break; // to avoid infinite loops
474  }
475  dbg->stopaddr = rz_debug_reg_get(dbg, "PC");
476  return reason;
477 }
RZ_API ut64 rz_debug_reg_get(RzDebug *dbg, const char *name)
Definition: dreg.c:99
#define rz_return_val_if_fail(expr, val)
Definition: rz_assert.h:108
RzDebugReasonType
Definition: rz_debug.h:89
@ RZ_DEBUG_REASON_DEAD
Definition: rz_debug.h:90
@ RZ_DEBUG_REASON_ERROR
Definition: rz_debug.h:104
ut64 stopaddr
Definition: rz_debug.h:278
#define RZ_DEBUG_REASON_MACH_RCV_INTERRUPTED
Definition: xnu_debug.h:261
static bool validate_mach_message(RzDebug *dbg, exc_msg *msg)
static bool handle_dead_notify(RzDebug *dbg, exc_msg *msg)
static int handle_exception_message(RzDebug *dbg, exc_msg *msg, int *ret_code, bool quiet_signal)
static void encode_reply(mig_reply_error_t *reply, mach_msg_header_t *hdr, int code)
struct _exc_msg exc_msg

References dbg, encode_reply(), eprintf, ex, _exception_info::exception_port, handle_dead_notify(), handle_exception_message(), msg, RZ_DEBUG_REASON_DEAD, RZ_DEBUG_REASON_ERROR, RZ_DEBUG_REASON_MACH_RCV_INTERRUPTED, RZ_DEBUG_REASON_UNKNOWN, rz_debug_reg_get(), RZ_LOG_ERROR, rz_return_val_if_fail, rz_debug_t::stopaddr, and validate_mach_message().

Referenced by xnu_attach(), and xnu_wait().

Variable Documentation

◆ ex