1 /* Handle faults in the signal thread.
2    Copyright (C) 1994-2022 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 <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 */
weak_alias(_hurdsig_fault_preemptor,_hurdsig_fault_preempter)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.exc_subcode)
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
_hurdsig_fault_catch_exception_raise_state(mach_port_t port,exception_type_t exception,exception_data_t code,mach_msg_type_number_t codeCnt,int * flavor,thread_state_t old_state,mach_msg_type_number_t old_stateCnt,thread_state_t new_state,mach_msg_type_number_t * new_stateCnt)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
_hurdsig_fault_catch_exception_raise_state_identity(mach_port_t exception_port,thread_t thread,task_t task,exception_type_t exception,exception_data_t code,mach_msg_type_number_t codeCnt,int * flavor,thread_state_t old_state,mach_msg_type_number_t old_stateCnt,thread_state_t new_state,mach_msg_type_number_t * new_stateCnt)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
faulted(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
_hurdsig_fault_init(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_FIX_NEW (&state);
208   MACHINE_THREAD_STATE_SET_PC (&state, faulted);
209   MACHINE_THREAD_STATE_SET_SP (&state, faultstack, sizeof faultstack);
210 
211   err = __USEPORT
212     (PROC,
213      __proc_handle_exceptions (port,
214 			       sigexc,
215 			       forward_sigexc, MACH_MSG_TYPE_MAKE_SEND,
216 			       MACHINE_THREAD_STATE_FLAVOR,
217 			       (natural_t *) &state,
218 			       MACHINE_THREAD_STATE_COUNT));
219   assert_perror (err);
220 
221   /* Direct signal thread exceptions to the proc server.  */
222 #ifdef THREAD_EXCEPTION_PORT
223   err = __thread_set_special_port (_hurd_msgport_thread,
224 				   THREAD_EXCEPTION_PORT, sigexc);
225 #elif defined (EXC_MASK_ALL)
226   __thread_set_exception_ports (_hurd_msgport_thread,
227 				EXC_MASK_ALL & ~(EXC_MASK_SYSCALL
228 						 | EXC_MASK_MACH_SYSCALL
229 						 | EXC_MASK_RPC_ALERT),
230 				sigexc,
231 				EXCEPTION_STATE_IDENTITY,
232 				MACHINE_THREAD_STATE);
233 #else
234 # error thread_set_exception_ports?
235 #endif
236   __mach_port_deallocate (__mach_task_self (), sigexc);
237   assert_perror (err);
238 }
239