Example #1
0
/* Handle an exception by invoking the user's fault handler and/or forwarding
   the duty to the previously installed handlers.  */
kern_return_t
catch_exception_raise (mach_port_t exception_port,
                       mach_port_t thread,
                       mach_port_t task,
                       exception_type_t exception,
                       exception_data_t code,
                       mach_msg_type_number_t code_count)
{
#ifdef SIGSEGV_EXC_STATE_TYPE
  SIGSEGV_EXC_STATE_TYPE exc_state;
#endif
  SIGSEGV_THREAD_STATE_TYPE thread_state;
  mach_msg_type_number_t state_count;
  unsigned long addr;
  unsigned long sp;

#ifdef DEBUG_EXCEPTION_HANDLING
  fprintf (stderr, "Exception: 0x%x Code: 0x%x 0x%x in catch....\n",
           exception,
           code_count > 0 ? code[0] : -1,
           code_count > 1 ? code[1] : -1);
#endif

  /* See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_get_state.html.  */
#ifdef SIGSEGV_EXC_STATE_TYPE
  state_count = SIGSEGV_EXC_STATE_COUNT;
  if (thread_get_state (thread, SIGSEGV_EXC_STATE_FLAVOR,
                        (void *) &exc_state, &state_count)
      != KERN_SUCCESS)
    {
      /* The thread is supposed to be suspended while the exception handler
         is called. This shouldn't fail. */
#ifdef DEBUG_EXCEPTION_HANDLING
      fprintf (stderr, "thread_get_state failed for exception state\n");
#endif
      return KERN_FAILURE;
    }
#endif

  /* It turns out any Darwin kernel starting at 10.2 contains a "fast" path
     to determine the address of a fault: it is located into code[1].
     MacOS X exception delivery is really slow, so we also pass code
     and make getting the EXC_STATE conditional.  */
  addr = (unsigned long) (SIGSEGV_FAULT_ADDRESS (code, exc_state));

  /* It gets worse if we want to retrieve the machine registers, so we
     call the user handler before detecting if the exception is really a
     stack fault.  */
  if (call_user_handler ((void *) addr, 0))
    return KERN_SUCCESS;

  /* See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_get_state.html.  */
  state_count = SIGSEGV_THREAD_STATE_COUNT;
  if (thread_get_state (thread, SIGSEGV_THREAD_STATE_FLAVOR,
                        (void *) &thread_state, &state_count)
      != KERN_SUCCESS)
    {
      /* The thread is supposed to be suspended while the exception handler
         is called. This shouldn't fail. */
#ifdef DEBUG_EXCEPTION_HANDLING
      fprintf (stderr, "thread_get_state failed for thread state\n");
#endif
      return KERN_FAILURE;
    }

  sp = (unsigned long) (SIGSEGV_STACK_POINTER (thread_state));

  /* Got the thread's state. Now extract the address that caused the
     fault and invoke the user's handler.  */
  save_thread_state = thread_state;

  /* If the fault address is near the stack pointer, it's a stack overflow.
     Otherwise, treat it like a normal SIGSEGV.  */
  if (addr <= sp + 4096 && sp <= addr + 4096)
    {
      unsigned long new_safe_esp;
#ifdef DEBUG_EXCEPTION_HANDLING
      fprintf (stderr, "Treating as stack overflow, sp = 0x%lx\n", (char *) sp);
#endif
      new_safe_esp =
#if STACK_DIRECTION < 0
        stk_extra_stack + stk_extra_stack_size - 256;
#else
        stk_extra_stack + 256;
#endif
#ifdef __i386__
      new_safe_esp &= -16; /* align */
      new_safe_esp -= 4; /* make room for (unused) return address slot */
#endif
      SIGSEGV_STACK_POINTER (thread_state) = new_safe_esp;
      /* Continue handling this fault in the faulting thread.  (We cannot longjmp while
         in the exception handling thread, so we need to mimic what signals do!)  */
      SIGSEGV_PROGRAM_COUNTER (thread_state) = (unsigned long) altstack_handler;
    }
  else
    {
      if (call_user_handler ((void *) addr, 1))
        return KERN_SUCCESS;

      SIGSEGV_PROGRAM_COUNTER (thread_state) = (unsigned long) terminating_handler;
    }

  /* See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_set_state.html.  */
  if (thread_set_state (thread, SIGSEGV_THREAD_STATE_FLAVOR,
                        (void *) &thread_state, state_count)
      != KERN_SUCCESS)
    {
#ifdef DEBUG_EXCEPTION_HANDLING
      fprintf (stderr, "thread_set_state failed for altstack state\n");
#endif
      return KERN_FAILURE;
    }
  return KERN_SUCCESS;
}
Example #2
0
void
sigacthandler(int sig, siginfo_t *sip, void *uvp)
{
	ucontext_t *ucp = uvp;
	ulwp_t *self = curthread;

	/*
	 * Do this in case we took a signal while in a cancelable system call.
	 * It does no harm if we were not in such a system call.
	 */
	self->ul_sp = 0;
	if (sig != SIGCANCEL)
		self->ul_cancel_async = self->ul_save_async;

	/*
	 * If this thread has performed a longjmp() from a signal handler
	 * back to main level some time in the past, it has left the kernel
	 * thinking that it is still in the signal context.  We repair this
	 * possible damage by setting ucp->uc_link to NULL if we know that
	 * we are actually executing at main level (self->ul_siglink == NULL).
	 * See the code for setjmp()/longjmp() for more details.
	 */
	if (self->ul_siglink == NULL)
		ucp->uc_link = NULL;

	/*
	 * If we are not in a critical region and are
	 * not deferring signals, take the signal now.
	 */
	if ((self->ul_critical + self->ul_sigdefer) == 0) {
		call_user_handler(sig, sip, ucp);
		/*
		 * On the surface, the following call seems redundant
		 * because call_user_handler() cannot return. However,
		 * we don't want to return from here because the compiler
		 * might recycle our frame. We want to keep it on the
		 * stack to assist debuggers such as pstack in identifying
		 * signal frames. The call to thr_panic() serves to prevent
		 * tail-call optimisation here.
		 */
		thr_panic("sigacthandler(): call_user_handler() returned");
	}

	/*
	 * We are in a critical region or we are deferring signals.  When
	 * we emerge from the region we will call take_deferred_signal().
	 */
	ASSERT(self->ul_cursig == 0);
	self->ul_cursig = (char)sig;
	if (sip != NULL)
		(void) memcpy(&self->ul_siginfo,
		    sip, sizeof (siginfo_t));
	else
		self->ul_siginfo.si_signo = 0;

	/*
	 * Make sure that if we return to a call to __lwp_park()
	 * or ___lwp_cond_wait() that it returns right away
	 * (giving us a spurious wakeup but not a deadlock).
	 */
	set_parking_flag(self, 0);

	/*
	 * Return to the previous context with all signals blocked.
	 * We will restore the signal mask in take_deferred_signal().
	 * Note that we are calling the system call trap here, not
	 * the setcontext() wrapper.  We don't want to change the
	 * thread's ul_sigmask by this operation.
	 */
	ucp->uc_sigmask = maskset;
	(void) __setcontext(ucp);
	thr_panic("sigacthandler(): __setcontext() returned");
}