1 /* $NetBSD: mach_exception.c,v 1.13 2008/04/28 20:23:44 martin Exp $ */
2
3 /*-
4 * Copyright (c) 2003 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Emmanuel Dreyfus
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
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.
18 *
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.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: mach_exception.c,v 1.13 2008/04/28 20:23:44 martin Exp $");
34
35 #include "opt_compat_darwin.h"
36
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <sys/signal.h>
40 #include <sys/proc.h>
41 #include <sys/malloc.h>
42
43 #ifdef COMPAT_DARWIN
44 #include <compat/darwin/darwin_exec.h>
45 #endif
46
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>
55
56 #include <machine/mach_machdep.h>
57
58 static void mach_siginfo_to_exception(const struct ksiginfo *, int *);
59
60 /*
61 * Exception handler
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.
69 */
70 void
71 mach_trapsignal(struct lwp *l, struct ksiginfo *ksi)
72 {
73 if (mach_trapsignal1(l, ksi) != 0)
74 trapsignal(l, ksi);
75 return;
76 }
77
78 int
79 mach_trapsignal1(struct lwp *l, struct ksiginfo *ksi)
80 {
81 struct proc *p = l->l_proc;
82 struct mach_emuldata *med;
83 int exc_no;
84 int code[2];
85
86 /* Don't inhinbit non maskable signals */
87 if (sigprop[ksi->ksi_signo] & SA_CANTMASK)
88 return EINVAL;
89
90 med = (struct mach_emuldata *)p->p_emuldata;
91
92 switch (ksi->ksi_signo) {
93 case SIGILL:
94 exc_no = MACH_EXC_BAD_INSTRUCTION;
95 break;
96 case SIGFPE:
97 exc_no = MACH_EXC_ARITHMETIC;
98 break;
99 case SIGSEGV:
100 case SIGBUS:
101 exc_no = MACH_EXC_BAD_ACCESS;
102 break;
103 case SIGTRAP:
104 exc_no = MACH_EXC_BREAKPOINT;
105 break;
106 default: /* SIGCHLD, SIGPOLL */
107 return EINVAL;
108 break;
109 }
110
111 mach_siginfo_to_exception(ksi, code);
112
113 return mach_exception(l, exc_no, code);
114 }
115
116 int
117 mach_exception(exc_l, exc, code)
118 struct lwp *exc_l; /* currently running lwp */
119 int exc;
120 int *code;
121 {
122 int behavior, flavor;
123 mach_msg_header_t *msgh;
124 size_t msglen;
125 struct mach_right *exc_mr;
126 struct mach_emuldata *exc_med;
127 struct mach_lwp_emuldata *exc_mle;
128 struct mach_emuldata *catcher_med;
129 struct mach_right *kernel_mr;
130 struct lwp *catcher_l; /* The lwp catching the exception */
131 struct mach_right *exc_task;
132 struct mach_right *exc_thread;
133 struct mach_port *exc_port;
134 struct mach_exc_info *mei;
135 int error = 0;
136
137 #ifdef DIAGNOSTIC
138 if (exc_l == NULL) {
139 printf("mach_exception: exc_l = %p\n", exc_l);
140 return ESRCH;
141 }
142 #endif
143 #ifdef DEBUG_MACH
144 printf("mach_exception: %d.%d, exc %d, code (%d, %d)\n",
145 exc_l->l_proc->p_pid, exc_l->l_lid, exc, code[0], code[1]);
146 #endif
147
148 /*
149 * It's extremely useful to have the ability of catching
150 * the process at the time it dies.
151 */
152 if (mach_exception_hang) {
153 struct proc *p = exc_l->l_proc;
154
155 sigminusset(&contsigmask, &exc_l->l_sigpendset->sp_set);
156 lwp_lock(exc_l);
157 p->p_pptr->p_nstopchild++;
158 p->p_stat = SSTOP;
159 exc_l->l_stat = LSSTOP;
160 p->p_nrlwps--;
161 KERNEL_UNLOCK_ALL(exc_l, &exc_l->l_biglocks);
162 mi_switch(exc_l);
163 KERNEL_LOCK(exc_l->l_biglocks, exc_l);
164 }
165
166 /*
167 * No exception if there is no exception port or if it has no receiver
168 */
169 exc_mle = exc_l->l_emuldata;
170 exc_med = exc_l->l_proc->p_emuldata;
171 if ((exc_port = exc_med->med_exc[exc]) == NULL)
172 return EINVAL;
173
174 MACH_PORT_REF(exc_port);
175 if (exc_port->mp_recv == NULL) {
176 error = EINVAL;
177 goto out;
178 }
179
180 #ifdef DEBUG_MACH
181 printf("catcher is %d.%d, state %d\n",
182 exc_port->mp_recv->mr_lwp->l_proc->p_pid,
183 exc_port->mp_recv->mr_lwp->l_lid,
184 exc_port->mp_recv->mr_lwp->l_proc->p_stat);
185 #endif
186 /*
187 * Don't send exceptions to dying processes
188 */
189 if (P_ZOMBIE(exc_port->mp_recv->mr_lwp->l_proc)) {
190 error = ESRCH;
191 goto out;
192 }
193
194 /*
195 * XXX Avoid a nasty deadlock because process in TX state
196 * (traced and suspended) are invulnerable to kill -9.
197 *
198 * The scenario:
199 * - the parent gets Child's signals though Mach exceptions
200 * - the parent is killed. Before calling the emulation hook
201 * mach_exit(), it will wait for the child
202 * - the child receives SIGHUP, which is turned into a Mach
203 * exception. The child sleeps awaiting for the parent
204 * to tell it to continue.
205 * For some reason I do not understand, it goes in the
206 * suspended state instead of the sleeping state.
207 * - Parents waits for the child, child is suspended, we
208 * are stuck.
209 *
210 * By preventing exception to traced processes with
211 * a dying parent, a signal is sent instead of the
212 * notification, this fixes the problem.
213 */
214 if ((exc_l->l_proc->p_slflag & PSL_TRACED) &&
215 (exc_l->l_proc->p_pptr->p_sflag & PS_WEXIT)) {
216 #ifdef DEBUG_MACH
217 printf("mach_exception: deadlock avoided\n");
218 #endif
219 error = EINVAL;
220 goto out;
221 }
222
223 if (exc_port->mp_datatype != MACH_MP_EXC_INFO) {
224 #ifdef DIAGNOSTIC
225 printf("mach_exception: unexpected datatype");
226 #endif
227 error = EINVAL;
228 goto out;
229 }
230 mei = exc_port->mp_data;
231 behavior = mei->mei_behavior;
232 flavor = mei->mei_flavor;
233
234 /*
235 * We want the port names in the target process, that is,
236 * the process with receive right for exc_port.
237 */
238 catcher_l = exc_port->mp_recv->mr_lwp;
239 catcher_med = catcher_l->l_proc->p_emuldata;
240 exc_mr = mach_right_get(exc_port, catcher_l, MACH_PORT_TYPE_SEND, 0);
241 kernel_mr = mach_right_get(catcher_med->med_kernel,
242 catcher_l, MACH_PORT_TYPE_SEND, 0);
243
244 exc_task = mach_right_get(exc_med->med_kernel,
245 catcher_l, MACH_PORT_TYPE_SEND, 0);
246 exc_thread = mach_right_get(exc_mle->mle_kernel,
247 catcher_l, MACH_PORT_TYPE_SEND, 0);
248
249 switch (behavior) {
250 case MACH_EXCEPTION_DEFAULT: {
251 mach_exception_raise_request_t *req;
252
253 req = malloc(sizeof(*req), M_EMULDATA, M_WAITOK | M_ZERO);
254 msglen = sizeof(*req);
255 msgh = (mach_msg_header_t *)req;
256
257 req->req_msgh.msgh_bits =
258 MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND) |
259 MACH_MSGH_REMOTE_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
260 req->req_msgh.msgh_size =
261 sizeof(*req) - sizeof(req->req_trailer);
262 req->req_msgh.msgh_remote_port = kernel_mr->mr_name;
263 req->req_msgh.msgh_local_port = exc_mr->mr_name;
264 req->req_msgh.msgh_id = MACH_EXC_RAISE_MSGID;
265
266 mach_add_port_desc(req, exc_thread->mr_name);
267 mach_add_port_desc(req, exc_task->mr_name);
268
269 req->req_exc = exc;
270 req->req_codecount = 2;
271 memcpy(&req->req_code[0], code, sizeof(req->req_code));
272
273 mach_set_trailer(req, msglen);
274
275 break;
276 }
277
278 case MACH_EXCEPTION_STATE: {
279 mach_exception_raise_state_request_t *req;
280 int dc;
281
282 req = malloc(sizeof(*req), M_EMULDATA, M_WAITOK | M_ZERO);
283 msglen = sizeof(*req);
284 msgh = (mach_msg_header_t *)req;
285
286 req->req_msgh.msgh_bits =
287 MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND) |
288 MACH_MSGH_REMOTE_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
289 req->req_msgh.msgh_size =
290 sizeof(*req) - sizeof(req->req_trailer);
291 req->req_msgh.msgh_remote_port = kernel_mr->mr_name;
292 req->req_msgh.msgh_local_port = exc_mr->mr_name;
293 req->req_msgh.msgh_id = MACH_EXCEPTION_STATE;
294 req->req_exc = exc;
295 req->req_codecount = 2;
296 memcpy(&req->req_code[0], code, sizeof(req->req_code));
297 req->req_flavor = flavor;
298 mach_thread_get_state_machdep(exc_l,
299 flavor, req->req_state, &dc);
300
301 msglen = msglen -
302 sizeof(req->req_state) +
303 (dc * sizeof(req->req_state[0]));
304 mach_set_trailer(req, msglen);
305
306 break;
307 }
308
309 case MACH_EXCEPTION_STATE_IDENTITY: {
310 mach_exception_raise_state_identity_request_t *req;
311 int dc;
312
313 req = malloc(sizeof(*req), M_EMULDATA, M_WAITOK | M_ZERO);
314 msglen = sizeof(*req);
315 msgh = (mach_msg_header_t *)req;
316
317 req->req_msgh.msgh_bits =
318 MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND) |
319 MACH_MSGH_REMOTE_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
320 req->req_msgh.msgh_size =
321 sizeof(*req) - sizeof(req->req_trailer);
322 req->req_msgh.msgh_remote_port = kernel_mr->mr_name;
323 req->req_msgh.msgh_local_port = exc_mr->mr_name;
324 req->req_msgh.msgh_id = MACH_EXC_RAISE_STATE_IDENTITY_MSGID;
325 req->req_body.msgh_descriptor_count = 2;
326
327 mach_add_port_desc(req, exc_thread->mr_name);
328 mach_add_port_desc(req, exc_task->mr_name);
329
330 req->req_exc = exc;
331 req->req_codecount = 2;
332 memcpy(&req->req_code[0], code, sizeof(req->req_code));
333 req->req_flavor = flavor;
334 mach_thread_get_state_machdep(exc_l,
335 flavor, req->req_state, &dc);
336
337 msglen = msglen -
338 sizeof(req->req_state) +
339 (dc * sizeof(req->req_state[0]));
340
341 mach_set_trailer(req, msglen);
342
343 break;
344 }
345
346 default:
347 printf("unknown exception bevahior %d\n", behavior);
348 error = EINVAL;
349 goto out;
350 break;
351 }
352
353 mach_set_trailer(msgh, msglen);
354
355 /*
356 * Once an exception is sent on the exception port,
357 * no new exception will be taken until the catcher
358 * acknowledge the first one.
359 */
360 rw_enter(&catcher_med->med_exclock, RW_WRITER);
361
362 /*
363 * If the catcher died, we are done.
364 */
365 if (((exc_port = exc_med->med_exc[exc]) == NULL) ||
366 (exc_port->mp_recv == NULL) ||
367 (P_ZOMBIE(exc_port->mp_recv->mr_lwp->l_proc))) {
368 error = ESRCH;
369 goto out;
370 }
371
372 (void)mach_message_get(msgh, msglen, exc_port, NULL);
373 wakeup(exc_port->mp_recv->mr_sethead);
374
375 /*
376 * The thread that caused the exception is now
377 * supposed to wait for a reply to its message.
378 */
379 #ifdef DEBUG_MACH
380 printf("mach_exception: %d.%d sleep on catcher_med->med_exclock = %p\n",
381 exc_l->l_proc->p_pid, exc_l->l_lid, &catcher_med->med_exclock);
382 #endif
383 error = tsleep(&catcher_med->med_exclock, PZERO, "mach_exc", 0);
384 #ifdef DEBUG_MACH
385 printf("mach_exception: %d.%d resumed, error = %d\n",
386 exc_l->l_proc->p_pid, exc_l->l_lid, error);
387 #endif
388
389 /*
390 * Unlock the catcher's exception handler
391 */
392 rw_exit(&catcher_med->med_exclock);
393
394 out:
395 MACH_PORT_UNREF(exc_port);
396 return error;
397 }
398
399 static void
400 mach_siginfo_to_exception(const struct ksiginfo *ksi, int *code)
401 {
402 code[1] = (long)ksi->ksi_addr;
403 switch (ksi->ksi_signo) {
404 case SIGBUS:
405 switch (ksi->ksi_code) {
406 case BUS_ADRALN:
407 code[0] = MACH_BUS_ADRALN;
408 break;
409 default:
410 printf("untranslated siginfo signo %d, code %d\n",
411 ksi->ksi_signo, ksi->ksi_code);
412 break;
413 }
414 break;
415
416 case SIGSEGV:
417 switch (ksi->ksi_code) {
418 case SEGV_MAPERR:
419 code[0] = MACH_SEGV_MAPERR;
420 break;
421 case SEGV_ACCERR:
422 code[0] = MACH_SEGV_ACCERR;
423 break;
424 default:
425 printf("untranslated siginfo signo %d, code %d\n",
426 ksi->ksi_signo, ksi->ksi_code);
427 break;
428 }
429 break;
430
431 case SIGTRAP:
432 switch (ksi->ksi_code) {
433 case TRAP_BRKPT:
434 code[0] = MACH_TRAP_BRKPT;
435 code[1] = (long)ksi->ksi_addr;
436 break;
437 default:
438 printf("untranslated siginfo signo %d, code %d\n",
439 ksi->ksi_signo, ksi->ksi_code);
440 break;
441 }
442 break;
443
444 case SIGILL:
445 switch (ksi->ksi_code) {
446 case ILL_ILLOPC:
447 case ILL_ILLOPN:
448 case ILL_ILLADR:
449 code[0] = MACH_ILL_ILLOPC;
450 break;
451 case ILL_PRVOPC:
452 case ILL_PRVREG:
453 code[0] = MACH_ILL_PRVOPC;
454 break;
455 case ILL_ILLTRP:
456 code[0] = MACH_ILL_ILLTRP;
457 break;
458 default:
459 printf("untranslated siginfo signo %d, code %d\n",
460 ksi->ksi_signo, ksi->ksi_code);
461 break;
462 }
463 break;
464
465 default:
466 printf("untranslated siginfo signo %d, code %d\n",
467 ksi->ksi_signo, ksi->ksi_code);
468 break;
469 }
470 }
471
472 int
473 mach_exception_raise(struct mach_trap_args *args)
474 {
475 struct lwp *l = args->l;
476 mach_exception_raise_reply_t *rep;
477 struct mach_emuldata *med;
478
479 /*
480 * No typo here: the reply is in the sent message.
481 * The kernel is acting as a client that gets the
482 * reply message to its exception message.
483 */
484 rep = args->smsg;
485
486 /*
487 * This message is sent by the process catching the
488 * exception to release the process that raised the exception.
489 * We wake it up if the return value is 0 (no error), else
490 * we should ignore this message.
491 */
492 #ifdef DEBUG_MACH
493 printf("mach_excpetion_raise: retval = %ld\n", (long)rep->rep_retval);
494 #endif
495 if (rep->rep_retval != 0)
496 return 0;
497
498 med = l->l_proc->p_emuldata;
499
500 /*
501 * Check for unexpected exception acknowledge, whereas
502 * the kernel sent no exception message.
503 */
504 if (!rw_lock_held(&med->med_exclock)) {
505 #ifdef DEBUG_MACH
506 printf("spurious mach_exception_raise\n");
507 #endif
508 return mach_msg_error(args, EINVAL);
509 }
510
511 /*
512 * Wakeup the thread that raised the exception.
513 */
514 #ifdef DEBUG_MACH
515 printf("mach_exception_raise: wakeup at %p\n", &med->med_exclock);
516 #endif
517 wakeup(&med->med_exclock);
518
519 return 0;
520 }
521
522 int
523 mach_exception_raise_state(struct mach_trap_args *args)
524 {
525 return mach_exception_raise(args);
526 }
527
528 int
529 mach_exception_raise_state_identity(struct mach_trap_args *args)
530 {
531 return mach_exception_raise(args);
532 }
Cache object: 2c335f814aa0d9f916a49a6672f562ee
|