lh | 9ed821d | 2023-04-07 01:36:19 -0700 | [diff] [blame^] | 1 | /* longjmp cleanup function for unwinding past signal handlers. |
| 2 | Copyright (C) 1995-2015 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 | <http://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 | |
| 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 = |
| 42 | (mach_port_t *) __hurd_threadvar_location (_HURD_THREADVAR_MIG_REPLY); |
| 43 | if (*reply_port) |
| 44 | { |
| 45 | mach_port_t port = *reply_port; |
| 46 | /* Assigning MACH_PORT_DEAD here tells libc's mig_get_reply_port |
| 47 | not to get another reply port, but avoids mig_dealloc_reply_port |
| 48 | trying to deallocate it after the receive fails (which it will, |
| 49 | because the reply port will be bogus, regardless). */ |
| 50 | *reply_port = MACH_PORT_DEAD; |
| 51 | __mach_port_destroy (__mach_task_self (), port); |
| 52 | } |
| 53 | *reply_port = scp->sc_reply_port; |
| 54 | } |
| 55 | |
| 56 | __spin_lock (&ss->lock); |
| 57 | /* We should only ever be called from _longjmp_unwind (in jmp-unwind.c), |
| 58 | which calls us inside a critical section. */ |
| 59 | assert (__spin_lock_locked (&ss->critical_section_lock)); |
| 60 | /* Are we on the alternate signal stack now? */ |
| 61 | onstack = (ss->sigaltstack.ss_flags & SS_ONSTACK); |
| 62 | __spin_unlock (&ss->lock); |
| 63 | |
| 64 | if (onstack && ! scp->sc_onstack) |
| 65 | { |
| 66 | /* We are unwinding off the signal stack. We must use sigreturn to |
| 67 | do it robustly. Mutate the sigcontext so that when sigreturn |
| 68 | resumes from that context, it will be as if `__longjmp (ENV, VAL)' |
| 69 | were done. */ |
| 70 | |
| 71 | struct hurd_userlink *link; |
| 72 | |
| 73 | inline uintptr_t demangle_ptr (uintptr_t x) |
| 74 | { |
| 75 | # ifdef PTR_DEMANGLE |
| 76 | PTR_DEMANGLE (x); |
| 77 | # endif |
| 78 | return x; |
| 79 | } |
| 80 | |
| 81 | /* Continue _longjmp_unwind's job of running the unwind |
| 82 | forms for frames being unwound, since we will not |
| 83 | return to its loop like this one, which called us. */ |
| 84 | for (link = ss->active_resources; |
| 85 | link && _JMPBUF_UNWINDS (env[0].__jmpbuf, link, demangle_ptr); |
| 86 | link = link->thread.next) |
| 87 | if (_hurd_userlink_unlink (link)) |
| 88 | { |
| 89 | if (link->cleanup == &_hurdsig_longjmp_from_handler) |
| 90 | { |
| 91 | /* We are unwinding past another signal handler invocation. |
| 92 | Just finish the cleanup for this (inner) one, and then |
| 93 | swap SCP to restore to the outer context. */ |
| 94 | cleanup (); |
| 95 | scp = link->cleanup_data; |
| 96 | } |
| 97 | else |
| 98 | (*link->cleanup) (link->cleanup_data, env, val); |
| 99 | } |
| 100 | |
| 101 | #define sc_machine_thread_state paste(sc_,machine_thread_state) |
| 102 | #define paste(a,b) paste1(a,b) |
| 103 | #define paste1(a,b) a##b |
| 104 | |
| 105 | /* There are no more unwind forms to be run! |
| 106 | Now we can just have the sigreturn do the longjmp for us. */ |
| 107 | _hurd_longjmp_thread_state |
| 108 | ((struct machine_thread_state *) &scp->sc_machine_thread_state, |
| 109 | env, val); |
| 110 | |
| 111 | /* Restore to the same current signal mask. If sigsetjmp saved the |
| 112 | mask, longjmp has already restored it as desired; if not, we |
| 113 | should leave it as it is. */ |
| 114 | scp->sc_mask = ss->blocked; |
| 115 | |
| 116 | /* sigreturn expects the link added by _hurd_setup_sighandler |
| 117 | to still be there, but _longjmp_unwind removed it just before |
| 118 | calling us. Put it back now so sigreturn can find it. */ |
| 119 | link = (void *) &scp[1]; |
| 120 | assert (! link->resource.next && ! link->resource.prevp); |
| 121 | assert (link->thread.next == ss->active_resources); |
| 122 | assert (link->thread.prevp == &ss->active_resources); |
| 123 | if (link->thread.next) |
| 124 | link->thread.next->thread.prevp = &link->thread.next; |
| 125 | ss->active_resources = link; |
| 126 | |
| 127 | /* We must momentarily exit the critical section so that sigreturn |
| 128 | does not get upset with us. But we don't want signal handlers |
| 129 | running right now, because we are presently in the bogus state of |
| 130 | having run all the unwind forms back to ENV's frame, but our SP is |
| 131 | still inside those unwound frames. */ |
| 132 | __spin_lock (&ss->lock); |
| 133 | __spin_unlock (&ss->critical_section_lock); |
| 134 | ss->blocked = ~(sigset_t) 0 & ~_SIG_CANT_MASK; |
| 135 | __spin_unlock (&ss->lock); |
| 136 | |
| 137 | /* Restore to the modified signal context that now |
| 138 | performs `longjmp (ENV, VAL)'. */ |
| 139 | __sigreturn (scp); |
| 140 | assert (! "sigreturn returned!"); |
| 141 | } |
| 142 | |
| 143 | /* We are not unwinding off the alternate signal stack. So nothing |
| 144 | really funny is going on here. We can just clean up this handler |
| 145 | frame and let _longjmp_unwind continue unwinding. */ |
| 146 | cleanup (); |
| 147 | ss->intr_port = scp->sc_intr_port; |
| 148 | } |