Esempio n. 1
0
int
pfinet_demuxer (mach_msg_header_t *inp,
                mach_msg_header_t *outp)
{
    struct port_info *pi;

    /* We have several classes in one bucket, which need to be demuxed
       differently.  */
    if (MACH_MSGH_BITS_LOCAL (inp->msgh_bits) ==
            MACH_MSG_TYPE_PROTECTED_PAYLOAD)
        pi = ports_lookup_payload (pfinet_bucket,
                                   inp->msgh_protected_payload,
                                   socketport_class);
    else
        pi = ports_lookup_port (pfinet_bucket,
                                inp->msgh_local_port,
                                socketport_class);

    if (pi)
    {
        ports_port_deref (pi);

        mig_routine_t routine;
        if ((routine = io_server_routine (inp)) ||
                (routine = socket_server_routine (inp)) ||
                (routine = pfinet_server_routine (inp)) ||
                (routine = iioctl_server_routine (inp)) ||
                (routine = NULL, trivfs_demuxer (inp, outp)) ||
                (routine = startup_notify_server_routine (inp)))
        {
            if (routine)
                (*routine) (inp, outp);
            return TRUE;
        }
        else
            return FALSE;
    }
    else
    {
        mig_routine_t routine;
        if ((routine = socket_server_routine (inp)) ||
                (routine = pfinet_server_routine (inp)) ||
                (routine = iioctl_server_routine (inp)) ||
                (routine = NULL, trivfs_demuxer (inp, outp)) ||
                (routine = startup_notify_server_routine (inp)))
        {
            if (routine)
                (*routine) (inp, outp);
            return TRUE;
        }
        else
            return FALSE;
    }
}
Esempio n. 2
0
ipc_kmsg_t
ipc_kobject_server(
	ipc_kmsg_t	request,
	mach_msg_option_t __unused option)
{
	mach_msg_size_t reply_size;
	ipc_kmsg_t reply;
	kern_return_t kr;
	ipc_port_t *destp;
	ipc_port_t  replyp = IPC_PORT_NULL;
	mach_msg_format_0_trailer_t *trailer;
	mig_hash_t *ptr;
	task_t task = TASK_NULL;
	uint32_t exec_token;
	boolean_t exec_token_changed = FALSE;

	/*
	 * Find out corresponding mig_hash entry if any
	 */
	{
	    int key = request->ikm_header->msgh_id;
	    unsigned int i = (unsigned int)MIG_HASH(key);
	    int max_iter = mig_table_max_displ;

	    do {
		ptr = &mig_buckets[i++ % MAX_MIG_ENTRIES];
	    } while (key != ptr->num && ptr->num && --max_iter);

	    if (!ptr->routine || key != ptr->num) {
	        ptr = (mig_hash_t *)0;
		reply_size = mig_reply_size;
	    } else {
		reply_size = ptr->size;
#if	MACH_COUNTER
		ptr->callcount++;
#endif
	    }
	}

	/* round up for trailer size */
        reply_size += MAX_TRAILER_SIZE;
	reply = ipc_kmsg_alloc(reply_size);

	if (reply == IKM_NULL) {
		printf("ipc_kobject_server: dropping request\n");
		ipc_kmsg_trace_send(request, option);
		ipc_kmsg_destroy(request);
		return IKM_NULL;
	}

	/*
	 * Initialize reply message.
	 */
	{
#define	InP	((mach_msg_header_t *) request->ikm_header)
#define	OutP	((mig_reply_error_t *) reply->ikm_header)

	    /* 
	     * MIG should really assure no data leakage -
	     * but until it does, pessimistically zero the
	     * whole reply buffer.
	     */
	    bzero((void *)OutP, reply_size);

	    OutP->NDR = NDR_record;
	    OutP->Head.msgh_size = sizeof(mig_reply_error_t);

	    OutP->Head.msgh_bits =
		MACH_MSGH_BITS_SET(MACH_MSGH_BITS_LOCAL(InP->msgh_bits), 0, 0, 0);
	    OutP->Head.msgh_remote_port = InP->msgh_local_port;
	    OutP->Head.msgh_local_port = MACH_PORT_NULL;
	    OutP->Head.msgh_voucher_port = MACH_PORT_NULL;
	    OutP->Head.msgh_id = InP->msgh_id + 100;

#undef	InP
#undef	OutP
	}

	/*
	 * Find the routine to call, and call it
	 * to perform the kernel function
	 */
	ipc_kmsg_trace_send(request, option);
	{
	    if (ptr) {
		/*
		 * Check if the port is a task port, if its a task port then
		 * snapshot the task exec token before the mig routine call.
		 */
		ipc_port_t port = request->ikm_header->msgh_remote_port;
		if (IP_VALID(port) && ip_kotype(port) == IKOT_TASK) {
			task = convert_port_to_task_with_exec_token(port, &exec_token);
		}

		(*ptr->routine)(request->ikm_header, reply->ikm_header);

		/* Check if the exec token changed during the mig routine */
		if (task != TASK_NULL) {
			if (exec_token != task->exec_token) {
				exec_token_changed = TRUE;
			}
			task_deallocate(task);
		}

		kernel_task->messages_received++;
	    }
	    else {
		if (!ipc_kobject_notify(request->ikm_header, reply->ikm_header)){
#if DEVELOPMENT || DEBUG
		    printf("ipc_kobject_server: bogus kernel message, id=%d\n",
			request->ikm_header->msgh_id);
#endif	/* DEVELOPMENT || DEBUG */
		    _MIG_MSGID_INVALID(request->ikm_header->msgh_id);

		    ((mig_reply_error_t *) reply->ikm_header)->RetCode
			= MIG_BAD_ID;
		}
		else
		  kernel_task->messages_received++;
	    }
	    kernel_task->messages_sent++;
	}

	/*
	 *	Destroy destination. The following code differs from
	 *	ipc_object_destroy in that we release the send-once
	 *	right instead of generating a send-once notification
	 * 	(which would bring us here again, creating a loop).
	 *	It also differs in that we only expect send or
	 *	send-once rights, never receive rights.
	 *
	 *	We set msgh_remote_port to IP_NULL so that the kmsg
	 *	destroy routines don't try to destroy the port twice.
	 */
	destp = (ipc_port_t *) &request->ikm_header->msgh_remote_port;
	switch (MACH_MSGH_BITS_REMOTE(request->ikm_header->msgh_bits)) {
		case MACH_MSG_TYPE_PORT_SEND:
		    ipc_port_release_send(*destp);
		    break;
		
		case MACH_MSG_TYPE_PORT_SEND_ONCE:
		    ipc_port_release_sonce(*destp);
		    break;
		
		default:
		    panic("ipc_kobject_server: strange destination rights");
	}
	*destp = IP_NULL;

	/*
	 *	Destroy voucher.  The kernel MIG servers never take ownership
	 *	of vouchers sent in messages.  Swallow any such rights here.
	 */
	if (IP_VALID(request->ikm_voucher)) {
		assert(MACH_MSG_TYPE_PORT_SEND ==
		       MACH_MSGH_BITS_VOUCHER(request->ikm_header->msgh_bits));
		ipc_port_release_send(request->ikm_voucher);
		request->ikm_voucher = IP_NULL;
	}

        if (!(reply->ikm_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
           ((mig_reply_error_t *) reply->ikm_header)->RetCode != KERN_SUCCESS)
	 	kr = ((mig_reply_error_t *) reply->ikm_header)->RetCode;
	else
		kr = KERN_SUCCESS;

	if ((kr == KERN_SUCCESS) || (kr == MIG_NO_REPLY)) {
		/*
		 *	The server function is responsible for the contents
		 *	of the message.  The reply port right is moved
		 *	to the reply message, and we have deallocated
		 *	the destination port right, so we just need
		 *	to free the kmsg.
		 */
		ipc_kmsg_free(request);

	} else {
		/*
		 *	The message contents of the request are intact.
		 *	Destroy everthing except the reply port right,
		 *	which is needed in the reply message.
		 */
		request->ikm_header->msgh_local_port = MACH_PORT_NULL;
		ipc_kmsg_destroy(request);
	}

	replyp = (ipc_port_t)reply->ikm_header->msgh_remote_port;

	if (kr == MIG_NO_REPLY) {
		/*
		 *	The server function will send a reply message
		 *	using the reply port right, which it has saved.
		 */

		ipc_kmsg_free(reply);

		return IKM_NULL;
	} else if (!IP_VALID(replyp)) {
		/*
		 *	Can't queue the reply message if the destination
		 *	(the reply port) isn't valid.
		 */

		ipc_kmsg_destroy(reply);

		return IKM_NULL;
	} else if (replyp->ip_receiver == ipc_space_kernel) {
		/*
		 * Don't send replies to kobject kernel ports
		 */
#if DEVELOPMENT || DEBUG
		printf("%s: refusing to send reply to kobject %d port (id:%d)\n",
		       __func__, ip_kotype(replyp),
		       request->ikm_header->msgh_id);
#endif	/* DEVELOPMENT || DEBUG */
		ipc_kmsg_destroy(reply);
		return IKM_NULL;
	}

	/* Fail the MIG call if the task exec token changed during the call */
	if (kr == KERN_SUCCESS && exec_token_changed) {
		/*
		 *	Create a new reply msg with error and destroy the old reply msg.
		 */
		ipc_kmsg_t new_reply = ipc_kmsg_alloc(reply_size);

		if (new_reply == IKM_NULL) {
			printf("ipc_kobject_server: dropping request\n");
			ipc_kmsg_destroy(reply);
			return IKM_NULL;
		}
		/*
		 *	Initialize the new reply message.
		 */
		{
#define	OutP_new	((mig_reply_error_t *) new_reply->ikm_header)
#define	OutP_old	((mig_reply_error_t *) reply->ikm_header)

		    bzero((void *)OutP_new, reply_size);

		    OutP_new->NDR = OutP_old->NDR;
		    OutP_new->Head.msgh_size = sizeof(mig_reply_error_t);
		    OutP_new->Head.msgh_bits = OutP_old->Head.msgh_bits & ~MACH_MSGH_BITS_COMPLEX;
		    OutP_new->Head.msgh_remote_port = OutP_old->Head.msgh_remote_port;
		    OutP_new->Head.msgh_local_port = MACH_PORT_NULL;
		    OutP_new->Head.msgh_voucher_port = MACH_PORT_NULL;
		    OutP_new->Head.msgh_id = OutP_old->Head.msgh_id;

		    /* Set the error as KERN_INVALID_TASK */
		    OutP_new->RetCode = KERN_INVALID_TASK;

#undef	OutP_new
#undef  OutP_old
		}

		/*
		 *	Destroy everything in reply except the reply port right,
		 *	which is needed in the new reply message.
		 */
		reply->ikm_header->msgh_remote_port = MACH_PORT_NULL;
		ipc_kmsg_destroy(reply);

		reply = new_reply;
	}

 	trailer = (mach_msg_format_0_trailer_t *)
		((vm_offset_t)reply->ikm_header + (int)reply->ikm_header->msgh_size);

 	trailer->msgh_sender = KERNEL_SECURITY_TOKEN;
 	trailer->msgh_trailer_type = MACH_MSG_TRAILER_FORMAT_0;
 	trailer->msgh_trailer_size = MACH_MSG_TRAILER_MINIMUM_SIZE;

	return reply;
}
Esempio n. 3
0
void
ports_manage_port_operations_one_thread (struct port_bucket *bucket,
					 ports_demuxer_type demuxer,
					 int timeout)
{
  struct ports_thread thread;
  error_t err;

  int 
  internal_demuxer (mach_msg_header_t *inp,
		    mach_msg_header_t *outheadp)
    {
      struct port_info *pi;
      struct rpc_info link;
      int status;
      error_t err;
      register mig_reply_header_t *outp = (mig_reply_header_t *) outheadp;
      static const mach_msg_type_t RetCodeType = {
		/* msgt_name = */		MACH_MSG_TYPE_INTEGER_32,
		/* msgt_size = */		32,
		/* msgt_number = */		1,
		/* msgt_inline = */		TRUE,
		/* msgt_longform = */		FALSE,
		/* msgt_deallocate = */		FALSE,
		/* msgt_unused = */		0
	};

      /* Fill in default response. */
      outp->Head.msgh_bits 
	= MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(inp->msgh_bits), 0);
      outp->Head.msgh_size = sizeof *outp;
      outp->Head.msgh_remote_port = inp->msgh_remote_port;
      outp->Head.msgh_local_port = MACH_PORT_NULL;
      outp->Head.msgh_seqno = 0;
      outp->Head.msgh_id = inp->msgh_id + 100;
      outp->RetCodeType = RetCodeType;
      outp->RetCode = MIG_BAD_ID;

      if (MACH_MSGH_BITS_LOCAL (inp->msgh_bits) ==
	  MACH_MSG_TYPE_PROTECTED_PAYLOAD)
	pi = ports_lookup_payload (bucket, inp->msgh_protected_payload, NULL);
      else
	{
	  pi = ports_lookup_port (bucket, inp->msgh_local_port, 0);
	  if (pi)
	    {
	      /* Store the objects address as the payload and set the
		 message type accordingly.  This prevents us from
		 having to do another hash table lookup in the intran
		 functions if protected payloads are not supported by
		 the kernel.  */
	      inp->msgh_bits =
		MACH_MSGH_BITS_OTHER (inp->msgh_bits)
		| MACH_MSGH_BITS (MACH_MSGH_BITS_REMOTE (inp->msgh_bits),
				  MACH_MSG_TYPE_PROTECTED_PAYLOAD);
	      inp->msgh_protected_payload = (unsigned long) pi;
	    }
	}

      if (pi)
	{
	  err = ports_begin_rpc (pi, inp->msgh_id, &link);
	  if (err)
	    {
	      mach_port_deallocate (mach_task_self (), inp->msgh_remote_port);
	      outp->RetCode = err;
	      status = 1;
	    }
	  else
	    {
	      /* No need to check cancel threshold here, because
		 in a single threaded server the cancel is always
		 handled in order. */
	      status = demuxer (inp, outheadp);
	      ports_end_rpc (pi, &link);
	    }
	  ports_port_deref (pi);
	}
      else
	{
	  outp->RetCode = EOPNOTSUPP;
	  status = 1;
	}

      _ports_thread_quiescent (&bucket->threadpool, &thread);
      return status;
    }

  /* XXX It is currently unsafe for most servers to terminate based on
     inactivity because a request may arrive after a server has
     started shutting down, causing the client to receive an error.
     Prevent the service loop from terminating by setting TIMEOUT to
     zero.  */
  timeout = 0;

  _ports_thread_online (&bucket->threadpool, &thread);
  do
    err = mach_msg_server_timeout (internal_demuxer, 0, bucket->portset, 
				   timeout ? MACH_RCV_TIMEOUT : 0, timeout);
  while (err != MACH_RCV_TIMED_OUT);
  _ports_thread_offline (&bucket->threadpool, &thread);
}
Esempio n. 4
0
ipc_kmsg_t
ipc_kobject_server(
	ipc_kmsg_t	request)
{
	mach_msg_size_t reply_size;
	ipc_kmsg_t reply;
	kern_return_t kr;
	mig_routine_t routine;
	ipc_port_t *destp;
	mach_msg_format_0_trailer_t *trailer;
	register mig_hash_t *ptr;
#if	MACH_RT
	boolean_t reply_rt;
#endif	/* MACH_RT */
	unsigned int th;

	/* Only fetch current thread if ETAP is configured */
	ETAP_DATA_LOAD(th, current_thread());
        ETAP_PROBE_DATA(ETAP_P_SYSCALL_MACH,
                        EVENT_BEGIN,
			((thread_t) th),
                        &request->ikm_header.msgh_id,
                        sizeof(int));
	/*
         * Find out corresponding mig_hash entry if any
         */
	{
	    register int key = request->ikm_header.msgh_id;
	    register int i = MIG_HASH(key);
	    register int max_iter = mig_table_max_displ;
	
	    do
		ptr = &mig_buckets[i++ % MAX_MIG_ENTRIES];
	    while (key != ptr->num && ptr->num && --max_iter);

	    if (!ptr->routine || key != ptr->num) {
	        ptr = (mig_hash_t *)0;
		reply_size = mig_reply_size;
	    } else {
		reply_size = ptr->size;
#if	MACH_COUNTER
		ptr->callcount++;
#endif
	    }
	}

	/* round up for ikm_cache; overhead is added by ikm_alloc */
        if ((reply_size += MAX_TRAILER_SIZE) < IKM_SAVED_MSG_SIZE)
		reply_size = IKM_SAVED_MSG_SIZE;

#if	MACH_RT
	reply_rt =
	  IP_VALID((ipc_port_t)request->ikm_header.msgh_local_port) ?
	    IP_RT((ipc_port_t)request->ikm_header.msgh_local_port) :
	    FALSE;
	    
	if (reply_rt)
	      reply = ikm_rtalloc(reply_size);
	else
#endif	/* MACH_RT */
	      reply = ikm_alloc(reply_size);

	if (reply == IKM_NULL) {
		printf("ipc_kobject_server: dropping request\n");
		ipc_kmsg_destroy(request);
		return IKM_NULL;
	}

	ikm_init(reply, reply_size);
#if	DIPC
	reply->ikm_handle = HANDLE_NULL;
#endif	/* DIPC */

	/*
	 * Initialize reply message.
	 */
	{
#define	InP	((mach_msg_header_t *) &request->ikm_header)
#define	OutP	((mig_reply_error_t *) &reply->ikm_header)

	    OutP->NDR = NDR_record;
	    OutP->Head.msgh_size = sizeof(mig_reply_error_t);

	    OutP->Head.msgh_bits =
		MACH_MSGH_BITS(MACH_MSGH_BITS_LOCAL(InP->msgh_bits), 0);
	    OutP->Head.msgh_remote_port = InP->msgh_local_port;
	    OutP->Head.msgh_local_port  = MACH_PORT_NULL;
	    OutP->Head.msgh_id = InP->msgh_id + 100;
#if	MACH_RT
	    if (reply_rt)
	          KMSG_MARK_RT(reply);
#endif	/* MACH_RT */
#undef	InP
#undef	OutP
	}

	/*
	 * Find the routine to call, and call it
	 * to perform the kernel function
	 */
	{
	    if (ptr)	
		(*ptr->routine)(&request->ikm_header, &reply->ikm_header);
	    else {
		if (!ipc_kobject_notify(&request->ikm_header, &reply->ikm_header)){
#if	MACH_IPC_TEST
		    printf("ipc_kobject_server: bogus kernel message, id=%d\n",
			request->ikm_header.msgh_id);
#endif	/* MACH_IPC_TEST */
		    ((mig_reply_error_t *) &reply->ikm_header)->RetCode
			= MIG_BAD_ID;
		}
	    }
	}

	/*
	 *	Destroy destination. The following code differs from
	 *	ipc_object_destroy in that we release the send-once
	 *	right instead of generating a send-once notification
	 * 	(which would bring us here again, creating a loop).
	 *	It also differs in that we only expect send or
	 *	send-once rights, never receive rights.
	 *
	 *	We set msgh_remote_port to IP_NULL so that the kmsg
	 *	destroy routines don't try to destroy the port twice.
	 */
	destp = (ipc_port_t *) &request->ikm_header.msgh_remote_port;
	switch (MACH_MSGH_BITS_REMOTE(request->ikm_header.msgh_bits)) {
		case MACH_MSG_TYPE_PORT_SEND:
		    ipc_port_release_send(*destp);
		    break;
		
		case MACH_MSG_TYPE_PORT_SEND_ONCE:
		    ipc_port_release_sonce(*destp);
		    break;
		
		default:
		    panic("ipc_object_destroy: strange destination rights");
	}
	*destp = IP_NULL;

        if (!(reply->ikm_header.msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
           ((mig_reply_error_t *) &reply->ikm_header)->RetCode != KERN_SUCCESS)
	 	kr = ((mig_reply_error_t *) &reply->ikm_header)->RetCode;
	else
		kr = KERN_SUCCESS;

	if ((kr == KERN_SUCCESS) || (kr == MIG_NO_REPLY)) {
		/*
		 *	The server function is responsible for the contents
		 *	of the message.  The reply port right is moved
		 *	to the reply message, and we have deallocated
		 *	the destination port right, so we just need
		 *	to free the kmsg.
		 */

		/*
		 * Like ipc_kmsg_put, but without the copyout.  Also,
		 * messages to the kernel will never have been allocated
		 * from the rt_zone.
		 */

		ikm_check_initialized(request, request->ikm_size);
		if (request->ikm_size != IKM_SAVED_KMSG_SIZE ||
		    !ikm_cache_put (request)) {
			ikm_free(request);
		}
	} else {
		/*
		 *	The message contents of the request are intact.
		 *	Destroy everthing except the reply port right,
		 *	which is needed in the reply message.
		 */

		request->ikm_header.msgh_local_port = MACH_PORT_NULL;
		ipc_kmsg_destroy(request);
	}

	if (kr == MIG_NO_REPLY) {
		/*
		 *	The server function will send a reply message
		 *	using the reply port right, which it has saved.
		 */

		ikm_free(reply);

		ETAP_PROBE_DATA(ETAP_P_SYSCALL_MACH,
				EVENT_END,
				((thread_t) th),
				&request->ikm_header.msgh_id,
				sizeof(int));

		return IKM_NULL;
	} else if (!IP_VALID((ipc_port_t)reply->ikm_header.msgh_remote_port)) {
		/*
		 *	Can't queue the reply message if the destination
		 *	(the reply port) isn't valid.
		 */

		ipc_kmsg_destroy(reply);

		ETAP_PROBE_DATA(ETAP_P_SYSCALL_MACH,
				EVENT_END,
				((thread_t) th),
				&request->ikm_header.msgh_id,
				sizeof(int));

		return IKM_NULL;
	}

 	trailer = (mach_msg_format_0_trailer_t *)
		((vm_offset_t)&reply->ikm_header + (int)reply->ikm_header.msgh_size);                
 	trailer->msgh_sender = KERNEL_SECURITY_TOKEN;
 	trailer->msgh_trailer_type = MACH_MSG_TRAILER_FORMAT_0;
 	trailer->msgh_trailer_size = MACH_MSG_TRAILER_MINIMUM_SIZE;

        ETAP_PROBE_DATA(ETAP_P_SYSCALL_MACH,
                        EVENT_END,
			((thread_t) th),
                        &request->ikm_header.msgh_id,
                        sizeof(int));

	return reply;
}
spc_message_t* spc_deserialize(spc_mach_message_t* mach_msg)
{
    reader_t reader;
    reader.next_port = 0;
    reader.num_ports = 0;
    reader.ports = NULL;
    reader.end = (unsigned char*)mach_msg + mach_msg->header.msgh_size;
    reader.ptr = mach_msg->buf;

    // Handle well-known message IDs
    if (mach_msg->header.msgh_id == MSGID_CONNECTION_INTERRUPTED) {
        spc_dictionary_t* dict = spc_dictionary_create();
        spc_dictionary_set_string(dict, "error", "Connection interrupted");
        printf("Connection interrupted\n");
        // TODO
        exit(-1);
    }

    if (mach_msg->header.msgh_bits & MACH_MSGH_BITS_COMPLEX) {
        mach_msg_body_t* body = (mach_msg_body_t*)spc_read(&reader, sizeof(mach_msg_body_t));
        for (int i = 0; i < body->msgh_descriptor_count; i++) {
            mach_msg_descriptor_type_t type = ((mach_msg_type_descriptor_t*)reader.ptr)->type;
            switch (type) {
                case MACH_MSG_PORT_DESCRIPTOR: {
                    reader.ports = realloc(reader.ports, (reader.num_ports + 1) * sizeof(spc_port_t));
                    mach_msg_port_descriptor_t* descriptor = (mach_msg_port_descriptor_t*)spc_read(&reader, sizeof(mach_msg_port_descriptor_t));
                    reader.ports[reader.num_ports].name = descriptor->name;
                    reader.ports[reader.num_ports].type = descriptor->disposition;
                    reader.num_ports += 1;
                    break;
                }
                case MACH_MSG_OOL_DESCRIPTOR:
                    spc_read(&reader, sizeof(mach_msg_ool_descriptor_t));
                    printf("Warning: ignoring OOL descriptor\n");
                    break;
                case MACH_MSG_OOL_PORTS_DESCRIPTOR:
                    spc_read(&reader, sizeof(mach_msg_ool_ports_descriptor_t));
                    printf("Warning: ignoring OOL ports descriptor\n");
                    break;
                default:
                    printf("Unsupported mach message descriptor type: %d\n", type);
                    exit(-1);
            }
        }
    }

    void* header = spc_read(&reader, 8);
    memcpy(last_header, header, 8);

    spc_value_t value = spc_deserialize_value(&reader);
    if (value.type != SPC_TYPE_DICT) {
        spc_value_destroy(value);
        puts("Invalid XPC message type");
        return NULL;
    }

    spc_message_t* msg = malloc(sizeof(spc_message_t));
    msg->remote_port.name = mach_msg->header.msgh_remote_port;
    msg->remote_port.type = MACH_MSGH_BITS_REMOTE(mach_msg->header.msgh_bits);
    msg->local_port.name = mach_msg->header.msgh_remote_port;
    msg->local_port.type = MACH_MSGH_BITS_LOCAL(mach_msg->header.msgh_bits);
    msg->id = mach_msg->header.msgh_id;
    msg->content = value.value.dict;

    return msg;
}
/*
 * The meat of our exception handler. This thread waits for an exception
 * message, annotates the exception if needed, then forwards it to the
 * previously installed handler (which will likely terminate the process).
 */
static void
MachExceptionHandler()
{
    kern_return_t ret;
    MachExceptionParameters& current = sMachExceptionState.current;
    MachExceptionParameters& previous = sMachExceptionState.previous;

    // We use the simplest kind of 64-bit exception message here.
    ExceptionRequest64 request = {};
    request.header.msgh_local_port = current.port;
    request.header.msgh_size = static_cast<mach_msg_size_t>(sizeof(request));
    ret = mach_msg(&request.header, MACH_RCV_MSG, 0, request.header.msgh_size,
                   current.port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);

    // Restore the previous handler. We're going to forward to it
    // anyway, and if we crash while doing so we don't want to hang.
    task_set_exception_ports(mach_task_self(), previous.mask, previous.port,
                             previous.behavior, previous.flavor);

    // If we failed even receiving the message, just give up.
    if (ret != MACH_MSG_SUCCESS)
        MOZ_CRASH("MachExceptionHandler: mach_msg failed to receive a message!");

    // Terminate the thread if we're shutting down.
    if (request.header.msgh_id == sIDQuit)
        return;

    // The only other valid message ID is the one associated with the
    // EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES behavior we chose.
    if (request.header.msgh_id != sIDRequest64)
        MOZ_CRASH("MachExceptionHandler: Unexpected Message ID!");

    // Make sure we can understand the exception we received.
    if (request.exception != EXC_BAD_ACCESS || request.code_count != 2)
        MOZ_CRASH("MachExceptionHandler: Unexpected exception type!");

    // Get the address that the offending code tried to access.
    uintptr_t address = uintptr_t(request.code[1]);

    // If the faulting address is inside one of our protected regions, we
    // want to annotate the crash to make it stand out from the crowd.
    if (sProtectedRegions.isProtected(address)) {
        ReportCrashIfDebug("Hit MOZ_CRASH(Tried to access a protected region!)\n");
        MOZ_CRASH_ANNOTATE("MOZ_CRASH(Tried to access a protected region!)");
    }

    // Forward to the previous handler which may be a debugger, the unix
    // signal handler, the crash reporter or something else entirely.
    if (previous.port != MACH_PORT_NULL) {
        mach_msg_type_number_t stateCount;
        thread_state_data_t state;
        if ((uint32_t(previous.behavior) & ~MACH_EXCEPTION_CODES) != EXCEPTION_DEFAULT) {
            // If the previous handler requested thread state, get it here.
            stateCount = THREAD_STATE_MAX;
            ret = thread_get_state(request.thread.name, previous.flavor, state, &stateCount);
            if (ret != KERN_SUCCESS)
                MOZ_CRASH("MachExceptionHandler: Could not get the thread state to forward!");
        }

        // Depending on the behavior of the previous handler, the forwarded
        // exception message will have a different set of fields.
        // Of particular note is that exception handlers that lack
        // MACH_EXCEPTION_CODES will get 32-bit fields even on 64-bit
        // systems. It appears that OSX simply truncates these fields.
        ExceptionRequestUnion forward;
        switch (uint32_t(previous.behavior)) {
          case EXCEPTION_DEFAULT:
             CopyExceptionRequest32(request, forward.r32);
             break;
          case EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES:
             CopyExceptionRequest64(request, forward.r64);
             break;
          case EXCEPTION_STATE:
             CopyExceptionRequestState32(request, forward.rs32,
                                         previous.flavor, stateCount, state);
             break;
          case EXCEPTION_STATE | MACH_EXCEPTION_CODES:
             CopyExceptionRequestState64(request, forward.rs64,
                                         previous.flavor, stateCount, state);
             break;
          case EXCEPTION_STATE_IDENTITY:
             CopyExceptionRequestStateIdentity32(request, forward.rsi32,
                                                 previous.flavor, stateCount, state);
             break;
          case EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES:
             CopyExceptionRequestStateIdentity64(request, forward.rsi64,
                                                 previous.flavor, stateCount, state);
             break;
          default:
             MOZ_CRASH("MachExceptionHandler: Unknown previous handler behavior!");
        }

        // Forward the generated message to the old port. The local and remote
        // port fields *and their rights* are swapped on arrival, so we need to
        // swap them back first.
        forward.header.msgh_bits = (request.header.msgh_bits & ~MACH_MSGH_BITS_PORTS_MASK) |
            MACH_MSGH_BITS(MACH_MSGH_BITS_LOCAL(request.header.msgh_bits),
                           MACH_MSGH_BITS_REMOTE(request.header.msgh_bits));
        forward.header.msgh_local_port = forward.header.msgh_remote_port;
        forward.header.msgh_remote_port = previous.port;
        ret = mach_msg(&forward.header, MACH_SEND_MSG, forward.header.msgh_size, 0,
                       MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
        if (ret != MACH_MSG_SUCCESS)
            MOZ_CRASH("MachExceptionHandler: Failed to forward to the previous handler!");
    } else {
        // There was no previous task-level exception handler, so defer to the
        // host level one instead. We set the return code to KERN_FAILURE to
        // indicate that we did not handle the exception.
        // The reply message ID is always the request ID + 100.
        ExceptionReply reply = {};
        reply.header.msgh_bits =
            MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request.header.msgh_bits), 0);
        reply.header.msgh_size = static_cast<mach_msg_size_t>(sizeof(reply));
        reply.header.msgh_remote_port = request.header.msgh_remote_port;
        reply.header.msgh_local_port = MACH_PORT_NULL;
        reply.header.msgh_id = request.header.msgh_id + 100;
        reply.NDR = request.NDR;
        reply.RetCode = KERN_FAILURE;
        ret = mach_msg(&reply.header, MACH_SEND_MSG, reply.header.msgh_size, 0,
                       MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
        if (ret != MACH_MSG_SUCCESS)
            MOZ_CRASH("MachExceptionHandler: Failed to forward to the host level!");
    }
}
Esempio n. 7
0
ipc_kmsg_t
ipc_kobject_server(
	ipc_kmsg_t	request)
{
	mach_msg_size_t reply_size;
	ipc_kmsg_t reply;
	kern_return_t kr;
	ipc_port_t *destp;
	mach_msg_format_0_trailer_t *trailer;
	register mig_hash_t *ptr;

	/*
	 * Find out corresponding mig_hash entry if any
	 */
	{
	    register int key = request->ikm_header->msgh_id;
	    register int i = MIG_HASH(key);
	    register int max_iter = mig_table_max_displ;
	
	    do
		ptr = &mig_buckets[i++ % MAX_MIG_ENTRIES];
	    while (key != ptr->num && ptr->num && --max_iter);

	    if (!ptr->routine || key != ptr->num) {
	        ptr = (mig_hash_t *)0;
		reply_size = mig_reply_size;
	    } else {
		reply_size = ptr->size;
#if	MACH_COUNTER
		ptr->callcount++;
#endif
	    }
	}

	/* round up for trailer size */
        reply_size += MAX_TRAILER_SIZE;
	reply = ipc_kmsg_alloc(reply_size);

	if (reply == IKM_NULL) {
		printf("ipc_kobject_server: dropping request\n");
		ipc_kmsg_destroy(request);
		return IKM_NULL;
	}

	/*
	 * Initialize reply message.
	 */
	{
#define	InP	((mach_msg_header_t *) request->ikm_header)
#define	OutP	((mig_reply_error_t *) reply->ikm_header)

	    /* 
	     * MIG should really assure no data leakage -
	     * but until it does, pessimistically zero the
	     * whole reply buffer.
	     */
	    bzero((void *)OutP, reply_size);

	    OutP->NDR = NDR_record;
	    OutP->Head.msgh_size = sizeof(mig_reply_error_t);

	    OutP->Head.msgh_bits =
		MACH_MSGH_BITS_SET(MACH_MSGH_BITS_LOCAL(InP->msgh_bits), 0, 0, 0);
	    OutP->Head.msgh_remote_port = InP->msgh_local_port;
	    OutP->Head.msgh_local_port = MACH_PORT_NULL;
	    OutP->Head.msgh_voucher_port = MACH_PORT_NULL;
	    OutP->Head.msgh_id = InP->msgh_id + 100;

#undef	InP
#undef	OutP
	}

	/*
	 * Find the routine to call, and call it
	 * to perform the kernel function
	 */
	{
	    if (ptr) {	
		(*ptr->routine)(request->ikm_header, reply->ikm_header);
		kernel_task->messages_received++;
	    }
	    else {
		if (!ipc_kobject_notify(request->ikm_header, reply->ikm_header)){
#if	MACH_IPC_TEST
		    printf("ipc_kobject_server: bogus kernel message, id=%d\n",
			request->ikm_header->msgh_id);
#endif	/* MACH_IPC_TEST */
		    _MIG_MSGID_INVALID(request->ikm_header->msgh_id);

		    ((mig_reply_error_t *) reply->ikm_header)->RetCode
			= MIG_BAD_ID;
		}
		else
		  kernel_task->messages_received++;
	    }
	    kernel_task->messages_sent++;
	}

	/*
	 *	Destroy destination. The following code differs from
	 *	ipc_object_destroy in that we release the send-once
	 *	right instead of generating a send-once notification
	 * 	(which would bring us here again, creating a loop).
	 *	It also differs in that we only expect send or
	 *	send-once rights, never receive rights.
	 *
	 *	We set msgh_remote_port to IP_NULL so that the kmsg
	 *	destroy routines don't try to destroy the port twice.
	 */
	destp = (ipc_port_t *) &request->ikm_header->msgh_remote_port;
	switch (MACH_MSGH_BITS_REMOTE(request->ikm_header->msgh_bits)) {
		case MACH_MSG_TYPE_PORT_SEND:
		    ipc_port_release_send(*destp);
		    break;
		
		case MACH_MSG_TYPE_PORT_SEND_ONCE:
		    ipc_port_release_sonce(*destp);
		    break;
		
		default:
		    panic("ipc_kobject_server: strange destination rights");
	}
	*destp = IP_NULL;

	/*
	 *	Destroy voucher.  The kernel MIG servers never take ownership
	 *	of vouchers sent in messages.  Swallow any such rights here.
	 */
	if (IP_VALID(request->ikm_voucher)) {
		assert(MACH_MSG_TYPE_PORT_SEND ==
		       MACH_MSGH_BITS_VOUCHER(request->ikm_header->msgh_bits));
		ipc_port_release_send(request->ikm_voucher);
		request->ikm_voucher = IP_NULL;
	}

        if (!(reply->ikm_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
           ((mig_reply_error_t *) reply->ikm_header)->RetCode != KERN_SUCCESS)
	 	kr = ((mig_reply_error_t *) reply->ikm_header)->RetCode;
	else
		kr = KERN_SUCCESS;

	if ((kr == KERN_SUCCESS) || (kr == MIG_NO_REPLY)) {
		/*
		 *	The server function is responsible for the contents
		 *	of the message.  The reply port right is moved
		 *	to the reply message, and we have deallocated
		 *	the destination port right, so we just need
		 *	to free the kmsg.
		 */
		ipc_kmsg_free(request);

	} else {
		/*
		 *	The message contents of the request are intact.
		 *	Destroy everthing except the reply port right,
		 *	which is needed in the reply message.
		 */
		request->ikm_header->msgh_local_port = MACH_PORT_NULL;
		ipc_kmsg_destroy(request);
	}

	if (kr == MIG_NO_REPLY) {
		/*
		 *	The server function will send a reply message
		 *	using the reply port right, which it has saved.
		 */

		ipc_kmsg_free(reply);

		return IKM_NULL;
	} else if (!IP_VALID((ipc_port_t)reply->ikm_header->msgh_remote_port)) {
		/*
		 *	Can't queue the reply message if the destination
		 *	(the reply port) isn't valid.
		 */

		ipc_kmsg_destroy(reply);

		return IKM_NULL;
	}

 	trailer = (mach_msg_format_0_trailer_t *)
		((vm_offset_t)reply->ikm_header + (int)reply->ikm_header->msgh_size);

 	trailer->msgh_sender = KERNEL_SECURITY_TOKEN;
 	trailer->msgh_trailer_type = MACH_MSG_TRAILER_FORMAT_0;
 	trailer->msgh_trailer_size = MACH_MSG_TRAILER_MINIMUM_SIZE;

	return reply;
}
Esempio n. 8
0
int
ethernet_demuxer (mach_msg_header_t *inp,
		  mach_msg_header_t *outp)
{
  struct net_rcv_msg *msg = (struct net_rcv_msg *) inp;
  struct sk_buff *skb;
  int datalen;
  struct ether_device *edev;
  struct device *dev = 0;
  mach_port_t local_port;

  if (inp->msgh_id != NET_RCV_MSG_ID)
    return 0;

  if (MACH_MSGH_BITS_LOCAL (inp->msgh_bits) ==
      MACH_MSG_TYPE_PROTECTED_PAYLOAD)
    {
      struct port_info *pi = ports_lookup_payload (NULL,
						   inp->msgh_protected_payload,
						   NULL);
      if (pi)
	{
	  local_port = pi->port_right;
	  ports_port_deref (pi);
	}
      else
	local_port = MACH_PORT_NULL;
    }
  else
    local_port = inp->msgh_local_port;

  for (edev = ether_dev; edev; edev = edev->next)
    if (local_port == edev->readptname)
      dev = &edev->dev;

  if (! dev)
    {
      if (inp->msgh_remote_port != MACH_PORT_NULL)
	mach_port_deallocate (mach_task_self (), inp->msgh_remote_port);
      return 1;
    }

  datalen = ETH_HLEN
    + msg->packet_type.msgt_number - sizeof (struct packet_header);

  pthread_mutex_lock (&net_bh_lock);
  skb = alloc_skb (datalen, GFP_ATOMIC);
  skb_put (skb, datalen);
  skb->dev = dev;

  /* Copy the two parts of the frame into the buffer. */
  memcpy (skb->data, msg->header, ETH_HLEN);
  memcpy (skb->data + ETH_HLEN,
	  msg->packet + sizeof (struct packet_header),
	  datalen - ETH_HLEN);

  /* Drop it on the queue. */
  skb->protocol = eth_type_trans (skb, dev);
  netif_rx (skb);
  pthread_mutex_unlock (&net_bh_lock);

  return 1;
}