Blame hurd/sigunwind.c

Packit 6c4009
/* longjmp cleanup function for unwinding past signal handlers.
Packit 6c4009
   Copyright (C) 1995-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <hurd.h>
Packit 6c4009
#include <thread_state.h>
Packit 6c4009
#include <hurd/threadvar.h>
Packit 6c4009
#include <jmpbuf-unwind.h>
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <stdint.h>
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* _hurd_setup_sighandler puts a link on the `active resources' chain so that
Packit 6c4009
   _longjmp_unwind will call this function with the `struct sigcontext *'
Packit 6c4009
   describing the context interrupted by the signal, when `longjmp' is jumping
Packit 6c4009
   to an environment that unwinds past the interrupted frame.  */
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
_hurdsig_longjmp_from_handler (void *data, jmp_buf env, int val)
Packit 6c4009
{
Packit 6c4009
  struct sigcontext *scp = data;
Packit 6c4009
  struct hurd_sigstate *ss = _hurd_self_sigstate ();
Packit 6c4009
  int onstack;
Packit 6c4009
  inline void cleanup (void)
Packit 6c4009
    {
Packit 6c4009
      /* Destroy the MiG reply port used by the signal handler, and restore
Packit 6c4009
	 the reply port in use by the thread when interrupted.  */
Packit 6c4009
      mach_port_t *reply_port = &__hurd_local_reply_port;
Packit 6c4009
      if (*reply_port)
Packit 6c4009
	{
Packit 6c4009
	  mach_port_t port = *reply_port;
Packit 6c4009
	  /* Assigning MACH_PORT_DEAD here tells libc's mig_get_reply_port
Packit 6c4009
	     not to get another reply port, but avoids mig_dealloc_reply_port
Packit 6c4009
	     trying to deallocate it after the receive fails (which it will,
Packit 6c4009
	     because the reply port will be bogus, regardless).  */
Packit 6c4009
	  *reply_port = MACH_PORT_DEAD;
Packit 6c4009
	  __mach_port_destroy (__mach_task_self (), port);
Packit 6c4009
	}
Packit 6c4009
      if (scp->sc_reply_port)
Packit 6c4009
	__mach_port_destroy (__mach_task_self (), scp->sc_reply_port);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  __spin_lock (&ss->lock);
Packit 6c4009
  /* We should only ever be called from _longjmp_unwind (in jmp-unwind.c),
Packit 6c4009
     which calls us inside a critical section.  */
Packit 6c4009
  assert (__spin_lock_locked (&ss->critical_section_lock));
Packit 6c4009
  /* Are we on the alternate signal stack now?  */
Packit 6c4009
  onstack = (ss->sigaltstack.ss_flags & SS_ONSTACK);
Packit 6c4009
  __spin_unlock (&ss->lock);
Packit 6c4009
Packit 6c4009
  if (onstack && ! scp->sc_onstack)
Packit 6c4009
    {
Packit 6c4009
      /* We are unwinding off the signal stack.  We must use sigreturn to
Packit 6c4009
	 do it robustly.  Mutate the sigcontext so that when sigreturn
Packit 6c4009
	 resumes from that context, it will be as if `__longjmp (ENV, VAL)'
Packit 6c4009
	 were done.  */
Packit 6c4009
Packit 6c4009
      struct hurd_userlink *link;
Packit 6c4009
Packit 6c4009
      inline uintptr_t demangle_ptr (uintptr_t x)
Packit 6c4009
	{
Packit 6c4009
# ifdef PTR_DEMANGLE
Packit 6c4009
	  PTR_DEMANGLE (x);
Packit 6c4009
# endif
Packit 6c4009
	  return x;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* Continue _longjmp_unwind's job of running the unwind
Packit 6c4009
	 forms for frames being unwound, since we will not
Packit 6c4009
	 return to its loop like this one, which called us.  */
Packit 6c4009
      for (link = ss->active_resources;
Packit 6c4009
	   link && _JMPBUF_UNWINDS (env[0].__jmpbuf, link, demangle_ptr);
Packit 6c4009
	   link = link->thread.next)
Packit 6c4009
	if (_hurd_userlink_unlink (link))
Packit 6c4009
	  {
Packit 6c4009
	    if (link->cleanup == &_hurdsig_longjmp_from_handler)
Packit 6c4009
	      {
Packit 6c4009
		/* We are unwinding past another signal handler invocation.
Packit 6c4009
		   Just finish the cleanup for this (inner) one, and then
Packit 6c4009
		   swap SCP to restore to the outer context.  */
Packit 6c4009
		cleanup ();
Packit 6c4009
		scp = link->cleanup_data;
Packit 6c4009
	      }
Packit 6c4009
	    else
Packit 6c4009
	      (*link->cleanup) (link->cleanup_data, env, val);
Packit 6c4009
	  }
Packit 6c4009
Packit 6c4009
#define sc_machine_thread_state paste(sc_,machine_thread_state)
Packit 6c4009
#define paste(a,b)	paste1(a,b)
Packit 6c4009
#define paste1(a,b)	a##b
Packit 6c4009
Packit 6c4009
      /* There are no more unwind forms to be run!
Packit 6c4009
	 Now we can just have the sigreturn do the longjmp for us.  */
Packit 6c4009
      _hurd_longjmp_thread_state
Packit 6c4009
	((struct machine_thread_state *) &scp->sc_machine_thread_state,
Packit 6c4009
	 env, val);
Packit 6c4009
Packit 6c4009
      /* Restore to the same current signal mask.  If sigsetjmp saved the
Packit 6c4009
	 mask, longjmp has already restored it as desired; if not, we
Packit 6c4009
	 should leave it as it is.  */
Packit 6c4009
      scp->sc_mask = ss->blocked;
Packit 6c4009
Packit 6c4009
      /* sigreturn expects the link added by _hurd_setup_sighandler
Packit 6c4009
	 to still be there, but _longjmp_unwind removed it just before
Packit 6c4009
	 calling us.  Put it back now so sigreturn can find it.  */
Packit 6c4009
      link = (void *) &scp[1];
Packit 6c4009
      assert (! link->resource.next && ! link->resource.prevp);
Packit 6c4009
      assert (link->thread.next == ss->active_resources);
Packit 6c4009
      assert (link->thread.prevp == &ss->active_resources);
Packit 6c4009
      if (link->thread.next)
Packit 6c4009
	link->thread.next->thread.prevp = &link->thread.next;
Packit 6c4009
      ss->active_resources = link;
Packit 6c4009
Packit 6c4009
      /* We must momentarily exit the critical section so that sigreturn
Packit 6c4009
	 does not get upset with us.  But we don't want signal handlers
Packit 6c4009
	 running right now, because we are presently in the bogus state of
Packit 6c4009
	 having run all the unwind forms back to ENV's frame, but our SP is
Packit 6c4009
	 still inside those unwound frames.  */
Packit 6c4009
      __spin_lock (&ss->lock);
Packit 6c4009
      __spin_unlock (&ss->critical_section_lock);
Packit 6c4009
      ss->blocked = ~(sigset_t) 0 & ~_SIG_CANT_MASK;
Packit 6c4009
      __spin_unlock (&ss->lock);
Packit 6c4009
Packit 6c4009
      /* Restore to the modified signal context that now
Packit 6c4009
	 performs `longjmp (ENV, VAL)'.  */
Packit 6c4009
      __sigreturn (scp);
Packit 6c4009
      assert (! "sigreturn returned!");
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* We are not unwinding off the alternate signal stack.  So nothing
Packit 6c4009
     really funny is going on here.  We can just clean up this handler
Packit 6c4009
     frame and let _longjmp_unwind continue unwinding.  */
Packit 6c4009
  cleanup ();
Packit 6c4009
  ss->intr_port = scp->sc_intr_port;
Packit 6c4009
}