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.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


[ 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.