1 /* $NetBSD: mach_exception.c,v 1.8 2006/03/07 03:32:06 thorpej 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 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <sys/cdefs.h>
40 __KERNEL_RCSID(0, "$NetBSD: mach_exception.c,v 1.8 2006/03/07 03:32:06 thorpej Exp $");
41
42 #include "opt_compat_darwin.h"
43
44 #include <sys/types.h>
45 #include <sys/param.h>
46 #include <sys/signal.h>
47 #include <sys/proc.h>
48 #include <sys/malloc.h>
49
50 #ifdef COMPAT_DARWIN
51 #include <compat/darwin/darwin_exec.h>
52 #endif
53
54 #include <compat/mach/mach_types.h>
55 #include <compat/mach/mach_exec.h>
56 #include <compat/mach/mach_errno.h>
57 #include <compat/mach/mach_thread.h>
58 #include <compat/mach/mach_exception.h>
59 #include <compat/mach/mach_message.h>
60 #include <compat/mach/mach_services.h>
61 #include <compat/mach/mach_sysctl.h>
62
63 #include <machine/mach_machdep.h>
64
65 static void mach_siginfo_to_exception(const struct ksiginfo *, int *);
66
67 /*
68 * Exception handler
69 * Mach does not use signals. But systems based on Mach (e.g.: Darwin),
70 * can use both Mach exceptions and UNIX signals. In order to allow the
71 * Mach layer to intercept the exception and inhibit UNIX signals, we have
72 * mach_trapsignal1 returning an error. If it returns 0, then the
73 * exception was intercepted at the Mach level, and no signal should
74 * be produced. Else, a signal might be sent. darwin_trapinfo calls
75 * mach_trapinfo1 and handle signals if it gets a non zero return value.
76 */
77 void
78 mach_trapsignal(l, ksi)
79 struct lwp *l;
80 const struct ksiginfo *ksi;
81 {
82 if (mach_trapsignal1(l, ksi) != 0)
83 trapsignal(l, ksi);
84 return;
85 }
86
87 int
88 mach_trapsignal1(l, ksi)
89 struct lwp *l;
90 const struct ksiginfo *ksi;
91 {
92 struct proc *p = l->l_proc;
93 struct mach_emuldata *med;
94 int exc_no;
95 int code[2];
96
97 /* Don't inhinbit non maskable signals */
98 if (sigprop[ksi->ksi_signo] & SA_CANTMASK)
99 return EINVAL;
100
101 med = (struct mach_emuldata *)p->p_emuldata;
102
103 switch (ksi->ksi_signo) {
104 case SIGILL:
105 exc_no = MACH_EXC_BAD_INSTRUCTION;
106 break;
107 case SIGFPE:
108 exc_no = MACH_EXC_ARITHMETIC;
109 break;
110 case SIGSEGV:
111 case SIGBUS:
112 exc_no = MACH_EXC_BAD_ACCESS;
113 break;
114 case SIGTRAP:
115 exc_no = MACH_EXC_BREAKPOINT;
116 break;
117 default: /* SIGCHLD, SIGPOLL */
118 return EINVAL;
119 break;
120 }
121
122 mach_siginfo_to_exception(ksi, code);
123
124 return mach_exception(l, exc_no, code);
125 }
126
127 int
128 mach_exception(exc_l, exc, code)
129 struct lwp *exc_l; /* currently running lwp */
130 int exc;
131 int *code;
132 {
133 int behavior, flavor;
134 mach_msg_header_t *msgh;
135 size_t msglen;
136 struct mach_right *exc_mr;
137 struct mach_emuldata *exc_med;
138 struct mach_lwp_emuldata *exc_mle;
139 struct mach_emuldata *catcher_med;
140 struct mach_right *kernel_mr;
141 struct lwp *catcher_l; /* The lwp catching the exception */
142 struct mach_right *exc_task;
143 struct mach_right *exc_thread;
144 struct mach_port *exc_port;
145 struct mach_exc_info *mei;
146 int error = 0;
147
148 #ifdef DIAGNOSTIC
149 if (exc_l == NULL) {
150 printf("mach_exception: exc_l = %p\n", exc_l);
151 return ESRCH;
152 }
153 #endif
154 #ifdef DEBUG_MACH
155 printf("mach_exception: %d.%d, exc %d, code (%d, %d)\n",
156 exc_l->l_proc->p_pid, exc_l->l_lid, exc, code[0], code[1]);
157 #endif
158
159 /*
160 * It's extremely useful to have the ability of catching
161 * the process at the time it dies.
162 */
163 if (mach_exception_hang) {
164 int s;
165 struct proc *p = exc_l->l_proc;
166
167 sigminusset(&contsigmask, &p->p_sigctx.ps_siglist);
168 SCHED_LOCK(s);
169 p->p_pptr->p_nstopchild++;
170 p->p_stat = SSTOP;
171 exc_l->l_stat = LSSTOP;
172 p->p_nrlwps--;
173 mi_switch(exc_l, NULL);
174 SCHED_ASSERT_UNLOCKED();
175 splx(s);
176 }
177
178 /*
179 * No exception if there is no exception port or if it has no receiver
180 */
181 exc_mle = exc_l->l_emuldata;
182 exc_med = exc_l->l_proc->p_emuldata;
183 if ((exc_port = exc_med->med_exc[exc]) == NULL)
184 return EINVAL;
185
186 MACH_PORT_REF(exc_port);
187 if (exc_port->mp_recv == NULL) {
188 error = EINVAL;
189 goto out;
190 }
191
192 #ifdef DEBUG_MACH
193 printf("catcher is %d.%d, state %d\n",
194 exc_port->mp_recv->mr_lwp->l_proc->p_pid,
195 exc_port->mp_recv->mr_lwp->l_lid,
196 exc_port->mp_recv->mr_lwp->l_proc->p_stat);
197 #endif
198 /*
199 * Don't send exceptions to dying processes
200 */
201 if (P_ZOMBIE(exc_port->mp_recv->mr_lwp->l_proc)) {
202 error = ESRCH;
203 goto out;
204 }
205
206 /*
207 * XXX Avoid a nasty deadlock because process in TX state
208 * (traced and suspended) are invulnerable to kill -9.
209 *
210 * The scenario:
211 * - the parent gets Child's signals though Mach exceptions
212 * - the parent is killed. Before calling the emulation hook
213 * mach_exit(), it will wait for the child
214 * - the child receives SIGHUP, which is turned into a Mach
215 * exception. The child sleeps awaiting for the parent
216 * to tell it to continue.
217 * For some reason I do not understand, it goes in the
218 * suspended state instead of the sleeping state.
219 * - Parents waits for the child, child is suspended, we
220 * are stuck.
221 *
222 * By preventing exception to traced processes with
223 * a dying parent, a signal is sent instead of the
224 * notification, this fixes the problem.
225 */
226 if ((exc_l->l_proc->p_flag & P_TRACED) &&
227 (exc_l->l_proc->p_pptr->p_flag & P_WEXIT)) {
228 #ifdef DEBUG_MACH
229 printf("mach_exception: deadlock avoided\n");
230 #endif
231 error = EINVAL;
232 goto out;
233 }
234
235 if (exc_port->mp_datatype != MACH_MP_EXC_INFO) {
236 #ifdef DIAGNOSTIC
237 printf("mach_exception: unexpected datatype");
238 #endif
239 error = EINVAL;
240 goto out;
241 }
242 mei = exc_port->mp_data;
243 behavior = mei->mei_behavior;
244 flavor = mei->mei_flavor;
245
246 /*
247 * We want the port names in the target process, that is,
248 * the process with receive right for exc_port.
249 */
250 catcher_l = exc_port->mp_recv->mr_lwp;
251 catcher_med = catcher_l->l_proc->p_emuldata;
252 exc_mr = mach_right_get(exc_port, catcher_l, MACH_PORT_TYPE_SEND, 0);
253 kernel_mr = mach_right_get(catcher_med->med_kernel,
254 catcher_l, MACH_PORT_TYPE_SEND, 0);
255
256 exc_task = mach_right_get(exc_med->med_kernel,
257 catcher_l, MACH_PORT_TYPE_SEND, 0);
258 exc_thread = mach_right_get(exc_mle->mle_kernel,
259 catcher_l, MACH_PORT_TYPE_SEND, 0);
260
261 switch (behavior) {
262 case MACH_EXCEPTION_DEFAULT: {
263 mach_exception_raise_request_t *req;
264
265 req = malloc(sizeof(*req), M_EMULDATA, M_WAITOK | M_ZERO);
266 msglen = sizeof(*req);
267 msgh = (mach_msg_header_t *)req;
268
269 req->req_msgh.msgh_bits =
270 MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND) |
271 MACH_MSGH_REMOTE_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
272 req->req_msgh.msgh_size =
273 sizeof(*req) - sizeof(req->req_trailer);
274 req->req_msgh.msgh_remote_port = kernel_mr->mr_name;
275 req->req_msgh.msgh_local_port = exc_mr->mr_name;
276 req->req_msgh.msgh_id = MACH_EXC_RAISE_MSGID;
277
278 mach_add_port_desc(req, exc_thread->mr_name);
279 mach_add_port_desc(req, exc_task->mr_name);
280
281 req->req_exc = exc;
282 req->req_codecount = 2;
283 memcpy(&req->req_code[0], code, sizeof(req->req_code));
284
285 mach_set_trailer(req, msglen);
286
287 break;
288 }
289
290 case MACH_EXCEPTION_STATE: {
291 mach_exception_raise_state_request_t *req;
292 int dc;
293
294 req = malloc(sizeof(*req), M_EMULDATA, M_WAITOK | M_ZERO);
295 msglen = sizeof(*req);
296 msgh = (mach_msg_header_t *)req;
297
298 req->req_msgh.msgh_bits =
299 MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND) |
300 MACH_MSGH_REMOTE_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
301 req->req_msgh.msgh_size =
302 sizeof(*req) - sizeof(req->req_trailer);
303 req->req_msgh.msgh_remote_port = kernel_mr->mr_name;
304 req->req_msgh.msgh_local_port = exc_mr->mr_name;
305 req->req_msgh.msgh_id = MACH_EXCEPTION_STATE;
306 req->req_exc = exc;
307 req->req_codecount = 2;
308 memcpy(&req->req_code[0], code, sizeof(req->req_code));
309 req->req_flavor = flavor;
310 mach_thread_get_state_machdep(exc_l,
311 flavor, req->req_state, &dc);
312
313 msglen = msglen -
314 sizeof(req->req_state) +
315 (dc * sizeof(req->req_state[0]));
316 mach_set_trailer(req, msglen);
317
318 break;
319 }
320
321 case MACH_EXCEPTION_STATE_IDENTITY: {
322 mach_exception_raise_state_identity_request_t *req;
323 int dc;
324
325 req = malloc(sizeof(*req), M_EMULDATA, M_WAITOK | M_ZERO);
326 msglen = sizeof(*req);
327 msgh = (mach_msg_header_t *)req;
328
329 req->req_msgh.msgh_bits =
330 MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND) |
331 MACH_MSGH_REMOTE_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
332 req->req_msgh.msgh_size =
333 sizeof(*req) - sizeof(req->req_trailer);
334 req->req_msgh.msgh_remote_port = kernel_mr->mr_name;
335 req->req_msgh.msgh_local_port = exc_mr->mr_name;
336 req->req_msgh.msgh_id = MACH_EXC_RAISE_STATE_IDENTITY_MSGID;
337 req->req_body.msgh_descriptor_count = 2;
338
339 mach_add_port_desc(req, exc_thread->mr_name);
340 mach_add_port_desc(req, exc_task->mr_name);
341
342 req->req_exc = exc;
343 req->req_codecount = 2;
344 memcpy(&req->req_code[0], code, sizeof(req->req_code));
345 req->req_flavor = flavor;
346 mach_thread_get_state_machdep(exc_l,
347 flavor, req->req_state, &dc);
348
349 msglen = msglen -
350 sizeof(req->req_state) +
351 (dc * sizeof(req->req_state[0]));
352
353 mach_set_trailer(req, msglen);
354
355 break;
356 }
357
358 default:
359 printf("unknown exception bevahior %d\n", behavior);
360 error = EINVAL;
361 goto out;
362 break;
363 }
364
365 mach_set_trailer(msgh, msglen);
366
367 /*
368 * Once an exception is sent on the exception port,
369 * no new exception will be taken until the catcher
370 * acknowledge the first one.
371 */
372 lockmgr(&catcher_med->med_exclock, LK_EXCLUSIVE, NULL);
373
374 /*
375 * If the catcher died, we are done.
376 */
377 if (((exc_port = exc_med->med_exc[exc]) == NULL) ||
378 (exc_port->mp_recv == NULL) ||
379 (P_ZOMBIE(exc_port->mp_recv->mr_lwp->l_proc))) {
380 error = ESRCH;
381 goto out;
382 }
383
384 (void)mach_message_get(msgh, msglen, exc_port, NULL);
385 wakeup(exc_port->mp_recv->mr_sethead);
386
387 /*
388 * The thread that caused the exception is now
389 * supposed to wait for a reply to its message.
390 */
391 #ifdef DEBUG_MACH
392 printf("mach_exception: %d.%d sleep on catcher_med->med_exclock = %p\n",
393 exc_l->l_proc->p_pid, exc_l->l_lid, &catcher_med->med_exclock);
394 #endif
395 error = tsleep(&catcher_med->med_exclock, PZERO, "mach_exc", 0);
396 #ifdef DEBUG_MACH
397 printf("mach_exception: %d.%d resumed, error = %d\n",
398 exc_l->l_proc->p_pid, exc_l->l_lid, error);
399 #endif
400
401 /*
402 * Unlock the catcher's exception handler
403 */
404 lockmgr(&catcher_med->med_exclock, LK_RELEASE, NULL);
405
406 out:
407 MACH_PORT_UNREF(exc_port);
408 return error;
409 }
410
411 static void
412 mach_siginfo_to_exception(ksi, code)
413 const struct ksiginfo *ksi;
414 int *code;
415 {
416 code[1] = (long)ksi->ksi_addr;
417 switch (ksi->ksi_signo) {
418 case SIGBUS:
419 switch (ksi->ksi_code) {
420 case BUS_ADRALN:
421 code[0] = MACH_BUS_ADRALN;
422 break;
423 default:
424 printf("untranslated siginfo signo %d, code %d\n",
425 ksi->ksi_signo, ksi->ksi_code);
426 break;
427 }
428 break;
429
430 case SIGSEGV:
431 switch (ksi->ksi_code) {
432 case SEGV_MAPERR:
433 code[0] = MACH_SEGV_MAPERR;
434 break;
435 case SEGV_ACCERR:
436 code[0] = MACH_SEGV_ACCERR;
437 break;
438 default:
439 printf("untranslated siginfo signo %d, code %d\n",
440 ksi->ksi_signo, ksi->ksi_code);
441 break;
442 }
443 break;
444
445 case SIGTRAP:
446 switch (ksi->ksi_code) {
447 case TRAP_BRKPT:
448 code[0] = MACH_TRAP_BRKPT;
449 code[1] = (long)ksi->ksi_addr;
450 break;
451 default:
452 printf("untranslated siginfo signo %d, code %d\n",
453 ksi->ksi_signo, ksi->ksi_code);
454 break;
455 }
456 break;
457
458 case SIGILL:
459 switch (ksi->ksi_code) {
460 case ILL_ILLOPC:
461 case ILL_ILLOPN:
462 case ILL_ILLADR:
463 code[0] = MACH_ILL_ILLOPC;
464 break;
465 case ILL_PRVOPC:
466 case ILL_PRVREG:
467 code[0] = MACH_ILL_PRVOPC;
468 break;
469 case ILL_ILLTRP:
470 code[0] = MACH_ILL_ILLTRP;
471 break;
472 default:
473 printf("untranslated siginfo signo %d, code %d\n",
474 ksi->ksi_signo, ksi->ksi_code);
475 break;
476 }
477 break;
478
479 default:
480 printf("untranslated siginfo signo %d, code %d\n",
481 ksi->ksi_signo, ksi->ksi_code);
482 break;
483 }
484 }
485
486 int
487 mach_exception_raise(args)
488 struct mach_trap_args *args;
489 {
490 struct lwp *l = args->l;
491 mach_exception_raise_reply_t *rep;
492 struct mach_emuldata *med;
493
494 /*
495 * No typo here: the reply is in the sent message.
496 * The kernel is acting as a client that gets the
497 * reply message to its exception message.
498 */
499 rep = args->smsg;
500
501 /*
502 * This message is sent by the process catching the
503 * exception to release the process that raised the exception.
504 * We wake it up if the return value is 0 (no error), else
505 * we should ignore this message.
506 */
507 #ifdef DEBUG_MACH
508 printf("mach_excpetion_raise: retval = %ld\n", (long)rep->rep_retval);
509 #endif
510 if (rep->rep_retval != 0)
511 return 0;
512
513 med = l->l_proc->p_emuldata;
514
515 /*
516 * Check for unexpected exception acknowledge, whereas
517 * the kernel sent no exception message.
518 */
519 if (lockstatus(&med->med_exclock) == 0) {
520 #ifdef DEBUG_MACH
521 printf("spurious mach_exception_raise\n");
522 #endif
523 return mach_msg_error(args, EINVAL);
524 }
525
526 /*
527 * Wakeup the thread that raised the exception.
528 */
529 #ifdef DEBUG_MACH
530 printf("mach_exception_raise: wakeup at %p\n", &med->med_exclock);
531 #endif
532 wakeup(&med->med_exclock);
533
534 return 0;
535 }
536
537 int
538 mach_exception_raise_state(args)
539 struct mach_trap_args *args;
540 {
541 return mach_exception_raise(args);
542 }
543
544 int
545 mach_exception_raise_state_identity(args)
546 struct mach_trap_args *args;
547 {
548 return mach_exception_raise(args);
549 }
Cache object: 0b588ebcc674c5e864f490a2d81c9a6b
|