1 /* $NetBSD: mach_exception.c,v 1.5 2005/02/26 23:10:19 perry 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.5 2005/02/26 23:10:19 perry Exp $");
41
42 #include "opt_ktrace.h"
43 #include "opt_compat_darwin.h"
44
45 #include <sys/types.h>
46 #include <sys/param.h>
47 #include <sys/signal.h>
48 #include <sys/proc.h>
49 #include <sys/malloc.h>
50
51 #ifdef COMPAT_DARWIN
52 #include <compat/darwin/darwin_exec.h>
53 #endif
54
55 #include <compat/mach/mach_types.h>
56 #include <compat/mach/mach_exec.h>
57 #include <compat/mach/mach_errno.h>
58 #include <compat/mach/mach_thread.h>
59 #include <compat/mach/mach_exception.h>
60 #include <compat/mach/mach_message.h>
61 #include <compat/mach/mach_services.h>
62 #include <compat/mach/mach_sysctl.h>
63
64 #include <machine/mach_machdep.h>
65
66 static void mach_siginfo_to_exception(const struct ksiginfo *, int *);
67
68 /*
69 * Exception handler
70 * Mach does not use signals. But systems based on Mach (e.g.: Darwin),
71 * can use both Mach exceptions and UNIX signals. In order to allow the
72 * Mach layer to intercept the exception and inhibit UNIX signals, we have
73 * mach_trapsignal1 returning an error. If it returns 0, then the
74 * exception was intercepted at the Mach level, and no signal should
75 * be produced. Else, a signal might be sent. darwin_trapinfo calls
76 * mach_trapinfo1 and handle signals if it gets a non zero return value.
77 */
78 void
79 mach_trapsignal(l, ksi)
80 struct lwp *l;
81 const struct ksiginfo *ksi;
82 {
83 if (mach_trapsignal1(l, ksi) != 0)
84 trapsignal(l, ksi);
85 return;
86 }
87
88 int
89 mach_trapsignal1(l, ksi)
90 struct lwp *l;
91 const struct ksiginfo *ksi;
92 {
93 struct proc *p = l->l_proc;
94 struct mach_emuldata *med;
95 int exc_no;
96 int code[2];
97
98 /* Don't inhinbit non maskable signals */
99 if (sigprop[ksi->ksi_signo] & SA_CANTMASK)
100 return EINVAL;
101
102 med = (struct mach_emuldata *)p->p_emuldata;
103
104 switch (ksi->ksi_signo) {
105 case SIGILL:
106 exc_no = MACH_EXC_BAD_INSTRUCTION;
107 break;
108 case SIGFPE:
109 exc_no = MACH_EXC_ARITHMETIC;
110 break;
111 case SIGSEGV:
112 case SIGBUS:
113 exc_no = MACH_EXC_BAD_ACCESS;
114 break;
115 case SIGTRAP:
116 exc_no = MACH_EXC_BREAKPOINT;
117 break;
118 default: /* SIGCHLD, SIGPOLL */
119 return EINVAL;
120 break;
121 }
122
123 mach_siginfo_to_exception(ksi, code);
124
125 return mach_exception(l, exc_no, code);
126 }
127
128 int
129 mach_exception(exc_l, exc, code)
130 struct lwp *exc_l; /* currently running lwp */
131 int exc;
132 int *code;
133 {
134 int behavior, flavor;
135 mach_msg_header_t *msgh;
136 size_t msglen;
137 struct mach_right *exc_mr;
138 struct mach_emuldata *exc_med;
139 struct mach_lwp_emuldata *exc_mle;
140 struct mach_emuldata *catcher_med;
141 struct mach_right *kernel_mr;
142 struct lwp *catcher_l; /* The lwp catching the exception */
143 struct mach_right *exc_task;
144 struct mach_right *exc_thread;
145 struct mach_port *exc_port;
146 struct mach_exc_info *mei;
147 int error = 0;
148
149 #ifdef DIAGNOSTIC
150 if (exc_l == NULL) {
151 printf("mach_exception: exc_l = %p\n", exc_l);
152 return ESRCH;
153 }
154 #endif
155 #ifdef DEBUG_MACH
156 printf("mach_exception: %d.%d, exc %d, code (%d, %d)\n",
157 exc_l->l_proc->p_pid, exc_l->l_lid, exc, code[0], code[1]);
158 #endif
159
160 /*
161 * It's extremely useful to have the ability of catching
162 * the process at the time it dies.
163 */
164 if (mach_exception_hang) {
165 int s;
166 struct proc *p = exc_l->l_proc;
167
168 sigminusset(&contsigmask, &p->p_sigctx.ps_siglist);
169 SCHED_LOCK(s);
170 p->p_pptr->p_nstopchild++;
171 p->p_stat = SSTOP;
172 exc_l->l_stat = LSSTOP;
173 p->p_nrlwps--;
174 mi_switch(exc_l, NULL);
175 SCHED_ASSERT_UNLOCKED();
176 splx(s);
177 }
178
179 /*
180 * No exception if there is no exception port or if it has no receiver
181 */
182 exc_mle = exc_l->l_emuldata;
183 exc_med = exc_l->l_proc->p_emuldata;
184 if ((exc_port = exc_med->med_exc[exc]) == NULL)
185 return EINVAL;
186
187 MACH_PORT_REF(exc_port);
188 if (exc_port->mp_recv == NULL) {
189 error = EINVAL;
190 goto out;
191 }
192
193 #ifdef DEBUG_MACH
194 printf("catcher is %d.%d, state %d\n",
195 exc_port->mp_recv->mr_lwp->l_proc->p_pid,
196 exc_port->mp_recv->mr_lwp->l_lid,
197 exc_port->mp_recv->mr_lwp->l_proc->p_stat);
198 #endif
199 /*
200 * Don't send exceptions to dying processes
201 */
202 if (P_ZOMBIE(exc_port->mp_recv->mr_lwp->l_proc)) {
203 error = ESRCH;
204 goto out;
205 }
206
207 /*
208 * XXX Avoid a nasty deadlock because process in TX state
209 * (traced and suspended) are invulnerable to kill -9.
210 *
211 * The scenario:
212 * - the parent gets Child's signals though Mach exceptions
213 * - the parent is killed. Before calling the emulation hook
214 * mach_exit(), it will wait for the child
215 * - the child receives SIGHUP, which is turned into a Mach
216 * exception. The child sleeps awaiting for the parent
217 * to tell it to continue.
218 * For some reason I do not understand, it goes in the
219 * suspended state instead of the sleeping state.
220 * - Parents waits for the child, child is suspended, we
221 * are stuck.
222 *
223 * By preventing exception to traced processes with
224 * a dying parent, a signal is sent instead of the
225 * notification, this fixes the problem.
226 */
227 if ((exc_l->l_proc->p_flag & P_TRACED) &&
228 (exc_l->l_proc->p_pptr->p_flag & P_WEXIT)) {
229 #ifdef DEBUG_MACH
230 printf("mach_exception: deadlock avoided\n");
231 #endif
232 error = EINVAL;
233 goto out;
234 }
235
236 if (exc_port->mp_datatype != MACH_MP_EXC_INFO) {
237 #ifdef DIAGNOSTIC
238 printf("mach_exception: unexpected datatype");
239 #endif
240 error = EINVAL;
241 goto out;
242 }
243 mei = exc_port->mp_data;
244 behavior = mei->mei_behavior;
245 flavor = mei->mei_flavor;
246
247 /*
248 * We want the port names in the target process, that is,
249 * the process with receive right for exc_port.
250 */
251 catcher_l = exc_port->mp_recv->mr_lwp;
252 catcher_med = catcher_l->l_proc->p_emuldata;
253 exc_mr = mach_right_get(exc_port, catcher_l, MACH_PORT_TYPE_SEND, 0);
254 kernel_mr = mach_right_get(catcher_med->med_kernel,
255 catcher_l, MACH_PORT_TYPE_SEND, 0);
256
257 exc_task = mach_right_get(exc_med->med_kernel,
258 catcher_l, MACH_PORT_TYPE_SEND, 0);
259 exc_thread = mach_right_get(exc_mle->mle_kernel,
260 catcher_l, MACH_PORT_TYPE_SEND, 0);
261
262 switch (behavior) {
263 case MACH_EXCEPTION_DEFAULT: {
264 mach_exception_raise_request_t *req;
265
266 req = malloc(sizeof(*req), M_EMULDATA, M_WAITOK | M_ZERO);
267 msglen = sizeof(*req);
268 msgh = (mach_msg_header_t *)req;
269
270 req->req_msgh.msgh_bits =
271 MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND) |
272 MACH_MSGH_REMOTE_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
273 req->req_msgh.msgh_size =
274 sizeof(*req) - sizeof(req->req_trailer);
275 req->req_msgh.msgh_remote_port = kernel_mr->mr_name;
276 req->req_msgh.msgh_local_port = exc_mr->mr_name;
277 req->req_msgh.msgh_id = MACH_EXC_RAISE_MSGID;
278
279 mach_add_port_desc(req, exc_thread->mr_name);
280 mach_add_port_desc(req, exc_task->mr_name);
281
282 req->req_exc = exc;
283 req->req_codecount = 2;
284 memcpy(&req->req_code[0], code, sizeof(req->req_code));
285
286 mach_set_trailer(req, msglen);
287
288 break;
289 }
290
291 case MACH_EXCEPTION_STATE: {
292 mach_exception_raise_state_request_t *req;
293 int dc;
294
295 req = malloc(sizeof(*req), M_EMULDATA, M_WAITOK | M_ZERO);
296 msglen = sizeof(*req);
297 msgh = (mach_msg_header_t *)req;
298
299 req->req_msgh.msgh_bits =
300 MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND) |
301 MACH_MSGH_REMOTE_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
302 req->req_msgh.msgh_size =
303 sizeof(*req) - sizeof(req->req_trailer);
304 req->req_msgh.msgh_remote_port = kernel_mr->mr_name;
305 req->req_msgh.msgh_local_port = exc_mr->mr_name;
306 req->req_msgh.msgh_id = MACH_EXCEPTION_STATE;
307 req->req_exc = exc;
308 req->req_codecount = 2;
309 memcpy(&req->req_code[0], code, sizeof(req->req_code));
310 req->req_flavor = flavor;
311 mach_thread_get_state_machdep(exc_l,
312 flavor, req->req_state, &dc);
313
314 msglen = msglen -
315 sizeof(req->req_state) +
316 (dc * sizeof(req->req_state[0]));
317 mach_set_trailer(req, msglen);
318
319 break;
320 }
321
322 case MACH_EXCEPTION_STATE_IDENTITY: {
323 mach_exception_raise_state_identity_request_t *req;
324 int dc;
325
326 req = malloc(sizeof(*req), M_EMULDATA, M_WAITOK | M_ZERO);
327 msglen = sizeof(*req);
328 msgh = (mach_msg_header_t *)req;
329
330 req->req_msgh.msgh_bits =
331 MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND) |
332 MACH_MSGH_REMOTE_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
333 req->req_msgh.msgh_size =
334 sizeof(*req) - sizeof(req->req_trailer);
335 req->req_msgh.msgh_remote_port = kernel_mr->mr_name;
336 req->req_msgh.msgh_local_port = exc_mr->mr_name;
337 req->req_msgh.msgh_id = MACH_EXC_RAISE_STATE_IDENTITY_MSGID;
338 req->req_body.msgh_descriptor_count = 2;
339
340 mach_add_port_desc(req, exc_thread->mr_name);
341 mach_add_port_desc(req, exc_task->mr_name);
342
343 req->req_exc = exc;
344 req->req_codecount = 2;
345 memcpy(&req->req_code[0], code, sizeof(req->req_code));
346 req->req_flavor = flavor;
347 mach_thread_get_state_machdep(exc_l,
348 flavor, req->req_state, &dc);
349
350 msglen = msglen -
351 sizeof(req->req_state) +
352 (dc * sizeof(req->req_state[0]));
353
354 mach_set_trailer(req, msglen);
355
356 break;
357 }
358
359 default:
360 printf("unknown exception bevahior %d\n", behavior);
361 error = EINVAL;
362 goto out;
363 break;
364 }
365
366 mach_set_trailer(msgh, msglen);
367
368 /*
369 * Once an exception is sent on the exception port,
370 * no new exception will be taken until the catcher
371 * acknowledge the first one.
372 */
373 lockmgr(&catcher_med->med_exclock, LK_EXCLUSIVE, NULL);
374
375 /*
376 * If the catcher died, we are done.
377 */
378 if (((exc_port = exc_med->med_exc[exc]) == NULL) ||
379 (exc_port->mp_recv == NULL) ||
380 (P_ZOMBIE(exc_port->mp_recv->mr_lwp->l_proc))) {
381 error = ESRCH;
382 goto out;
383 }
384
385 (void)mach_message_get(msgh, msglen, exc_port, NULL);
386 wakeup(exc_port->mp_recv->mr_sethead);
387
388 /*
389 * The thread that caused the exception is now
390 * supposed to wait for a reply to its message.
391 */
392 #ifdef DEBUG_MACH
393 printf("mach_exception: %d.%d sleep on catcher_med->med_exclock = %p\n",
394 exc_l->l_proc->p_pid, exc_l->l_lid, &catcher_med->med_exclock);
395 #endif
396 error = tsleep(&catcher_med->med_exclock, PZERO, "mach_exc", 0);
397 #ifdef DEBUG_MACH
398 printf("mach_exception: %d.%d resumed, error = %d\n",
399 exc_l->l_proc->p_pid, exc_l->l_lid, error);
400 #endif
401
402 /*
403 * Unlock the catcher's exception handler
404 */
405 lockmgr(&catcher_med->med_exclock, LK_RELEASE, NULL);
406
407 out:
408 MACH_PORT_UNREF(exc_port);
409 return error;
410 }
411
412 static void
413 mach_siginfo_to_exception(ksi, code)
414 const struct ksiginfo *ksi;
415 int *code;
416 {
417 switch (ksi->ksi_signo) {
418 case SIGBUS:
419 switch (ksi->ksi_code) {
420 case BUS_ADRALN:
421 code[0] = MACH_BUS_ADRALN;
422 code[1] = (long)ksi->ksi_addr;
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 SIGSEGV:
432 switch (ksi->ksi_code) {
433 case SEGV_MAPERR:
434 code[0] = MACH_SEGV_MAPERR;
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 SIGTRAP:
445 switch (ksi->ksi_code) {
446 case TRAP_BRKPT:
447 code[0] = MACH_TRAP_BRKPT;
448 code[1] = (long)ksi->ksi_addr;
449 break;
450 default:
451 printf("untranslated siginfo signo %d, code %d\n",
452 ksi->ksi_signo, ksi->ksi_code);
453 break;
454 }
455 break;
456
457 case SIGILL:
458 switch (ksi->ksi_code) {
459 case ILL_ILLOPC:
460 case ILL_ILLOPN:
461 case ILL_ILLADR:
462 case ILL_ILLTRP:
463 code[0] = MACH_ILL_ILLOPC;
464 code[1] = (long)ksi->ksi_addr;
465 break;
466 case ILL_PRVOPC:
467 case ILL_PRVREG:
468 code[0] = MACH_ILL_PRVOPC;
469 code[1] = (long)ksi->ksi_addr;
470 break;
471 default:
472 printf("untranslated siginfo signo %d, code %d\n",
473 ksi->ksi_signo, ksi->ksi_code);
474 break;
475 }
476 break;
477
478 default:
479 printf("untranslated siginfo signo %d, code %d\n",
480 ksi->ksi_signo, ksi->ksi_code);
481 break;
482 }
483 return;
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: a96b9670c8d3dac7523a5e5e620926c0
|