xf.li | bdd93d5 | 2023-05-12 07:10:14 -0700 | [diff] [blame] | 1 | /* Handle faults in the signal thread. |
| 2 | Copyright (C) 1994-2016 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 <hurd/signal.h> |
| 21 | #include "hurdfault.h" |
| 22 | #include <errno.h> |
| 23 | #include <string.h> |
| 24 | #include <setjmp.h> |
| 25 | #include <stdio.h> |
| 26 | #include <thread_state.h> |
| 27 | #include "faultexc_server.h" /* mig-generated header for our exc server. */ |
| 28 | #include <assert.h> |
| 29 | |
| 30 | jmp_buf _hurdsig_fault_env; |
| 31 | struct hurd_signal_preemptor _hurdsig_fault_preemptor = {0}; |
| 32 | |
| 33 | /* XXX temporary to deal with spelling fix */ |
| 34 | weak_alias (_hurdsig_fault_preemptor, _hurdsig_fault_preempter) |
| 35 | |
| 36 | static mach_port_t forward_sigexc; |
| 37 | |
| 38 | kern_return_t |
| 39 | _hurdsig_fault_catch_exception_raise (mach_port_t port, |
| 40 | thread_t thread, |
| 41 | task_t task, |
| 42 | #ifdef EXC_MASK_ALL /* New interface flavor. */ |
| 43 | exception_type_t exception, |
| 44 | exception_data_t code, |
| 45 | mach_msg_type_number_t codeCnt |
| 46 | #else /* Vanilla Mach 3.0 interface. */ |
| 47 | integer_t exception, |
| 48 | integer_t code, integer_t subcode |
| 49 | #endif |
| 50 | ) |
| 51 | { |
| 52 | int signo; |
| 53 | struct hurd_signal_detail d; |
| 54 | |
| 55 | if (port != forward_sigexc || |
| 56 | thread != _hurd_msgport_thread || task != __mach_task_self ()) |
| 57 | return EPERM; /* Strange bogosity. */ |
| 58 | |
| 59 | d.exc = exception; |
| 60 | #ifdef EXC_MASK_ALL |
| 61 | assert (codeCnt >= 2); |
| 62 | d.exc_code = code[0]; |
| 63 | d.exc_subcode = code[1]; |
| 64 | #else |
| 65 | d.exc_code = code; |
| 66 | d.exc_subcode = subcode; |
| 67 | #endif |
| 68 | |
| 69 | /* Call the machine-dependent function to translate the Mach exception |
| 70 | codes into a signal number and subcode. */ |
| 71 | _hurd_exception2signal (&d, &signo); |
| 72 | |
| 73 | return HURD_PREEMPT_SIGNAL_P (&_hurdsig_fault_preemptor, signo, d.code) |
| 74 | ? 0 : EGREGIOUS; |
| 75 | } |
| 76 | |
| 77 | #ifdef EXC_MASK_ALL |
| 78 | /* XXX New interface flavor has additional RPCs that we could be using |
| 79 | instead. These RPCs roll a thread_get_state/thread_set_state into |
| 80 | the message, so the signal thread ought to use these to save some calls. |
| 81 | */ |
| 82 | kern_return_t |
| 83 | _hurdsig_fault_catch_exception_raise_state |
| 84 | (mach_port_t port, |
| 85 | exception_type_t exception, |
| 86 | exception_data_t code, |
| 87 | mach_msg_type_number_t codeCnt, |
| 88 | int *flavor, |
| 89 | thread_state_t old_state, |
| 90 | mach_msg_type_number_t old_stateCnt, |
| 91 | thread_state_t new_state, |
| 92 | mach_msg_type_number_t *new_stateCnt) |
| 93 | { |
| 94 | abort (); |
| 95 | return KERN_FAILURE; |
| 96 | } |
| 97 | |
| 98 | kern_return_t |
| 99 | _hurdsig_fault_catch_exception_raise_state_identity |
| 100 | (mach_port_t exception_port, |
| 101 | thread_t thread, |
| 102 | task_t task, |
| 103 | exception_type_t exception, |
| 104 | exception_data_t code, |
| 105 | mach_msg_type_number_t codeCnt, |
| 106 | int *flavor, |
| 107 | thread_state_t old_state, |
| 108 | mach_msg_type_number_t old_stateCnt, |
| 109 | thread_state_t new_state, |
| 110 | mach_msg_type_number_t *new_stateCnt) |
| 111 | { |
| 112 | abort (); |
| 113 | return KERN_FAILURE; |
| 114 | } |
| 115 | #endif |
| 116 | |
| 117 | |
| 118 | #ifdef NDR_CHAR_ASCII /* OSF Mach flavors have different names. */ |
| 119 | # define mig_reply_header_t mig_reply_error_t |
| 120 | #endif |
| 121 | |
| 122 | static void |
| 123 | faulted (void) |
| 124 | { |
| 125 | struct |
| 126 | { |
| 127 | mach_msg_header_t head; |
| 128 | char buf[64]; |
| 129 | } request; |
| 130 | mig_reply_header_t reply; |
| 131 | extern int _hurdsig_fault_exc_server (mach_msg_header_t *, |
| 132 | mach_msg_header_t *); |
| 133 | |
| 134 | /* Wait for the exception_raise message forwarded by the proc server. */ |
| 135 | |
| 136 | if (__mach_msg (&request.head, MACH_RCV_MSG, 0, |
| 137 | sizeof request, forward_sigexc, |
| 138 | MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL) |
| 139 | != MACH_MSG_SUCCESS) |
| 140 | __libc_fatal ("msg receive failed on signal thread exc\n"); |
| 141 | |
| 142 | /* Run the exc demuxer which should call the server function above. |
| 143 | That function returns 0 if the exception was expected. */ |
| 144 | _hurdsig_fault_exc_server (&request.head, &reply.Head); |
| 145 | if (reply.Head.msgh_remote_port != MACH_PORT_NULL) |
| 146 | __mach_msg (&reply.Head, MACH_SEND_MSG, reply.Head.msgh_size, |
| 147 | 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); |
| 148 | if (reply.RetCode == MIG_BAD_ID) |
| 149 | __mach_msg_destroy (&request.head); |
| 150 | |
| 151 | if (reply.RetCode) |
| 152 | __libc_fatal ("BUG: unexpected fault in signal thread\n"); |
| 153 | |
| 154 | _hurdsig_fault_preemptor.signals = 0; |
| 155 | longjmp (_hurdsig_fault_env, 1); |
| 156 | } |
| 157 | |
| 158 | static char faultstack[1024]; |
| 159 | |
| 160 | /* Send exceptions for the signal thread to the proc server. |
| 161 | It will forward the message on to our message port, |
| 162 | and then restore the thread's state to code which |
| 163 | does `longjmp (_hurd_sigthread_fault_env, 1)'. */ |
| 164 | |
| 165 | void |
| 166 | _hurdsig_fault_init (void) |
| 167 | { |
| 168 | error_t err; |
| 169 | struct machine_thread_state state; |
| 170 | mach_port_t sigexc; |
| 171 | |
| 172 | /* Allocate a port to receive signal thread exceptions. |
| 173 | We will move this receive right to the proc server. */ |
| 174 | err = __mach_port_allocate (__mach_task_self (), |
| 175 | MACH_PORT_RIGHT_RECEIVE, &sigexc); |
| 176 | assert_perror (err); |
| 177 | err = __mach_port_allocate (__mach_task_self (), |
| 178 | MACH_PORT_RIGHT_RECEIVE, &forward_sigexc); |
| 179 | assert_perror (err); |
| 180 | |
| 181 | /* Allocate a port to receive the exception msgs forwarded |
| 182 | from the proc server. */ |
| 183 | err = __mach_port_insert_right (__mach_task_self (), sigexc, |
| 184 | sigexc, MACH_MSG_TYPE_MAKE_SEND); |
| 185 | assert_perror (err); |
| 186 | |
| 187 | /* Set the queue limit for this port to just one. The proc server will |
| 188 | notice if we ever get a second exception while one remains queued and |
| 189 | unreceived, and decide we are hopelessly buggy. */ |
| 190 | #ifdef MACH_PORT_RECEIVE_STATUS_COUNT |
| 191 | { |
| 192 | const mach_port_limits_t lim = { mpl_qlimit: 1 }; |
| 193 | assert (MACH_PORT_RECEIVE_STATUS_COUNT == sizeof lim / sizeof (natural_t)); |
| 194 | err = __mach_port_set_attributes (__mach_task_self (), forward_sigexc, |
| 195 | MACH_PORT_RECEIVE_STATUS, |
| 196 | (mach_port_info_t) &lim, |
| 197 | MACH_PORT_RECEIVE_STATUS_COUNT); |
| 198 | } |
| 199 | #else |
| 200 | err = __mach_port_set_qlimit (__mach_task_self (), forward_sigexc, 1); |
| 201 | #endif |
| 202 | assert_perror (err); |
| 203 | |
| 204 | /* This state will be restored when we fault. |
| 205 | It runs the function above. */ |
| 206 | memset (&state, 0, sizeof state); |
| 207 | MACHINE_THREAD_STATE_SET_PC (&state, faulted); |
| 208 | MACHINE_THREAD_STATE_SET_SP (&state, faultstack, sizeof faultstack); |
| 209 | |
| 210 | err = __USEPORT |
| 211 | (PROC, |
| 212 | __proc_handle_exceptions (port, |
| 213 | sigexc, |
| 214 | forward_sigexc, MACH_MSG_TYPE_MAKE_SEND, |
| 215 | MACHINE_THREAD_STATE_FLAVOR, |
| 216 | (natural_t *) &state, |
| 217 | MACHINE_THREAD_STATE_COUNT)); |
| 218 | assert_perror (err); |
| 219 | |
| 220 | /* Direct signal thread exceptions to the proc server. */ |
| 221 | #ifdef THREAD_EXCEPTION_PORT |
| 222 | err = __thread_set_special_port (_hurd_msgport_thread, |
| 223 | THREAD_EXCEPTION_PORT, sigexc); |
| 224 | #elif defined (EXC_MASK_ALL) |
| 225 | __thread_set_exception_ports (_hurd_msgport_thread, |
| 226 | EXC_MASK_ALL & ~(EXC_MASK_SYSCALL |
| 227 | | EXC_MASK_MACH_SYSCALL |
| 228 | | EXC_MASK_RPC_ALERT), |
| 229 | sigexc, |
| 230 | EXCEPTION_STATE_IDENTITY, |
| 231 | MACHINE_THREAD_STATE); |
| 232 | #else |
| 233 | # error thread_set_exception_ports? |
| 234 | #endif |
| 235 | __mach_port_deallocate (__mach_task_self (), sigexc); |
| 236 | assert_perror (err); |
| 237 | } |