/* * fileport_notify * * Description: Handle a no-senders notification for a fileport. Unless * the message is spoofed, destroys the port and releases * its reference on the fileglob. * * Parameters: msg A Mach no-senders notification message. */ void fileport_notify(mach_msg_header_t *msg) { mach_no_senders_notification_t *notification = (void *)msg; ipc_port_t port = notification->not_header.msgh_remote_port; struct fileglob *fg = NULL; if (!IP_VALID(port)) panic("Invalid port passed to fileport_notify()\n"); ip_lock(port); fg = (struct fileglob *)port->ip_kobject; if (!ip_active(port)) panic("Inactive port passed to fileport_notify()\n"); if (ip_kotype(port) != IKOT_FILEPORT) panic("Port of type other than IKOT_FILEPORT passed to fileport_notify()\n"); if (fg == NULL) panic("fileport without an assocated fileglob\n"); if (port->ip_srights == 0) { ip_unlock(port); fileport_releasefg(fg); ipc_port_dealloc_kernel(port); } else { ip_unlock(port); } return; }
ipc_port_t retrieve_task_self_fast( register task_t task) { register ipc_port_t port; assert(task == current_task()); itk_lock(task); assert(task->itk_self != IP_NULL); if ((port = task->itk_sself) == task->itk_self) { /* no interposing */ ip_lock(port); assert(ip_active(port)); ip_reference(port); port->ip_srights++; ip_unlock(port); } else port = ipc_port_copy_send(port); itk_unlock(task); return port; }
EXTERN ipc_port_t iokit_alloc_object_port( io_object_t obj, ipc_kobject_type_t type ) { ipc_port_t notify; ipc_port_t port; do { /* Allocate port, keeping a reference for it. */ port = ipc_port_alloc_kernel(); if( port == IP_NULL) continue; /* set kobject & type */ // iokit_add_reference( obj ); ipc_kobject_set( port, (ipc_kobject_t) obj, type); /* Request no-senders notifications on the port. */ ip_lock( port); notify = ipc_port_make_sonce_locked( port); ipc_port_nsrequest( port, 1, notify, ¬ify); /* port unlocked */ assert( notify == IP_NULL); gIOKitPortCount++; } while( FALSE); return( port ); }
void ipc_port_release_send( ipc_port_t port) { ipc_port_t nsrequest = IP_NULL; mach_port_mscount_t mscount; if (!IP_VALID(port)) return; ip_lock(port); if (!ip_active(port)) { ip_unlock(port); ip_release(port); return; } assert(port->ip_srights > 0); if (--port->ip_srights == 0 && port->ip_nsrequest != IP_NULL) { nsrequest = port->ip_nsrequest; port->ip_nsrequest = IP_NULL; mscount = port->ip_mscount; ip_unlock(port); ip_release(port); ipc_notify_no_senders(nsrequest, mscount); } else { ip_unlock(port); ip_release(port); } }
/* Called when an ACKMESSAGE packet is received. <name> indicates * the flipc name of the port holding the messages to be acknowledged. * <msg_count> indicates the number of messages being acked for this node:port. */ static void flipc_cmd_ack(flipc_ack_msg_t fmsg, mach_node_t node __unused, uint32_t flags __unused) { unsigned int msg_count = fmsg->msg_count; thread_t thread = current_thread(); boolean_t kick = FALSE; flipc_port_t fport = (flipc_port_t)mnl_obj_lookup(fmsg->mnl.object); ipc_port_t lport = fport->lport; ip_lock(lport); ipc_mqueue_t lport_mq = &lport->ip_messages; imq_lock(lport_mq); assert(fport->peek_count >= msg_count); // Can't ack what we haven't peeked! while (msg_count--) { ipc_mqueue_select_on_thread(lport_mq, IMQ_NULL, 0, 0, thread); fport->peek_count--; kick |= ipc_kmsg_delayed_destroy(thread->ith_kmsg); } imq_unlock(lport_mq); ip_unlock(lport); if (kick) ipc_kmsg_reap_delayed(); }
ipc_port_t retrieve_thread_self_fast( thread_t thread) { register ipc_port_t port; assert(thread == current_thread()); thread_mtx_lock(thread); assert(thread->ith_self != IP_NULL); if ((port = thread->ith_sself) == thread->ith_self) { /* no interposing */ ip_lock(port); assert(ip_active(port)); ip_reference(port); port->ip_srights++; ip_unlock(port); } else port = ipc_port_copy_send(port); thread_mtx_unlock(thread); return port; }
/* * Routine: convert_port_to_locked_task * Purpose: * Internal helper routine to convert from a port to a locked * task. Used by several routines that try to convert from a * task port to a reference on some task related object. * Conditions: * Nothing locked, blocking OK. */ task_t convert_port_to_locked_task(ipc_port_t port) { int try_failed_count = 0; while (IP_VALID(port)) { task_t task; ip_lock(port); if (!ip_active(port) || (ip_kotype(port) != IKOT_TASK)) { ip_unlock(port); return TASK_NULL; } task = (task_t) port->ip_kobject; assert(task != TASK_NULL); /* * Normal lock ordering puts task_lock() before ip_lock(). * Attempt out-of-order locking here. */ if (task_lock_try(task)) { ip_unlock(port); return(task); } try_failed_count++; ip_unlock(port); mutex_pause(try_failed_count); } return TASK_NULL; }
void host_notify_port_destroy( ipc_port_t port) { host_notify_t entry; mutex_lock(&host_notify_lock); ip_lock(port); if (ip_kotype(port) == IKOT_HOST_NOTIFY) { entry = (host_notify_t)port->ip_kobject; assert(entry != NULL); ipc_kobject_set_atomically(port, IKO_NULL, IKOT_NONE); ip_unlock(port); assert(entry->port == port); remqueue(NULL, (queue_entry_t)entry); mutex_unlock(&host_notify_lock); zfree(host_notify_zone, (vm_offset_t)entry); ipc_port_release_sonce(port); return; } ip_unlock(port); mutex_unlock(&host_notify_lock); }
void mach_port_gst_helper( ipc_pset_t pset, ipc_port_t port, ipc_entry_num_t maxnames, mach_port_name_t *names, ipc_entry_num_t *actualp) { ipc_pset_t ip_pset; mach_port_name_t name; assert(port != IP_NULL); ip_lock(port); assert(ip_active(port)); name = port->ip_receiver_name; assert(name != MACH_PORT_NULL); ip_unlock(port); if (ipc_pset_member(pset, port)) { ipc_entry_num_t actual = *actualp; if (actual < maxnames) names[actual] = name; *actualp = actual+1; } }
void mk_timer_port_destroy( ipc_port_t port) { mk_timer_t timer = NULL; ip_lock(port); if (ip_kotype(port) == IKOT_TIMER) { timer = (mk_timer_t)port->ip_kobject; assert(timer != NULL); ipc_kobject_set_atomically(port, IKO_NULL, IKOT_NONE); simple_lock(&timer->lock); assert(timer->port == port); } ip_unlock(port); if (timer != NULL) { if (thread_call_cancel(&timer->call_entry)) timer->active--; timer->is_armed = FALSE; timer->is_dead = TRUE; if (timer->active == 0) { simple_unlock(&timer->lock); zfree(mk_timer_zone, timer); ipc_port_release_send(port); return; } simple_unlock(&timer->lock); } }
/* * fileport_alloc * * Description: Obtain a send right for the given fileglob, which must be * referenced. * * Parameters: fg A fileglob. * * Returns: Port of type IKOT_FILEPORT with fileglob set as its kobject. * Port is returned with a send right. */ ipc_port_t fileport_alloc(struct fileglob *fg) { ipc_port_t fileport; ipc_port_t sendport; ipc_port_t notifyport; fileport = ipc_port_alloc_kernel(); if (fileport == IP_NULL) { goto out; } ipc_kobject_set(fileport, (ipc_kobject_t)fg, IKOT_FILEPORT); notifyport = ipc_port_make_sonce(fileport); ip_lock(fileport); /* unlocked by ipc_port_nsrequest */ ipc_port_nsrequest(fileport, 1, notifyport, ¬ifyport); sendport = ipc_port_make_send(fileport); if (!IP_VALID(sendport)) { panic("Couldn't allocate send right for fileport!\n"); } out: return fileport; }
ipc_port_t ipc_port_lookup_notify( ipc_space_t space, mach_port_name_t name) { ipc_port_t port; ipc_entry_t entry; assert(is_active(space)); entry = ipc_entry_lookup(space, name); if (entry == IE_NULL) return IP_NULL; if ((entry->ie_bits & MACH_PORT_TYPE_RECEIVE) == 0) return IP_NULL; port = (ipc_port_t) entry->ie_object; assert(port != IP_NULL); ip_lock(port); assert(ip_active(port)); assert(port->ip_receiver_name == name); assert(port->ip_receiver == space); ip_reference(port); port->ip_sorights++; ip_unlock(port); return port; }
/* * Routine: convert_port_to_mig_object [interface] * Purpose: * Base implementation of MIG intrans routine to convert from * an incoming port reference to a new reference on the * underlying object. A new reference must be created, because * the port's reference could go away asynchronously. * Returns: * NULL - Not an active MIG object port or iid not supported * Otherwise, a reference to the underlying MIG interface * Conditions: * Nothing locked. */ mig_object_t convert_port_to_mig_object( ipc_port_t port, const MIGIID *iid) { mig_object_t mig_object; void *ppv; if (!IP_VALID(port)) return NULL; ip_lock(port); if (!ip_active(port) || (ip_kotype(port) != IKOT_MIG)) { ip_unlock(port); return NULL; } /* * Our port points to some MIG object interface. Now * query it to get a reference to the desired interface. */ ppv = NULL; mig_object = (mig_object_t)port->ip_kobject; mig_object->pVtbl->QueryInterface((IMIGObject *)mig_object, iid, &ppv); ip_unlock(port); return (mig_object_t)ppv; }
boolean_t ipc_right_check( ipc_space_t space, ipc_port_t port, mach_port_t name, ipc_entry_t entry) { ipc_entry_bits_t bits; assert(space->is_active); assert(port == (ipc_port_t) entry->ie_object); ip_lock(port); if (ip_active(port)) return FALSE; ip_unlock(port); /* this was either a pure send right or a send-once right */ bits = entry->ie_bits; assert((bits & MACH_PORT_TYPE_RECEIVE) == 0); assert(IE_BITS_UREFS(bits) > 0); if (bits & MACH_PORT_TYPE_SEND) { assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND); /* clean up msg-accepted request */ if (bits & IE_BITS_MAREQUEST) { bits &= ~IE_BITS_MAREQUEST; ipc_marequest_cancel(space, name); } ipc_reverse_remove(space, (ipc_object_t) port); } else { assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE); assert(IE_BITS_UREFS(bits) == 1); assert((bits & IE_BITS_MAREQUEST) == 0); } ipc_port_release(port); /* convert entry to dead name */ bits = (bits &~ IE_BITS_TYPE_MASK) | MACH_PORT_TYPE_DEAD_NAME; if (entry->ie_request != 0) { assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX); entry->ie_request = 0; bits++; /* increment urefs */ } entry->ie_bits = bits; entry->ie_object = IO_NULL; return TRUE; }
void exception( integer_t _exception, integer_t code, integer_t subcode) { ipc_thread_t self = current_thread(); ipc_port_t exc_port; if (_exception == KERN_SUCCESS) panic("exception"); /* * Optimized version of retrieve_thread_exception. */ ith_lock(self); assert(self->ith_self != IP_NULL); exc_port = self->ith_exception; if (!IP_VALID(exc_port)) { ith_unlock(self); exception_try_task(_exception, code, subcode); /*NOTREACHED*/ } ip_lock(exc_port); ith_unlock(self); if (!ip_active(exc_port)) { ip_unlock(exc_port); exception_try_task(_exception, code, subcode); /*NOTREACHED*/ } /* * Make a naked send right for the exception port. */ ip_reference(exc_port); exc_port->ip_srights++; ip_unlock(exc_port); /* * If this exception port doesn't work, * we will want to try the task's exception port. * Indicate this by saving the exception state. */ self->ith_exc = _exception; self->ith_exc_code = code; self->ith_exc_subcode = subcode; exception_raise(exc_port, retrieve_thread_self_fast(self), retrieve_task_self_fast(self->task), _exception, code, subcode); /*NOTREACHED*/ }
void mach_port_names_helper( ipc_port_timestamp_t timestamp, ipc_entry_t entry, mach_port_name_t name, mach_port_name_t *names, mach_port_type_t *types, ipc_entry_num_t *actualp, ipc_space_t space) { ipc_entry_bits_t bits; ipc_port_request_index_t request; mach_port_type_t type; ipc_entry_num_t actual; bits = entry->ie_bits; request = entry->ie_request; if (bits & MACH_PORT_TYPE_SEND_RIGHTS) { ipc_port_t port; boolean_t died; port = (ipc_port_t) entry->ie_object; assert(port != IP_NULL); /* * The timestamp serializes mach_port_names * with ipc_port_destroy. If the port died, * but after mach_port_names started, pretend * that it isn't dead. */ ip_lock(port); died = (!ip_active(port) && IP_TIMESTAMP_ORDER(port->ip_timestamp, timestamp)); ip_unlock(port); if (died) { /* pretend this is a dead-name entry */ bits &= ~(IE_BITS_TYPE_MASK); bits |= MACH_PORT_TYPE_DEAD_NAME; if (request != 0) bits++; request = 0; } } type = IE_BITS_TYPE(bits); if (request != 0) type |= MACH_PORT_TYPE_DNREQUEST; actual = *actualp; names[actual] = name; types[actual] = type; *actualp = actual+1; }
/* * Routine: ipc_kobject_set * Purpose: * Make a port represent a kernel object of the given type. * The caller is responsible for handling refs for the * kernel object, if necessary. * Conditions: * Nothing locked. The port must be active if setting * a kobject linkage. Clearing a linkage is OK on an * inactive port. */ void ipc_kobject_set( ipc_port_t port, ipc_kobject_t kobject, ipc_kobject_type_t type) { ip_lock(port); ipc_kobject_set_atomically(port, kobject, type); ip_unlock(port); }
/* * Routine: convert_mig_object_to_port [interface] * Purpose: * Base implementation of MIG outtrans routine to convert from * a mig object reference to a new send right on the object's * port. The object reference is consumed. * Returns: * IP_NULL - Null MIG object supplied * Otherwise, a newly made send right for the port * Conditions: * Nothing locked. */ ipc_port_t convert_mig_object_to_port( mig_object_t mig_object) { ipc_port_t port; boolean_t deallocate = TRUE; if (mig_object == MIG_OBJECT_NULL) return IP_NULL; port = mig_object->port; while ((port == IP_NULL) || ((port = ipc_port_make_send(port)) == IP_NULL)) { ipc_port_t previous; /* * Either the port was never set up, or it was just * deallocated out from under us by the no-senders * processing. In either case, we must: * Attempt to make one * Arrange for no senders * Try to atomically register it with the object * Destroy it if we are raced. */ port = ipc_port_alloc_kernel(); ip_lock(port); ipc_kobject_set_atomically(port, (ipc_kobject_t) mig_object, IKOT_MIG); /* make a sonce right for the notification */ port->ip_sorights++; ip_reference(port); ipc_port_nsrequest(port, 1, port, &previous); /* port unlocked */ assert(previous == IP_NULL); if (OSCompareAndSwapPtr((void *)IP_NULL, (void *)port, (void * volatile *)&mig_object->port)) { deallocate = FALSE; } else { ipc_port_dealloc_kernel(port); port = mig_object->port; } } if (deallocate) mig_object->pVtbl->Release((IMIGObject *)mig_object); return (port); }
boolean_t ipc_right_reverse( ipc_space_t space, ipc_object_t object, mach_port_t *namep, ipc_entry_t *entryp) { ipc_port_t port; mach_port_t name; ipc_entry_t entry; /* would switch on io_otype to handle multiple types of object */ assert(space->is_active); assert(io_otype(object) == IOT_PORT); port = (ipc_port_t) object; ip_lock(port); if (!ip_active(port)) { ip_unlock(port); return FALSE; } if (port->ip_receiver == space) { name = port->ip_receiver_name; assert(name != MACH_PORT_NULL); entry = ipc_entry_lookup(space, name); assert(entry != IE_NULL); assert(entry->ie_bits & MACH_PORT_TYPE_RECEIVE); assert(port == (ipc_port_t) entry->ie_object); *namep = name; *entryp = entry; return TRUE; } if ((*entryp = ipc_reverse_lookup(space, (ipc_object_t) port))) { *namep = (*entryp)->ie_name; assert((entry = *entryp) != IE_NULL); assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_SEND); assert(port == (ipc_port_t) entry->ie_object); return TRUE; } ip_unlock(port); return FALSE; }
void exception_try_task( integer_t _exception, integer_t code, integer_t subcode) { ipc_thread_t self = current_thread(); task_t task = self->task; ipc_port_t exc_port; /* * Optimized version of retrieve_task_exception. */ itk_lock(task); assert(task->itk_self != IP_NULL); exc_port = task->itk_exception; if (!IP_VALID(exc_port)) { itk_unlock(task); exception_no_server(); /*NOTREACHED*/ } ip_lock(exc_port); itk_unlock(task); if (!ip_active(exc_port)) { ip_unlock(exc_port); exception_no_server(); /*NOTREACHED*/ } /* * Make a naked send right for the exception port. */ ip_reference(exc_port); exc_port->ip_srights++; ip_unlock(exc_port); /* * This is the thread's last chance. * Clear the saved exception state. */ self->ith_exc = KERN_SUCCESS; exception_raise(exc_port, retrieve_thread_self_fast(self), retrieve_task_self_fast(task), _exception, code, subcode); /*NOTREACHED*/ }
kern_return_t mach_set_port_label( ipc_space_t space, mach_port_name_t name, vm_offset_t labelstr) { #ifdef MAC ipc_entry_t entry; kern_return_t kr; struct label inl; int rc; if (space == IS_NULL || space->is_task == NULL) return KERN_INVALID_TASK; if (!MACH_PORT_VALID(name)) return KERN_INVALID_NAME; mac_init_port_label(&inl); rc = mac_internalize_port_label(&inl, labelstr); if (rc) return KERN_INVALID_ARGUMENT; kr = ipc_right_lookup_write(space, name, &entry); if (kr != KERN_SUCCESS) return kr; if (io_otype(entry->ie_object) != IOT_PORT) { is_write_unlock(space); return KERN_INVALID_RIGHT; } ipc_port_t port = (ipc_port_t) entry->ie_object; ip_lock(port); rc = mac_check_port_relabel(&space->is_task->maclabel, &port->ip_label, &inl); if (rc) kr = KERN_NO_ACCESS; else mac_copy_port_label(&inl, &port->ip_label); ip_unlock(port); is_write_unlock(space); return kr; #else return KERN_INVALID_ARGUMENT; #endif }
ipc_port_t ipc_port_make_sonce( ipc_port_t port) { assert(IP_VALID(port)); ip_lock(port); assert(ip_active(port)); port->ip_sorights++; ip_reference(port); ip_unlock(port); return port; }
static void host_notify_all( host_flavor_t notify_type, mach_msg_header_t *msg, mach_msg_size_t msg_size) { queue_t notify_queue = &host_notify_queue[notify_type]; mutex_lock(&host_notify_lock); if (!queue_empty(notify_queue)) { queue_head_t send_queue; host_notify_t entry; send_queue = *notify_queue; queue_init(notify_queue); send_queue.next->prev = &send_queue; send_queue.prev->next = &send_queue; msg->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0); msg->msgh_local_port = MACH_PORT_NULL; msg->msgh_id = host_notify_replyid[notify_type]; msg->msgh_reserved = 0; while ((entry = (host_notify_t)dequeue(&send_queue)) != NULL) { ipc_port_t port; port = entry->port; assert(port != IP_NULL); ip_lock(port); assert(ip_kotype(port) == IKOT_HOST_NOTIFY); assert(port->ip_kobject == (ipc_kobject_t)entry); ipc_kobject_set_atomically(port, IKO_NULL, IKOT_NONE); ip_unlock(port); mutex_unlock(&host_notify_lock); zfree(host_notify_zone, (vm_offset_t)entry); msg->msgh_remote_port = port; (void) mach_msg_send_from_kernel(msg, msg_size); mutex_lock(&host_notify_lock); } } mutex_unlock(&host_notify_lock); }
kern_return_t mach_port_kobject( ipc_space_t space, mach_port_name_t name, natural_t *typep, mach_vm_address_t *addrp) { ipc_entry_t entry; ipc_port_t port; kern_return_t kr; mach_vm_address_t kaddr; if (space == IS_NULL) return KERN_INVALID_TASK; kr = ipc_right_lookup_read(space, name, &entry); if (kr != KERN_SUCCESS) return kr; /* space is read-locked and active */ if ((entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE) == 0) { is_read_unlock(space); return KERN_INVALID_RIGHT; } __IGNORE_WCASTALIGN(port = (ipc_port_t) entry->ie_object); assert(port != IP_NULL); ip_lock(port); is_read_unlock(space); if (!ip_active(port)) { ip_unlock(port); return KERN_INVALID_RIGHT; } *typep = (unsigned int) ip_kotype(port); kaddr = (mach_vm_address_t)port->ip_kobject; ip_unlock(port); #if (DEVELOPMENT || DEBUG) if (0 != kaddr && is_ipc_kobject(*typep)) *addrp = VM_KERNEL_UNSLIDE_OR_PERM(kaddr); else #endif *addrp = 0; return KERN_SUCCESS; }
/* * Routine: ipc_kobject_set * Purpose: * Make a port represent a kernel object of the given type. * The caller is responsible for handling refs for the * kernel object, if necessary. * Conditions: * Nothing locked. The port must be active if setting * a kobject linkage. Clearing a linkage is OK on an * inactive port. */ void ipc_kobject_set( ipc_port_t port, ipc_kobject_t kobject, ipc_kobject_type_t type) { ip_lock(port); ipc_kobject_set_atomically(port, kobject, type); #if CONFIG_MACF_MACH mac_port_label_update_kobject (&port->ip_label, type); #endif ip_unlock(port); }
void ipc_port_release_sonce( ipc_port_t port) { assert(IP_VALID(port)); ip_lock(port); assert(port->ip_sorights > 0); port->ip_sorights--; ip_unlock(port); ip_release(port); }
/* * fileport_get_fileglob * * Description: Obtain the fileglob associated with a given port. * * Parameters: port A Mach port of type IKOT_FILEPORT. * * Returns: NULL The given Mach port did not reference a * fileglob. * !NULL The fileglob that is associated with the * Mach port. * * Notes: The caller must have a reference on the fileport. */ struct fileglob * fileport_port_to_fileglob(ipc_port_t port) { struct fileglob *fg = NULL; if (!IP_VALID(port)) return NULL; ip_lock(port); if (ip_active(port) && IKOT_FILEPORT == ip_kotype(port)) fg = (void *)port->ip_kobject; ip_unlock(port); return fg; }
processor_set_name_t convert_port_to_pset_name( ipc_port_t port) { boolean_t r; processor_set_t pset = PROCESSOR_SET_NULL; r = FALSE; while (!r && IP_VALID(port)) { ip_lock(port); r = ref_pset_port_locked(port, TRUE, &pset); /* port unlocked */ } return pset; }
void ipc_kobject_set( ipc_port_t port, ipc_kobject_t kobject, ipc_kobject_type_t type) { ip_lock(port); assert(ip_active(port) || (ip_kotype(port) == IKOT_PAGER_TERMINATING)); #if MACH_ASSERT port->ip_spares[2] = (port->ip_bits & IO_BITS_KOTYPE); #endif /* MACH_ASSERT */ port->ip_bits = (port->ip_bits &~ IO_BITS_KOTYPE) | type; port->ip_kobject = kobject; ip_unlock(port); }
/* * Routine: convert_port_to_clock_ctrl * Purpose: * Convert from a port to a clock. * Doesn't consume the port ref; produces a clock ref, * which may be null. * Conditions: * Nothing locked. */ clock_t convert_port_to_clock_ctrl( ipc_port_t port) { clock_t clock = CLOCK_NULL; if (IP_VALID(port)) { ip_lock(port); if (ip_active(port) && (ip_kotype(port) == IKOT_CLOCK_CTRL)) { clock = (clock_t) port->ip_kobject; } ip_unlock(port); } return (clock); }