kern_return_t ipc_right_info( ipc_space_t space, mach_port_t name, ipc_entry_t entry, mach_port_type_t *typep, mach_port_urefs_t *urefsp) { ipc_entry_bits_t bits = entry->ie_bits; ipc_port_request_index_t request; mach_port_type_t type; if (bits & MACH_PORT_TYPE_SEND_RIGHTS) { ipc_port_t port = (ipc_port_t) entry->ie_object; if (ipc_right_check(space, port, name, entry)) { bits = entry->ie_bits; assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); } else ip_unlock(port); } type = IE_BITS_TYPE(bits); request = entry->ie_request; if (request != 0) type |= MACH_PORT_TYPE_DNREQUEST; if (bits & IE_BITS_MAREQUEST) type |= MACH_PORT_TYPE_MAREQUEST; *typep = type; *urefsp = IE_BITS_UREFS(bits); return KERN_SUCCESS; }
/* * Get a label handle representing the given port's port label. */ kern_return_t mach_get_label( ipc_space_t space, mach_port_name_t name, mach_port_name_t *outlabel) { ipc_entry_t entry; ipc_port_t port; struct label outl; kern_return_t kr; int dead; if (!MACH_PORT_VALID(name)) return KERN_INVALID_NAME; /* Lookup the port name in the task's space. */ kr = ipc_right_lookup_write(space, name, &entry); if (kr != KERN_SUCCESS) return kr; port = (ipc_port_t) entry->ie_object; dead = ipc_right_check(space, port, name, entry); if (dead) { is_write_unlock(space); return KERN_INVALID_RIGHT; } /* port is now locked */ is_write_unlock(space); /* Make sure we are not dealing with a label handle. */ if (ip_kotype(port) == IKOT_LABELH) { /* already is a label handle! */ ip_unlock(port); return KERN_INVALID_ARGUMENT; } /* Copy the port label and stash it in a new label handle. */ mac_init_port_label(&outl); mac_copy_port_label(&port->ip_label, &outl); kr = labelh_new_user(space, &outl, outlabel); ip_unlock(port); return KERN_SUCCESS; }
kern_return_t mach_get_label_text( ipc_space_t space, mach_port_name_t name, vm_offset_t policies, vm_offset_t outlabel) { ipc_entry_t entry; kern_return_t kr; struct label *l; int dead; if (space == IS_NULL || space->is_task == NULL) return KERN_INVALID_TASK; if (!MACH_PORT_VALID(name)) return KERN_INVALID_NAME; kr = ipc_right_lookup_write(space, name, &entry); if (kr != KERN_SUCCESS) return kr; dead = ipc_right_check(space, entry->ie_object, name, entry); if (dead) { is_write_unlock(space); return KERN_INVALID_RIGHT; } /* object (port) is now locked */ is_write_unlock(space); l = io_getlabel(entry->ie_object); mac_externalize_port_label(l, policies, outlabel, 512, 0); io_unlocklabel(entry->ie_object); io_unlock(entry->ie_object); return KERN_SUCCESS; }
kern_return_t ipc_right_delta( ipc_space_t space, mach_port_t name, ipc_entry_t entry, mach_port_right_t right, mach_port_delta_t delta) { ipc_entry_bits_t bits = entry->ie_bits; assert(space->is_active); assert(right < MACH_PORT_RIGHT_NUMBER); /* Rights-specific restrictions and operations. */ switch (right) { case MACH_PORT_RIGHT_PORT_SET: { ipc_pset_t pset; if ((bits & MACH_PORT_TYPE_PORT_SET) == 0) goto invalid_right; assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_PORT_SET); assert(IE_BITS_UREFS(bits) == 0); assert((bits & IE_BITS_MAREQUEST) == 0); assert(entry->ie_request == 0); if (delta == 0) goto success; if (delta != -1) goto invalid_value; pset = (ipc_pset_t) entry->ie_object; assert(pset != IPS_NULL); entry->ie_object = IO_NULL; ipc_entry_dealloc(space, name, entry); ips_lock(pset); assert(ips_active(pset)); is_write_unlock(space); ipc_pset_destroy(pset); /* consumes ref, unlocks */ break; } case MACH_PORT_RIGHT_RECEIVE: { ipc_port_t port; ipc_port_t dnrequest = IP_NULL; if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) goto invalid_right; if (delta == 0) goto success; if (delta != -1) goto invalid_value; if (bits & IE_BITS_MAREQUEST) { bits &= ~IE_BITS_MAREQUEST; ipc_marequest_cancel(space, name); } port = (ipc_port_t) entry->ie_object; assert(port != IP_NULL); /* * The port lock is needed for ipc_right_dncancel; * otherwise, we wouldn't have to take the lock * until just before dropping the space lock. */ ip_lock(port); assert(ip_active(port)); assert(port->ip_receiver_name == name); assert(port->ip_receiver == space); if (bits & MACH_PORT_TYPE_SEND) { assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_RECEIVE); assert(IE_BITS_UREFS(bits) > 0); assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX); assert(port->ip_srights > 0); /* * The remaining send right turns into a * dead name. Notice we don't decrement * ip_srights, generate a no-senders notif, * or use ipc_right_dncancel, because the * port is destroyed "first". */ bits &= ~IE_BITS_TYPE_MASK; bits |= MACH_PORT_TYPE_DEAD_NAME; if (entry->ie_request != 0) { entry->ie_request = 0; bits++; /* increment urefs */ } entry->ie_bits = bits; entry->ie_object = IO_NULL; } else { assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE); assert(IE_BITS_UREFS(bits) == 0); dnrequest = ipc_right_dncancel_macro(space, port, name, entry); entry->ie_object = IO_NULL; ipc_entry_dealloc(space, name, entry); } is_write_unlock(space); ipc_port_clear_receiver(port); ipc_port_destroy(port); /* consumes ref, unlocks */ if (dnrequest != IP_NULL) ipc_notify_port_deleted(dnrequest, name); break; } case MACH_PORT_RIGHT_SEND_ONCE: { ipc_port_t port, dnrequest; if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) goto invalid_right; assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE); assert(IE_BITS_UREFS(bits) == 1); assert((bits & IE_BITS_MAREQUEST) == 0); if ((delta > 0) || (delta < -1)) goto invalid_value; port = (ipc_port_t) entry->ie_object; assert(port != IP_NULL); if (ipc_right_check(space, port, name, entry)) { assert(!(entry->ie_bits & MACH_PORT_TYPE_SEND_ONCE)); goto invalid_right; } /* port is locked and active */ assert(port->ip_sorights > 0); if (delta == 0) { ip_unlock(port); goto success; } dnrequest = ipc_right_dncancel_macro(space, port, name, entry); ip_unlock(port); entry->ie_object = IO_NULL; ipc_entry_dealloc(space, name, entry); is_write_unlock(space); ipc_notify_send_once(port); if (dnrequest != IP_NULL) ipc_notify_port_deleted(dnrequest, name); break; } case MACH_PORT_RIGHT_DEAD_NAME: { mach_port_urefs_t urefs; if (bits & MACH_PORT_TYPE_SEND_RIGHTS) { ipc_port_t port; port = (ipc_port_t) entry->ie_object; assert(port != IP_NULL); if (!ipc_right_check(space, port, name, entry)) { /* port is locked and active */ ip_unlock(port); goto invalid_right; } bits = entry->ie_bits; } else if ((bits & MACH_PORT_TYPE_DEAD_NAME) == 0) goto invalid_right; assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); assert(IE_BITS_UREFS(bits) > 0); assert((bits & IE_BITS_MAREQUEST) == 0); assert(entry->ie_object == IO_NULL); assert(entry->ie_request == 0); urefs = IE_BITS_UREFS(bits); if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta)) goto invalid_value; if (MACH_PORT_UREFS_OVERFLOW(urefs, delta)) goto urefs_overflow; if ((urefs + delta) == 0) ipc_entry_dealloc(space, name, entry); else entry->ie_bits = bits + delta; is_write_unlock(space); break; } case MACH_PORT_RIGHT_SEND: { mach_port_urefs_t urefs; ipc_port_t port; ipc_port_t dnrequest = IP_NULL; ipc_port_t nsrequest = IP_NULL; mach_port_mscount_t mscount = 0; /* '=0' to shut up lint */ if ((bits & MACH_PORT_TYPE_SEND) == 0) goto invalid_right; /* maximum urefs for send is MACH_PORT_UREFS_MAX-1 */ urefs = IE_BITS_UREFS(bits); if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta)) goto invalid_value; if (MACH_PORT_UREFS_OVERFLOW(urefs+1, delta)) goto urefs_overflow; port = (ipc_port_t) entry->ie_object; assert(port != IP_NULL); if (ipc_right_check(space, port, name, entry)) { assert((entry->ie_bits & MACH_PORT_TYPE_SEND) == 0); goto invalid_right; } /* port is locked and active */ assert(port->ip_srights > 0); if ((urefs + delta) == 0) { if (--port->ip_srights == 0) { nsrequest = port->ip_nsrequest; if (nsrequest != IP_NULL) { port->ip_nsrequest = IP_NULL; mscount = port->ip_mscount; } } if (bits & MACH_PORT_TYPE_RECEIVE) { assert(port->ip_receiver_name == name); assert(port->ip_receiver == space); assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_RECEIVE); entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK| MACH_PORT_TYPE_SEND); } else { assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND); dnrequest = ipc_right_dncancel_macro( space, port, name, entry); ipc_reverse_remove(space, (ipc_object_t) port); if (bits & IE_BITS_MAREQUEST) ipc_marequest_cancel(space, name); ip_release(port); entry->ie_object = IO_NULL; ipc_entry_dealloc(space, name, entry); } } else entry->ie_bits = bits + delta; ip_unlock(port); /* even if dropped a ref, port is active */ is_write_unlock(space); if (nsrequest != IP_NULL) ipc_notify_no_senders(nsrequest, mscount); if (dnrequest != IP_NULL) ipc_notify_port_deleted(dnrequest, name); break; } default: #if MACH_ASSERT assert(!"ipc_right_delta: strange right"); #else panic("ipc_right_delta: strange right"); #endif } return KERN_SUCCESS; success: is_write_unlock(space); return KERN_SUCCESS; invalid_right: is_write_unlock(space); return KERN_INVALID_RIGHT; invalid_value: is_write_unlock(space); return KERN_INVALID_VALUE; urefs_overflow: is_write_unlock(space); return KERN_UREFS_OVERFLOW; }
kern_return_t ipc_right_dealloc( ipc_space_t space, mach_port_t name, ipc_entry_t entry) { ipc_entry_bits_t bits = entry->ie_bits; mach_port_type_t type = IE_BITS_TYPE(bits); assert(space->is_active); switch (type) { case MACH_PORT_TYPE_DEAD_NAME: { dead_name: assert(IE_BITS_UREFS(bits) > 0); assert(entry->ie_request == 0); assert(entry->ie_object == IO_NULL); assert((bits & IE_BITS_MAREQUEST) == 0); if (IE_BITS_UREFS(bits) == 1) ipc_entry_dealloc(space, name, entry); else entry->ie_bits = bits-1; /* decrement urefs */ is_write_unlock(space); break; } case MACH_PORT_TYPE_SEND_ONCE: { ipc_port_t port, dnrequest; assert(IE_BITS_UREFS(bits) == 1); assert((bits & IE_BITS_MAREQUEST) == 0); port = (ipc_port_t) entry->ie_object; assert(port != IP_NULL); if (ipc_right_check(space, port, name, entry)) { bits = entry->ie_bits; assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); goto dead_name; } /* port is locked and active */ assert(port->ip_sorights > 0); dnrequest = ipc_right_dncancel_macro(space, port, name, entry); ip_unlock(port); entry->ie_object = IO_NULL; ipc_entry_dealloc(space, name, entry); is_write_unlock(space); ipc_notify_send_once(port); if (dnrequest != IP_NULL) ipc_notify_port_deleted(dnrequest, name); break; } case MACH_PORT_TYPE_SEND: { ipc_port_t port; ipc_port_t dnrequest = IP_NULL; ipc_port_t nsrequest = IP_NULL; mach_port_mscount_t mscount = 0; /* '=0' to shut up lint */ assert(IE_BITS_UREFS(bits) > 0); port = (ipc_port_t) entry->ie_object; assert(port != IP_NULL); if (ipc_right_check(space, port, name, entry)) { bits = entry->ie_bits; assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); goto dead_name; } /* port is locked and active */ assert(port->ip_srights > 0); if (IE_BITS_UREFS(bits) == 1) { if (--port->ip_srights == 0) { nsrequest = port->ip_nsrequest; if (nsrequest != IP_NULL) { port->ip_nsrequest = IP_NULL; mscount = port->ip_mscount; } } dnrequest = ipc_right_dncancel_macro(space, port, name, entry); ipc_reverse_remove(space, (ipc_object_t) port); if (bits & IE_BITS_MAREQUEST) ipc_marequest_cancel(space, name); ip_release(port); entry->ie_object = IO_NULL; ipc_entry_dealloc(space, name, entry); } else entry->ie_bits = bits-1; /* decrement urefs */ ip_unlock(port); /* even if dropped a ref, port is active */ is_write_unlock(space); if (nsrequest != IP_NULL) ipc_notify_no_senders(nsrequest, mscount); if (dnrequest != IP_NULL) ipc_notify_port_deleted(dnrequest, name); break; } case MACH_PORT_TYPE_SEND_RECEIVE: { ipc_port_t port; ipc_port_t nsrequest = IP_NULL; mach_port_mscount_t mscount = 0; /* '=0' to shut up lint */ assert(IE_BITS_UREFS(bits) > 0); 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); assert(port->ip_srights > 0); if (IE_BITS_UREFS(bits) == 1) { if (--port->ip_srights == 0) { nsrequest = port->ip_nsrequest; if (nsrequest != IP_NULL) { port->ip_nsrequest = IP_NULL; mscount = port->ip_mscount; } } entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK| MACH_PORT_TYPE_SEND); } else entry->ie_bits = bits-1; /* decrement urefs */ ip_unlock(port); is_write_unlock(space); if (nsrequest != IP_NULL) ipc_notify_no_senders(nsrequest, mscount); break; } default: is_write_unlock(space); return KERN_INVALID_RIGHT; } return KERN_SUCCESS; }
kern_return_t ipc_right_dnrequest( ipc_space_t space, mach_port_t name, boolean_t immediate, ipc_port_t notify, ipc_port_t *previousp) { ipc_port_t previous; for (;;) { ipc_entry_t entry; ipc_entry_bits_t bits; kern_return_t kr; kr = ipc_right_lookup_write(space, name, &entry); if (kr != KERN_SUCCESS) return kr; /* space is write-locked and active */ bits = entry->ie_bits; if (bits & MACH_PORT_TYPE_PORT_RIGHTS) { ipc_port_t port; ipc_port_request_index_t request; port = (ipc_port_t) entry->ie_object; assert(port != IP_NULL); if (!ipc_right_check(space, port, name, entry)) { /* port is locked and active */ if (notify == IP_NULL) { previous = ipc_right_dncancel_macro( space, port, name, entry); ip_unlock(port); is_write_unlock(space); break; } /* * If a registered soright exists, * want to atomically switch with it. * If ipc_port_dncancel finds us a * soright, then the following * ipc_port_dnrequest will reuse * that slot, so we are guaranteed * not to unlock and retry. */ previous = ipc_right_dncancel_macro(space, port, name, entry); kr = ipc_port_dnrequest(port, name, notify, &request); if (kr != KERN_SUCCESS) { assert(previous == IP_NULL); is_write_unlock(space); kr = ipc_port_dngrow(port); /* port is unlocked */ if (kr != KERN_SUCCESS) return kr; continue; } assert(request != 0); ip_unlock(port); entry->ie_request = request; is_write_unlock(space); break; } bits = entry->ie_bits; assert(bits & MACH_PORT_TYPE_DEAD_NAME); } if ((bits & MACH_PORT_TYPE_DEAD_NAME) && immediate && (notify != IP_NULL)) { mach_port_urefs_t urefs = IE_BITS_UREFS(bits); assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); assert(urefs > 0); if (MACH_PORT_UREFS_OVERFLOW(urefs, 1)) { is_write_unlock(space); return KERN_UREFS_OVERFLOW; } entry->ie_bits = bits + 1; /* increment urefs */ is_write_unlock(space); ipc_notify_dead_name(notify, name); previous = IP_NULL; break; } is_write_unlock(space); if (bits & MACH_PORT_TYPE_PORT_OR_DEAD) return KERN_INVALID_ARGUMENT; else return KERN_INVALID_RIGHT; } *previousp = previous; return KERN_SUCCESS; }
kern_return_t ipc_right_copyin_two( ipc_space_t space, mach_port_t name, ipc_entry_t entry, ipc_object_t *objectp, ipc_port_t *sorightp) { ipc_entry_bits_t bits = entry->ie_bits; mach_port_urefs_t urefs; ipc_port_t port; ipc_port_t dnrequest = IP_NULL; assert(space->is_active); if ((bits & MACH_PORT_TYPE_SEND) == 0) goto invalid_right; urefs = IE_BITS_UREFS(bits); if (urefs < 2) goto invalid_right; port = (ipc_port_t) entry->ie_object; assert(port != IP_NULL); if (ipc_right_check(space, port, name, entry)) { goto invalid_right; } /* port is locked and active */ assert(port->ip_srights > 0); if (urefs == 2) { if (bits & MACH_PORT_TYPE_RECEIVE) { assert(port->ip_receiver_name == name); assert(port->ip_receiver == space); assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_RECEIVE); port->ip_srights++; ip_reference(port); ip_reference(port); } else { assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND); dnrequest = ipc_right_dncancel_macro(space, port, name, entry); ipc_reverse_remove(space, (ipc_object_t) port); if (bits & IE_BITS_MAREQUEST) ipc_marequest_cancel(space, name); port->ip_srights++; ip_reference(port); entry->ie_object = IO_NULL; } entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK|MACH_PORT_TYPE_SEND); } else { port->ip_srights += 2; ip_reference(port); ip_reference(port); entry->ie_bits = bits-2; /* decrement urefs */ } ip_unlock(port); *objectp = (ipc_object_t) port; *sorightp = dnrequest; return KERN_SUCCESS; invalid_right: return KERN_INVALID_RIGHT; }
void ipc_right_copyin_undo( ipc_space_t space, mach_port_t name, ipc_entry_t entry, mach_msg_type_name_t msgt_name, ipc_object_t object, ipc_port_t soright) { ipc_entry_bits_t bits = entry->ie_bits; assert(space->is_active); assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) || (msgt_name == MACH_MSG_TYPE_COPY_SEND) || (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE)); if (soright != IP_NULL) { assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) || (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE)); assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE); assert(entry->ie_object == IO_NULL); assert(object != IO_DEAD); entry->ie_bits = ((bits &~ IE_BITS_RIGHT_MASK) | MACH_PORT_TYPE_DEAD_NAME | 2); } else if (IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE) { assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) || (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE)); assert(entry->ie_object == IO_NULL); entry->ie_bits = ((bits &~ IE_BITS_RIGHT_MASK) | MACH_PORT_TYPE_DEAD_NAME | 1); } else if (IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME) { assert(entry->ie_object == IO_NULL); assert(object == IO_DEAD); assert(IE_BITS_UREFS(bits) > 0); if (msgt_name != MACH_MSG_TYPE_COPY_SEND) { assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX); entry->ie_bits = bits+1; /* increment urefs */ } } else { assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) || (msgt_name == MACH_MSG_TYPE_COPY_SEND)); assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND); assert(object != IO_DEAD); assert(entry->ie_object == object); assert(IE_BITS_UREFS(bits) > 0); if (msgt_name != MACH_MSG_TYPE_COPY_SEND) { assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX-1); entry->ie_bits = bits+1; /* increment urefs */ } /* * May as well convert the entry to a dead name. * (Or if it is a compat entry, destroy it.) */ (void) ipc_right_check(space, (ipc_port_t) object, name, entry); /* object is dead so it is not locked */ } /* release the reference acquired by copyin */ if (object != IO_DEAD) ipc_object_release(object); }
kern_return_t ipc_right_copyin( ipc_space_t space, mach_port_t name, ipc_entry_t entry, mach_msg_type_name_t msgt_name, boolean_t deadok, ipc_object_t *objectp, ipc_port_t *sorightp) { ipc_entry_bits_t bits = entry->ie_bits; assert(space->is_active); switch (msgt_name) { case MACH_MSG_TYPE_MAKE_SEND: { ipc_port_t port; if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) goto invalid_right; 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); port->ip_mscount++; port->ip_srights++; ip_reference(port); ip_unlock(port); *objectp = (ipc_object_t) port; *sorightp = IP_NULL; break; } case MACH_MSG_TYPE_MAKE_SEND_ONCE: { ipc_port_t port; if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) goto invalid_right; 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); port->ip_sorights++; ip_reference(port); ip_unlock(port); *objectp = (ipc_object_t) port; *sorightp = IP_NULL; break; } case MACH_MSG_TYPE_MOVE_RECEIVE: { ipc_port_t port; ipc_port_t dnrequest = IP_NULL; if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) goto invalid_right; 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); if (bits & MACH_PORT_TYPE_SEND) { assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_RECEIVE); assert(IE_BITS_UREFS(bits) > 0); assert(port->ip_srights > 0); entry->ie_name = name; ipc_reverse_insert(space, (ipc_object_t) port, entry); ip_reference(port); } else { assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE); assert(IE_BITS_UREFS(bits) == 0); dnrequest = ipc_right_dncancel_macro(space, port, name, entry); if (bits & IE_BITS_MAREQUEST) ipc_marequest_cancel(space, name); entry->ie_object = IO_NULL; } entry->ie_bits = bits &~ MACH_PORT_TYPE_RECEIVE; ipc_port_clear_receiver(port); port->ip_receiver_name = MACH_PORT_NULL; port->ip_destination = IP_NULL; /* * Clear the protected payload field to retain * the behavior of mach_msg. */ ipc_port_flag_protected_payload_clear(port); ip_unlock(port); *objectp = (ipc_object_t) port; *sorightp = dnrequest; break; } case MACH_MSG_TYPE_COPY_SEND: { ipc_port_t port; if (bits & MACH_PORT_TYPE_DEAD_NAME) goto copy_dead; /* allow for dead send-once rights */ if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) goto invalid_right; assert(IE_BITS_UREFS(bits) > 0); port = (ipc_port_t) entry->ie_object; assert(port != IP_NULL); if (ipc_right_check(space, port, name, entry)) { bits = entry->ie_bits; goto copy_dead; } /* port is locked and active */ if ((bits & MACH_PORT_TYPE_SEND) == 0) { assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE); assert(port->ip_sorights > 0); ip_unlock(port); goto invalid_right; } assert(port->ip_srights > 0); port->ip_srights++; ip_reference(port); ip_unlock(port); *objectp = (ipc_object_t) port; *sorightp = IP_NULL; break; } case MACH_MSG_TYPE_MOVE_SEND: { ipc_port_t port; ipc_port_t dnrequest = IP_NULL; if (bits & MACH_PORT_TYPE_DEAD_NAME) goto move_dead; /* allow for dead send-once rights */ if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) goto invalid_right; assert(IE_BITS_UREFS(bits) > 0); port = (ipc_port_t) entry->ie_object; assert(port != IP_NULL); if (ipc_right_check(space, port, name, entry)) { bits = entry->ie_bits; goto move_dead; } /* port is locked and active */ if ((bits & MACH_PORT_TYPE_SEND) == 0) { assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE); assert(port->ip_sorights > 0); ip_unlock(port); goto invalid_right; } assert(port->ip_srights > 0); if (IE_BITS_UREFS(bits) == 1) { if (bits & MACH_PORT_TYPE_RECEIVE) { assert(port->ip_receiver_name == name); assert(port->ip_receiver == space); assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_RECEIVE); ip_reference(port); } else { assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND); dnrequest = ipc_right_dncancel_macro( space, port, name, entry); ipc_reverse_remove(space, (ipc_object_t) port); if (bits & IE_BITS_MAREQUEST) ipc_marequest_cancel(space, name); entry->ie_object = IO_NULL; } entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK|MACH_PORT_TYPE_SEND); } else { port->ip_srights++; ip_reference(port); entry->ie_bits = bits-1; /* decrement urefs */ } ip_unlock(port); *objectp = (ipc_object_t) port; *sorightp = dnrequest; break; } case MACH_MSG_TYPE_MOVE_SEND_ONCE: { ipc_port_t port; ipc_port_t dnrequest; if (bits & MACH_PORT_TYPE_DEAD_NAME) goto move_dead; /* allow for dead send rights */ if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0) goto invalid_right; assert(IE_BITS_UREFS(bits) > 0); port = (ipc_port_t) entry->ie_object; assert(port != IP_NULL); if (ipc_right_check(space, port, name, entry)) { bits = entry->ie_bits; goto move_dead; } /* port is locked and active */ if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) { assert(bits & MACH_PORT_TYPE_SEND); assert(port->ip_srights > 0); ip_unlock(port); goto invalid_right; } assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE); assert(IE_BITS_UREFS(bits) == 1); assert((bits & IE_BITS_MAREQUEST) == 0); assert(port->ip_sorights > 0); dnrequest = ipc_right_dncancel_macro(space, port, name, entry); ip_unlock(port); entry->ie_object = IO_NULL; entry->ie_bits = bits &~ MACH_PORT_TYPE_SEND_ONCE; *objectp = (ipc_object_t) port; *sorightp = dnrequest; break; } default: #if MACH_ASSERT assert(!"ipc_right_copyin: strange rights"); #else panic("ipc_right_copyin: strange rights"); #endif } return KERN_SUCCESS; copy_dead: assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); assert(IE_BITS_UREFS(bits) > 0); assert((bits & IE_BITS_MAREQUEST) == 0); assert(entry->ie_request == 0); assert(entry->ie_object == 0); if (!deadok) goto invalid_right; *objectp = IO_DEAD; *sorightp = IP_NULL; return KERN_SUCCESS; move_dead: assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME); assert(IE_BITS_UREFS(bits) > 0); assert((bits & IE_BITS_MAREQUEST) == 0); assert(entry->ie_request == 0); assert(entry->ie_object == 0); if (!deadok) goto invalid_right; if (IE_BITS_UREFS(bits) == 1) entry->ie_bits = bits &~ MACH_PORT_TYPE_DEAD_NAME; else entry->ie_bits = bits-1; /* decrement urefs */ *objectp = IO_DEAD; *sorightp = IP_NULL; return KERN_SUCCESS; invalid_right: return KERN_INVALID_RIGHT; }