Blame hurd/sigunwind.c

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