1 /* $NetBSD: mach_exception.c,v 1.13 2008/04/28 20:23:44 martin Exp $ */
4 * Copyright (c) 2003 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: mach_exception.c,v 1.13 2008/04/28 20:23:44 martin Exp $");
35 #include "opt_compat_darwin.h"
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <sys/signal.h>
41 #include <sys/malloc.h>
44 #include <compat/darwin/darwin_exec.h>
47 #include <compat/mach/mach_types.h>
48 #include <compat/mach/mach_exec.h>
49 #include <compat/mach/mach_errno.h>
50 #include <compat/mach/mach_thread.h>
51 #include <compat/mach/mach_exception.h>
52 #include <compat/mach/mach_message.h>
53 #include <compat/mach/mach_services.h>
54 #include <compat/mach/mach_sysctl.h>
56 #include <machine/mach_machdep.h>
58 static void mach_siginfo_to_exception(const struct ksiginfo
*, int *);
62 * Mach does not use signals. But systems based on Mach (e.g.: Darwin),
63 * can use both Mach exceptions and UNIX signals. In order to allow the
64 * Mach layer to intercept the exception and inhibit UNIX signals, we have
65 * mach_trapsignal1 returning an error. If it returns 0, then the
66 * exception was intercepted at the Mach level, and no signal should
67 * be produced. Else, a signal might be sent. darwin_trapinfo calls
68 * mach_trapinfo1 and handle signals if it gets a non zero return value.
71 mach_trapsignal(struct lwp
*l
, struct ksiginfo
*ksi
)
73 if (mach_trapsignal1(l
, ksi
) != 0)
79 mach_trapsignal1(struct lwp
*l
, struct ksiginfo
*ksi
)
81 struct proc
*p
= l
->l_proc
;
82 struct mach_emuldata
*med
;
86 /* Don't inhinbit non maskable signals */
87 if (sigprop
[ksi
->ksi_signo
] & SA_CANTMASK
)
90 med
= (struct mach_emuldata
*)p
->p_emuldata
;
92 switch (ksi
->ksi_signo
) {
94 exc_no
= MACH_EXC_BAD_INSTRUCTION
;
97 exc_no
= MACH_EXC_ARITHMETIC
;
101 exc_no
= MACH_EXC_BAD_ACCESS
;
104 exc_no
= MACH_EXC_BREAKPOINT
;
106 default: /* SIGCHLD, SIGPOLL */
111 mach_siginfo_to_exception(ksi
, code
);
113 return mach_exception(l
, exc_no
, code
);
117 mach_exception(struct lwp
*exc_l
, int exc
, int *code
)
118 /* exc_l: currently running lwp */
120 int behavior
, flavor
;
121 mach_msg_header_t
*msgh
;
123 struct mach_right
*exc_mr
;
124 struct mach_emuldata
*exc_med
;
125 struct mach_lwp_emuldata
*exc_mle
;
126 struct mach_emuldata
*catcher_med
;
127 struct mach_right
*kernel_mr
;
128 struct lwp
*catcher_l
; /* The lwp catching the exception */
129 struct mach_right
*exc_task
;
130 struct mach_right
*exc_thread
;
131 struct mach_port
*exc_port
;
132 struct mach_exc_info
*mei
;
137 printf("mach_exception: exc_l = %p\n", exc_l
);
142 printf("mach_exception: %d.%d, exc %d, code (%d, %d)\n",
143 exc_l
->l_proc
->p_pid
, exc_l
->l_lid
, exc
, code
[0], code
[1]);
147 * It's extremely useful to have the ability of catching
148 * the process at the time it dies.
150 if (mach_exception_hang
) {
151 struct proc
*p
= exc_l
->l_proc
;
153 sigminusset(&contsigmask
, &exc_l
->l_sigpendset
->sp_set
);
155 p
->p_pptr
->p_nstopchild
++;
157 exc_l
->l_stat
= LSSTOP
;
159 KERNEL_UNLOCK_ALL(exc_l
, &exc_l
->l_biglocks
);
161 KERNEL_LOCK(exc_l
->l_biglocks
, exc_l
);
165 * No exception if there is no exception port or if it has no receiver
167 exc_mle
= exc_l
->l_emuldata
;
168 exc_med
= exc_l
->l_proc
->p_emuldata
;
169 if ((exc_port
= exc_med
->med_exc
[exc
]) == NULL
)
172 MACH_PORT_REF(exc_port
);
173 if (exc_port
->mp_recv
== NULL
) {
179 printf("catcher is %d.%d, state %d\n",
180 exc_port
->mp_recv
->mr_lwp
->l_proc
->p_pid
,
181 exc_port
->mp_recv
->mr_lwp
->l_lid
,
182 exc_port
->mp_recv
->mr_lwp
->l_proc
->p_stat
);
185 * Don't send exceptions to dying processes
187 if (P_ZOMBIE(exc_port
->mp_recv
->mr_lwp
->l_proc
)) {
193 * XXX Avoid a nasty deadlock because process in TX state
194 * (traced and suspended) are invulnerable to kill -9.
197 * - the parent gets Child's signals though Mach exceptions
198 * - the parent is killed. Before calling the emulation hook
199 * mach_exit(), it will wait for the child
200 * - the child receives SIGHUP, which is turned into a Mach
201 * exception. The child sleeps awaiting for the parent
202 * to tell it to continue.
203 * For some reason I do not understand, it goes in the
204 * suspended state instead of the sleeping state.
205 * - Parents waits for the child, child is suspended, we
208 * By preventing exception to traced processes with
209 * a dying parent, a signal is sent instead of the
210 * notification, this fixes the problem.
212 if ((exc_l
->l_proc
->p_slflag
& PSL_TRACED
) &&
213 (exc_l
->l_proc
->p_pptr
->p_sflag
& PS_WEXIT
)) {
215 printf("mach_exception: deadlock avoided\n");
221 if (exc_port
->mp_datatype
!= MACH_MP_EXC_INFO
) {
223 printf("mach_exception: unexpected datatype");
228 mei
= exc_port
->mp_data
;
229 behavior
= mei
->mei_behavior
;
230 flavor
= mei
->mei_flavor
;
233 * We want the port names in the target process, that is,
234 * the process with receive right for exc_port.
236 catcher_l
= exc_port
->mp_recv
->mr_lwp
;
237 catcher_med
= catcher_l
->l_proc
->p_emuldata
;
238 exc_mr
= mach_right_get(exc_port
, catcher_l
, MACH_PORT_TYPE_SEND
, 0);
239 kernel_mr
= mach_right_get(catcher_med
->med_kernel
,
240 catcher_l
, MACH_PORT_TYPE_SEND
, 0);
242 exc_task
= mach_right_get(exc_med
->med_kernel
,
243 catcher_l
, MACH_PORT_TYPE_SEND
, 0);
244 exc_thread
= mach_right_get(exc_mle
->mle_kernel
,
245 catcher_l
, MACH_PORT_TYPE_SEND
, 0);
248 case MACH_EXCEPTION_DEFAULT
: {
249 mach_exception_raise_request_t
*req
;
251 req
= malloc(sizeof(*req
), M_EMULDATA
, M_WAITOK
| M_ZERO
);
252 msglen
= sizeof(*req
);
253 msgh
= (mach_msg_header_t
*)req
;
255 req
->req_msgh
.msgh_bits
=
256 MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND
) |
257 MACH_MSGH_REMOTE_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE
);
258 req
->req_msgh
.msgh_size
=
259 sizeof(*req
) - sizeof(req
->req_trailer
);
260 req
->req_msgh
.msgh_remote_port
= kernel_mr
->mr_name
;
261 req
->req_msgh
.msgh_local_port
= exc_mr
->mr_name
;
262 req
->req_msgh
.msgh_id
= MACH_EXC_RAISE_MSGID
;
264 mach_add_port_desc(req
, exc_thread
->mr_name
);
265 mach_add_port_desc(req
, exc_task
->mr_name
);
268 req
->req_codecount
= 2;
269 memcpy(&req
->req_code
[0], code
, sizeof(req
->req_code
));
271 mach_set_trailer(req
, msglen
);
276 case MACH_EXCEPTION_STATE
: {
277 mach_exception_raise_state_request_t
*req
;
280 req
= malloc(sizeof(*req
), M_EMULDATA
, M_WAITOK
| M_ZERO
);
281 msglen
= sizeof(*req
);
282 msgh
= (mach_msg_header_t
*)req
;
284 req
->req_msgh
.msgh_bits
=
285 MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND
) |
286 MACH_MSGH_REMOTE_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE
);
287 req
->req_msgh
.msgh_size
=
288 sizeof(*req
) - sizeof(req
->req_trailer
);
289 req
->req_msgh
.msgh_remote_port
= kernel_mr
->mr_name
;
290 req
->req_msgh
.msgh_local_port
= exc_mr
->mr_name
;
291 req
->req_msgh
.msgh_id
= MACH_EXCEPTION_STATE
;
293 req
->req_codecount
= 2;
294 memcpy(&req
->req_code
[0], code
, sizeof(req
->req_code
));
295 req
->req_flavor
= flavor
;
296 mach_thread_get_state_machdep(exc_l
,
297 flavor
, req
->req_state
, &dc
);
300 sizeof(req
->req_state
) +
301 (dc
* sizeof(req
->req_state
[0]));
302 mach_set_trailer(req
, msglen
);
307 case MACH_EXCEPTION_STATE_IDENTITY
: {
308 mach_exception_raise_state_identity_request_t
*req
;
311 req
= malloc(sizeof(*req
), M_EMULDATA
, M_WAITOK
| M_ZERO
);
312 msglen
= sizeof(*req
);
313 msgh
= (mach_msg_header_t
*)req
;
315 req
->req_msgh
.msgh_bits
=
316 MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND
) |
317 MACH_MSGH_REMOTE_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE
);
318 req
->req_msgh
.msgh_size
=
319 sizeof(*req
) - sizeof(req
->req_trailer
);
320 req
->req_msgh
.msgh_remote_port
= kernel_mr
->mr_name
;
321 req
->req_msgh
.msgh_local_port
= exc_mr
->mr_name
;
322 req
->req_msgh
.msgh_id
= MACH_EXC_RAISE_STATE_IDENTITY_MSGID
;
323 req
->req_body
.msgh_descriptor_count
= 2;
325 mach_add_port_desc(req
, exc_thread
->mr_name
);
326 mach_add_port_desc(req
, exc_task
->mr_name
);
329 req
->req_codecount
= 2;
330 memcpy(&req
->req_code
[0], code
, sizeof(req
->req_code
));
331 req
->req_flavor
= flavor
;
332 mach_thread_get_state_machdep(exc_l
,
333 flavor
, req
->req_state
, &dc
);
336 sizeof(req
->req_state
) +
337 (dc
* sizeof(req
->req_state
[0]));
339 mach_set_trailer(req
, msglen
);
345 printf("unknown exception bevahior %d\n", behavior
);
351 mach_set_trailer(msgh
, msglen
);
354 * Once an exception is sent on the exception port,
355 * no new exception will be taken until the catcher
356 * acknowledge the first one.
358 rw_enter(&catcher_med
->med_exclock
, RW_WRITER
);
361 * If the catcher died, we are done.
363 if (((exc_port
= exc_med
->med_exc
[exc
]) == NULL
) ||
364 (exc_port
->mp_recv
== NULL
) ||
365 (P_ZOMBIE(exc_port
->mp_recv
->mr_lwp
->l_proc
))) {
370 (void)mach_message_get(msgh
, msglen
, exc_port
, NULL
);
371 wakeup(exc_port
->mp_recv
->mr_sethead
);
374 * The thread that caused the exception is now
375 * supposed to wait for a reply to its message.
378 printf("mach_exception: %d.%d sleep on catcher_med->med_exclock = %p\n",
379 exc_l
->l_proc
->p_pid
, exc_l
->l_lid
, &catcher_med
->med_exclock
);
381 error
= tsleep(&catcher_med
->med_exclock
, PZERO
, "mach_exc", 0);
383 printf("mach_exception: %d.%d resumed, error = %d\n",
384 exc_l
->l_proc
->p_pid
, exc_l
->l_lid
, error
);
388 * Unlock the catcher's exception handler
390 rw_exit(&catcher_med
->med_exclock
);
393 MACH_PORT_UNREF(exc_port
);
398 mach_siginfo_to_exception(const struct ksiginfo
*ksi
, int *code
)
400 code
[1] = (long)ksi
->ksi_addr
;
401 switch (ksi
->ksi_signo
) {
403 switch (ksi
->ksi_code
) {
405 code
[0] = MACH_BUS_ADRALN
;
408 printf("untranslated siginfo signo %d, code %d\n",
409 ksi
->ksi_signo
, ksi
->ksi_code
);
415 switch (ksi
->ksi_code
) {
417 code
[0] = MACH_SEGV_MAPERR
;
420 code
[0] = MACH_SEGV_ACCERR
;
423 printf("untranslated siginfo signo %d, code %d\n",
424 ksi
->ksi_signo
, ksi
->ksi_code
);
430 switch (ksi
->ksi_code
) {
432 code
[0] = MACH_TRAP_BRKPT
;
433 code
[1] = (long)ksi
->ksi_addr
;
436 printf("untranslated siginfo signo %d, code %d\n",
437 ksi
->ksi_signo
, ksi
->ksi_code
);
443 switch (ksi
->ksi_code
) {
447 code
[0] = MACH_ILL_ILLOPC
;
451 code
[0] = MACH_ILL_PRVOPC
;
454 code
[0] = MACH_ILL_ILLTRP
;
457 printf("untranslated siginfo signo %d, code %d\n",
458 ksi
->ksi_signo
, ksi
->ksi_code
);
464 printf("untranslated siginfo signo %d, code %d\n",
465 ksi
->ksi_signo
, ksi
->ksi_code
);
471 mach_exception_raise(struct mach_trap_args
*args
)
473 struct lwp
*l
= args
->l
;
474 mach_exception_raise_reply_t
*rep
;
475 struct mach_emuldata
*med
;
478 * No typo here: the reply is in the sent message.
479 * The kernel is acting as a client that gets the
480 * reply message to its exception message.
485 * This message is sent by the process catching the
486 * exception to release the process that raised the exception.
487 * We wake it up if the return value is 0 (no error), else
488 * we should ignore this message.
491 printf("mach_excpetion_raise: retval = %ld\n", (long)rep
->rep_retval
);
493 if (rep
->rep_retval
!= 0)
496 med
= l
->l_proc
->p_emuldata
;
499 * Check for unexpected exception acknowledge, whereas
500 * the kernel sent no exception message.
502 if (!rw_lock_held(&med
->med_exclock
)) {
504 printf("spurious mach_exception_raise\n");
506 return mach_msg_error(args
, EINVAL
);
510 * Wakeup the thread that raised the exception.
513 printf("mach_exception_raise: wakeup at %p\n", &med
->med_exclock
);
515 wakeup(&med
->med_exclock
);
521 mach_exception_raise_state(struct mach_trap_args
*args
)
523 return mach_exception_raise(args
);
527 mach_exception_raise_state_identity(struct mach_trap_args
*args
)
529 return mach_exception_raise(args
);