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


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