The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/compat/mach/mach_exception.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.