(root)/
glibc-2.38/
hurd/
sigunwind.c
       1  /* longjmp cleanup function for unwinding past signal handlers.
       2     Copyright (C) 1995-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <hurd.h>
      20  #include <thread_state.h>
      21  #include <jmpbuf-unwind.h>
      22  #include <assert.h>
      23  #include <stdint.h>
      24  #include <pointer_guard.h>
      25  
      26  /* _hurd_setup_sighandler puts a link on the `active resources' chain so that
      27     _longjmp_unwind will call this function with the `struct sigcontext *'
      28     describing the context interrupted by the signal, when `longjmp' is jumping
      29     to an environment that unwinds past the interrupted frame.  */
      30  
      31  void
      32  _hurdsig_longjmp_from_handler (void *data, jmp_buf env, int val)
      33  {
      34    struct sigcontext *scp = data;
      35    struct hurd_sigstate *ss = _hurd_self_sigstate ();
      36    int onstack;
      37    inline void cleanup (void)
      38      {
      39        /* Destroy the MiG reply port used by the signal handler, and restore
      40  	 the reply port in use by the thread when interrupted.  */
      41        mach_port_t reply_port = THREAD_GETMEM (THREAD_SELF, reply_port);
      42        /* Assigning MACH_PORT_DEAD here tells libc's mig_get_reply_port not to
      43           get another reply port, but avoids mig_dealloc_reply_port trying to
      44           deallocate it after the receive fails (which it will, because the
      45           reply port will be bogus, regardless).  */
      46        THREAD_SETMEM (THREAD_SELF, reply_port, MACH_PORT_DEAD);
      47        if (MACH_PORT_VALID (reply_port))
      48          __mach_port_mod_refs (__mach_task_self (), reply_port,
      49                                MACH_PORT_RIGHT_RECEIVE, -1);
      50        if (scp->sc_reply_port)
      51          __mach_port_mod_refs (__mach_task_self (), scp->sc_reply_port,
      52                                MACH_PORT_RIGHT_RECEIVE, -1);
      53      }
      54  
      55    __spin_lock (&ss->lock);
      56    /* We should only ever be called from _longjmp_unwind (in jmp-unwind.c),
      57       which calls us inside a critical section.  */
      58    assert (__spin_lock_locked (&ss->critical_section_lock));
      59    /* Are we on the alternate signal stack now?  */
      60    onstack = (ss->sigaltstack.ss_flags & SS_ONSTACK);
      61    __spin_unlock (&ss->lock);
      62  
      63    if (onstack && ! scp->sc_onstack)
      64      {
      65        /* We are unwinding off the signal stack.  We must use sigreturn to
      66  	 do it robustly.  Mutate the sigcontext so that when sigreturn
      67  	 resumes from that context, it will be as if `__longjmp (ENV, VAL)'
      68  	 were done.  */
      69  
      70        struct hurd_userlink *link;
      71  
      72        inline uintptr_t demangle_ptr (uintptr_t x)
      73  	{
      74  	  PTR_DEMANGLE (x);
      75  	  return x;
      76  	}
      77  
      78        /* Continue _longjmp_unwind's job of running the unwind
      79  	 forms for frames being unwound, since we will not
      80  	 return to its loop like this one, which called us.  */
      81        for (link = ss->active_resources;
      82  	   link && _JMPBUF_UNWINDS (env[0].__jmpbuf, link, demangle_ptr);
      83  	   link = link->thread.next)
      84  	if (_hurd_userlink_unlink (link))
      85  	  {
      86  	    if (link->cleanup == &_hurdsig_longjmp_from_handler)
      87  	      {
      88  		/* We are unwinding past another signal handler invocation.
      89  		   Just finish the cleanup for this (inner) one, and then
      90  		   swap SCP to restore to the outer context.  */
      91  		cleanup ();
      92  		scp = link->cleanup_data;
      93  	      }
      94  	    else
      95  	      (*link->cleanup) (link->cleanup_data, env, val);
      96  	  }
      97  
      98  #define sc_machine_thread_state paste(sc_,machine_thread_state)
      99  #define paste(a,b)	paste1(a,b)
     100  #define paste1(a,b)	a##b
     101  
     102        /* There are no more unwind forms to be run!
     103  	 Now we can just have the sigreturn do the longjmp for us.  */
     104        _hurd_longjmp_thread_state
     105  	((struct machine_thread_state *) &scp->sc_machine_thread_state,
     106  	 env, val);
     107  
     108        /* Restore to the same current signal mask.  If sigsetjmp saved the
     109  	 mask, longjmp has already restored it as desired; if not, we
     110  	 should leave it as it is.  */
     111        scp->sc_mask = ss->blocked;
     112  
     113        /* sigreturn expects the link added by _hurd_setup_sighandler
     114  	 to still be there, but _longjmp_unwind removed it just before
     115  	 calling us.  Put it back now so sigreturn can find it.  */
     116        link = (void *) &scp[1];
     117        assert (! link->resource.next && ! link->resource.prevp);
     118        assert (link->thread.next == ss->active_resources);
     119        assert (link->thread.prevp == &ss->active_resources);
     120        if (link->thread.next)
     121  	link->thread.next->thread.prevp = &link->thread.next;
     122        ss->active_resources = link;
     123  
     124        /* We must momentarily exit the critical section so that sigreturn
     125  	 does not get upset with us.  But we don't want signal handlers
     126  	 running right now, because we are presently in the bogus state of
     127  	 having run all the unwind forms back to ENV's frame, but our SP is
     128  	 still inside those unwound frames.  */
     129        __spin_lock (&ss->lock);
     130        __spin_unlock (&ss->critical_section_lock);
     131        ss->blocked = ~(sigset_t) 0 & ~_SIG_CANT_MASK;
     132        __spin_unlock (&ss->lock);
     133  
     134        /* Restore to the modified signal context that now
     135  	 performs `longjmp (ENV, VAL)'.  */
     136        __sigreturn (scp);
     137        assert (! "sigreturn returned!");
     138      }
     139  
     140    /* We are not unwinding off the alternate signal stack.  So nothing
     141       really funny is going on here.  We can just clean up this handler
     142       frame and let _longjmp_unwind continue unwinding.  */
     143    cleanup ();
     144    ss->intr_port = scp->sc_intr_port;
     145  }