assert(is_active(space));
assert(io_otype(object) == IOT_PORT);
- port = (ipc_port_t) object;
+ port = ip_object_to_port(object);
ip_lock(port);
if (!ip_active(port)) {
assert(entry != IE_NULL);
assert(entry->ie_bits & MACH_PORT_TYPE_RECEIVE);
- assert(port == (ipc_port_t) entry->ie_object);
+ assert(port == ip_object_to_port(entry->ie_object));
*namep = name;
*entryp = entry;
return TRUE;
}
- if (ipc_hash_lookup(space, (ipc_object_t) port, namep, entryp)) {
+ if (ipc_hash_lookup(space, ip_to_object(port), namep, entryp)) {
assert((entry = *entryp) != IE_NULL);
assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_SEND);
- assert(port == (ipc_port_t) entry->ie_object);
+ assert(port == ip_object_to_port(entry->ie_object));
return TRUE;
}
if (entry->ie_bits & MACH_PORT_TYPE_PORT_RIGHTS) {
ipc_port_request_index_t new_request;
- port = (ipc_port_t) entry->ie_object;
+ port = ip_object_to_port(entry->ie_object);
assert(port != IP_NULL);
if (!ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
{
ipc_port_t previous;
- assert(ip_active(port));
- assert(port == (ipc_port_t) entry->ie_object);
+ require_ip_active(port);
+ assert(port == ip_object_to_port(entry->ie_object));
if (entry->ie_request == IE_REQ_NONE) {
return IP_NULL;
ipc_entry_bits_t bits;
assert(is_active(space));
- assert(port == (ipc_port_t) entry->ie_object);
+ assert(port == ip_object_to_port(entry->ie_object));
ip_lock(port);
if (ip_active(port) ||
*/
if ((bits & MACH_PORT_TYPE_SEND) != 0) {
- ipc_hash_delete(space, (ipc_object_t)port, name, entry);
+ ipc_hash_delete(space, ip_to_object(port), name, entry);
}
/* convert entry to dead name */
break;
case MACH_PORT_TYPE_PORT_SET: {
- ipc_pset_t pset = (ipc_pset_t) entry->ie_object;
+ ipc_pset_t pset = ips_object_to_pset(entry->ie_object);
assert(entry->ie_request == IE_REQ_NONE);
assert(pset != IPS_NULL);
case MACH_PORT_TYPE_RECEIVE:
case MACH_PORT_TYPE_SEND_RECEIVE:
case MACH_PORT_TYPE_SEND_ONCE: {
- ipc_port_t port = (ipc_port_t) entry->ie_object;
+ ipc_port_t port = ip_object_to_port(entry->ie_object);
ipc_port_t request;
ipc_port_t nsrequest = IP_NULL;
mach_port_mscount_t mscount = 0;
ipc_port_destroy(port); /* clears receiver, consumes our ref, unlocks */
} else if (type & MACH_PORT_TYPE_SEND_ONCE) {
assert(port->ip_sorights > 0);
+ port->ip_reply_context = 0;
ip_unlock(port);
ipc_notify_send_once(port); /* consumes our ref */
break;
case MACH_PORT_TYPE_PORT_SET: {
- ipc_pset_t pset = (ipc_pset_t) entry->ie_object;
+ ipc_pset_t pset = ips_object_to_pset(entry->ie_object);
assert(entry->ie_request == IE_REQ_NONE);
assert(pset != IPS_NULL);
case MACH_PORT_TYPE_RECEIVE:
case MACH_PORT_TYPE_SEND_RECEIVE:
case MACH_PORT_TYPE_SEND_ONCE: {
- ipc_port_t port = (ipc_port_t) entry->ie_object;
+ ipc_port_t port = ip_object_to_port(entry->ie_object);
ipc_port_t nsrequest = IP_NULL;
mach_port_mscount_t mscount = 0;
ipc_port_t request;
assert(port != IP_NULL);
if (type == MACH_PORT_TYPE_SEND) {
- ipc_hash_delete(space, (ipc_object_t) port,
- name, entry);
+ ipc_hash_delete(space, ip_to_object(port), name, entry);
}
ip_lock(port);
}
if (type & MACH_PORT_TYPE_RECEIVE) {
- assert(ip_active(port));
+ require_ip_active(port);
assert(port->ip_receiver == space);
ipc_port_destroy(port); /* clears receiver, consumes our ref, unlocks */
} else if (type & MACH_PORT_TYPE_SEND_ONCE) {
assert(port->ip_sorights > 0);
+ port->ip_reply_context = 0;
ip_unlock(port);
ipc_notify_send_once(port); /* consumes our ref */
assert(IE_BITS_UREFS(bits) == 0);
assert(entry->ie_request == IE_REQ_NONE);
- pset = (ipc_pset_t) entry->ie_object;
+ pset = ips_object_to_pset(entry->ie_object);
assert(pset != IPS_NULL);
entry->ie_object = IO_NULL;
assert(IE_BITS_UREFS(bits) == 1);
- port = (ipc_port_t) entry->ie_object;
+ port = ip_object_to_port(entry->ie_object);
assert(port != IP_NULL);
if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
assert(port->ip_sorights > 0);
+ /*
+ * clear any reply context:
+ * no one will be sending the response b/c we are destroying
+ * the single, outstanding send once right.
+ */
+ port->ip_reply_context = 0;
+
request = ipc_right_request_cancel_macro(space, port, name, entry);
ip_unlock(port);
assert(IE_BITS_UREFS(bits) > 0);
- port = (ipc_port_t) entry->ie_object;
+ port = ip_object_to_port(entry->ie_object);
assert(port != IP_NULL);
if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
request = ipc_right_request_cancel_macro(space, port,
name, entry);
- ipc_hash_delete(space, (ipc_object_t) port,
- name, entry);
+ ipc_hash_delete(space, ip_to_object(port), name, entry);
ip_unlock(port);
entry->ie_object = IO_NULL;
assert(IE_BITS_UREFS(bits) > 0);
- port = (ipc_port_t) entry->ie_object;
+ port = ip_object_to_port(entry->ie_object);
assert(port != IP_NULL);
ip_lock(port);
- assert(ip_active(port));
+ require_ip_active(port);
assert(port->ip_receiver_name == name);
assert(port->ip_receiver == space);
assert(port->ip_srights > 0);
goto invalid_value;
}
- pset = (ipc_pset_t) entry->ie_object;
+ pset = ips_object_to_pset(entry->ie_object);
assert(pset != IPS_NULL);
entry->ie_object = IO_NULL;
ipc_port_t request = IP_NULL;
if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) {
- mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
+ if ((bits & MACH_PORT_TYPE_EX_RECEIVE) == 0) {
+ mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
+ }
goto invalid_right;
}
goto invalid_value;
}
- port = (ipc_port_t) entry->ie_object;
+ port = ip_object_to_port(entry->ie_object);
assert(port != IP_NULL);
/*
*/
ip_lock(port);
- assert(ip_active(port));
+ require_ip_active(port);
assert(port->ip_receiver_name == name);
assert(port->ip_receiver == space);
*/
ipc_entry_modified(space, name, entry);
entry->ie_bits &= ~MACH_PORT_TYPE_RECEIVE;
- ipc_hash_insert(space, (ipc_object_t) port,
+ entry->ie_bits |= MACH_PORT_TYPE_EX_RECEIVE;
+ ipc_hash_insert(space, ip_to_object(port),
name, entry);
ip_reference(port);
} else {
* port is destroyed "first".
*/
bits &= ~IE_BITS_TYPE_MASK;
- bits |= MACH_PORT_TYPE_DEAD_NAME;
+ bits |= (MACH_PORT_TYPE_DEAD_NAME | MACH_PORT_TYPE_EX_RECEIVE);
if (entry->ie_request) {
entry->ie_request = IE_REQ_NONE;
/* if urefs are pegged due to overflow, leave them pegged */
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
assert(IE_BITS_UREFS(bits) == 1);
- port = (ipc_port_t) entry->ie_object;
+ port = ip_object_to_port(entry->ie_object);
assert(port != IP_NULL);
if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
goto success;
}
+ /*
+ * clear any reply context:
+ * no one will be sending the response b/c we are destroying
+ * the single, outstanding send once right.
+ */
+ port->ip_reply_context = 0;
+
request = ipc_right_request_cancel_macro(space, port, name, entry);
ip_unlock(port);
mach_port_urefs_t urefs;
if (bits & MACH_PORT_TYPE_SEND_RIGHTS) {
- port = (ipc_port_t) entry->ie_object;
+ port = ip_object_to_port(entry->ie_object);
assert(port != IP_NULL);
if (!ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
if ((bits & MACH_PORT_TYPE_SEND) == 0) {
/* invalid right exception only when not live/dead confusion */
- if ((bits & MACH_PORT_TYPE_DEAD_NAME) == 0) {
+ if ((bits & MACH_PORT_TYPE_DEAD_NAME) == 0
+#if !defined(AE_MAKESENDRIGHT_FIXED)
+ /*
+ * AE tries to add single send right without knowing if it already owns one.
+ * But if it doesn't, it should own the receive right and delta should be 1.
+ */
+ && (((bits & MACH_PORT_TYPE_RECEIVE) == 0) || (delta != 1))
+#endif
+ ) {
mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
}
goto invalid_right;
/* maximum urefs for send is MACH_PORT_UREFS_MAX */
- port = (ipc_port_t) entry->ie_object;
+ port = ip_object_to_port(entry->ie_object);
assert(port != IP_NULL);
if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
request = ipc_right_request_cancel_macro(space, port,
name, entry);
- ipc_hash_delete(space, (ipc_object_t) port,
+ ipc_hash_delete(space, ip_to_object(port),
name, entry);
ip_unlock(port);
assert(is_active(space));
- if (((bits & MACH_PORT_TYPE_RECEIVE) == 0) ||
- (srdelta && ((bits & MACH_PORT_TYPE_SEND) == 0))) {
+ if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) {
+ is_write_unlock(space);
+
+ /* No exception if we used to have receive and held entry since */
+ if ((bits & MACH_PORT_TYPE_EX_RECEIVE) == 0) {
+ mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
+ }
+ return KERN_INVALID_RIGHT;
+ }
+
+ if (srdelta && (bits & MACH_PORT_TYPE_SEND) == 0) {
is_write_unlock(space);
mach_port_guard_exception(name, 0, 0, kGUARD_EXC_INVALID_RIGHT);
return KERN_INVALID_RIGHT;
goto invalid_value;
}
- port = (ipc_port_t) entry->ie_object;
+ port = ip_object_to_port(entry->ie_object);
assert(port != IP_NULL);
ip_lock(port);
- assert(ip_active(port));
+ require_ip_active(port);
assert(port->ip_receiver_name == name);
assert(port->ip_receiver == space);
*/
ipc_entry_modified(space, name, entry);
entry->ie_bits &= ~MACH_PORT_TYPE_RECEIVE;
- ipc_hash_insert(space, (ipc_object_t) port,
+ entry->ie_bits |= MACH_PORT_TYPE_EX_RECEIVE;
+ ipc_hash_insert(space, ip_to_object(port),
name, entry);
ip_reference(port);
} else {
* port is destroyed "first".
*/
bits &= ~IE_BITS_TYPE_MASK;
- bits |= MACH_PORT_TYPE_DEAD_NAME;
+ bits |= (MACH_PORT_TYPE_DEAD_NAME | MACH_PORT_TYPE_EX_RECEIVE);
if (entry->ie_request) {
entry->ie_request = IE_REQ_NONE;
if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
bits = entry->ie_bits;
request = entry->ie_request;
- port = (ipc_port_t) entry->ie_object;
+ port = ip_object_to_port(entry->ie_object);
if (bits & MACH_PORT_TYPE_RECEIVE) {
assert(IP_VALID(port));
if (request != IE_REQ_NONE) {
ip_lock(port);
- assert(ip_active(port));
+ require_ip_active(port);
type |= ipc_port_request_type(port, name, request);
ip_unlock(port);
}
}
/*
- * Routine: ipc_right_copyin_check
+ * Routine: ipc_right_copyin_check_reply
* Purpose:
- * Check if a subsequent ipc_right_copyin would succeed.
+ * Check if a subsequent ipc_right_copyin would succeed. Used only
+ * by ipc_kmsg_copyin_header to check if reply_port can be copied in.
+ * If the reply port is an immovable send right, it errors out.
* Conditions:
* The space is locked (read or write) and active.
*/
boolean_t
-ipc_right_copyin_check(
+ipc_right_copyin_check_reply(
__assert_only ipc_space_t space,
- __unused mach_port_name_t name,
- ipc_entry_t entry,
- mach_msg_type_name_t msgt_name)
+ mach_port_name_t reply_name,
+ ipc_entry_t reply_entry,
+ mach_msg_type_name_t reply_type)
{
ipc_entry_bits_t bits;
- ipc_port_t port;
+ ipc_port_t reply_port;
- bits = entry->ie_bits;
+ bits = reply_entry->ie_bits;
assert(is_active(space));
- switch (msgt_name) {
+ switch (reply_type) {
case MACH_MSG_TYPE_MAKE_SEND:
if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) {
return FALSE;
break;
case MACH_MSG_TYPE_MOVE_RECEIVE:
- if ((bits & MACH_PORT_TYPE_RECEIVE) == 0) {
- return FALSE;
- }
- if (io_kotype(entry->ie_object) != IKOT_NONE) {
- return FALSE;
- }
- port = (ipc_port_t) entry->ie_object;
- if (port->ip_specialreply) {
- return FALSE;
- }
- break;
+ /* ipc_kmsg_copyin_header already filters it out */
+ return FALSE;
case MACH_MSG_TYPE_COPY_SEND:
case MACH_MSG_TYPE_MOVE_SEND:
return FALSE;
}
- port = (ipc_port_t) entry->ie_object;
- assert(port != IP_NULL);
+ reply_port = ip_object_to_port(reply_entry->ie_object);
+ assert(reply_port != IP_NULL);
/*
* active status peek to avoid checks that will be skipped
* on copyin for dead ports. Lock not held, so will not be
* atomic (but once dead, there's no going back).
*/
- if (!ip_active(port)) {
+ if (!ip_active(reply_port)) {
break;
}
- if (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE) {
+ /*
+ * Can't copyin a send right that is marked immovable. This bit
+ * is set only during port creation and never unset. So it can
+ * be read without a lock.
+ */
+ if (reply_port->ip_immovable_send) {
+ mach_port_guard_exception(reply_name, 0, 0, kGUARD_EXC_IMMOVABLE);
+ return FALSE;
+ }
+
+ if (reply_type == MACH_MSG_TYPE_MOVE_SEND_ONCE) {
if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) {
return FALSE;
}
return TRUE;
}
+/*
+ * Routine: ipc_right_copyin_check_guard_locked
+ * Purpose:
+ * Check if the port is guarded and the guard
+ * value matches the one passed in the arguments.
+ * If MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND is set,
+ * check if the port is unguarded.
+ * Conditions:
+ * The port is locked.
+ * Returns:
+ * KERN_SUCCESS Port is either unguarded
+ * or guarded with expected value
+ * KERN_INVALID_ARGUMENT Port is either unguarded already or guard mismatch.
+ * This also raises a EXC_GUARD exception.
+ */
+static kern_return_t
+ipc_right_copyin_check_guard_locked(
+ mach_port_name_t name,
+ ipc_port_t port,
+ mach_port_context_t context,
+ mach_msg_guard_flags_t *guard_flags)
+{
+ mach_msg_guard_flags_t flags = *guard_flags;
+ if ((flags & MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND) && !port->ip_guarded && !context) {
+ return KERN_SUCCESS;
+ } else if (port->ip_guarded && (port->ip_context == context)) {
+ return KERN_SUCCESS;
+ }
+
+ /* Incorrect guard; Raise exception */
+ mach_port_guard_exception(name, context, port->ip_context, kGUARD_EXC_INCORRECT_GUARD);
+ return KERN_INVALID_ARGUMENT;
+}
+
/*
* Routine: ipc_right_copyin
* Purpose:
* Returns:
* KERN_SUCCESS Acquired an object, possibly IO_DEAD.
* KERN_INVALID_RIGHT Name doesn't denote correct right.
+ * KERN_INVALID_CAPABILITY Trying to move an kobject port or an immovable right
+ * KERN_INVALID_ARGUMENT Port is unguarded or guard mismatch
*/
kern_return_t
ipc_right_copyin(
- ipc_space_t space,
- mach_port_name_t name,
- ipc_entry_t entry,
- mach_msg_type_name_t msgt_name,
- ipc_right_copyin_flags_t flags,
- ipc_object_t *objectp,
- ipc_port_t *sorightp,
- ipc_port_t *releasep,
- int *assertcntp)
+ ipc_space_t space,
+ mach_port_name_t name,
+ ipc_entry_t entry,
+ mach_msg_type_name_t msgt_name,
+ ipc_right_copyin_flags_t flags,
+ ipc_object_t *objectp,
+ ipc_port_t *sorightp,
+ ipc_port_t *releasep,
+ int *assertcntp,
+ mach_port_context_t context,
+ mach_msg_guard_flags_t *guard_flags)
{
ipc_entry_bits_t bits;
ipc_port_t port;
+ kern_return_t kr;
+ boolean_t deadok = flags & IPC_RIGHT_COPYIN_FLAGS_DEADOK? TRUE : FALSE;
+ boolean_t allow_imm_send = flags & IPC_RIGHT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND? TRUE : FALSE;
*releasep = IP_NULL;
*assertcntp = 0;
- boolean_t deadok = (flags & IPC_RIGHT_COPYIN_FLAGS_DEADOK) ? TRUE : FALSE;
bits = entry->ie_bits;
goto invalid_right;
}
- port = (ipc_port_t) entry->ie_object;
+ port = ip_object_to_port(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);
+ ipc_port_make_send_locked(port);
ip_unlock(port);
- *objectp = (ipc_object_t) port;
+ *objectp = ip_to_object(port);
*sorightp = IP_NULL;
break;
}
goto invalid_right;
}
- port = (ipc_port_t) entry->ie_object;
+ port = ip_object_to_port(entry->ie_object);
assert(port != IP_NULL);
ip_lock(port);
- assert(ip_active(port));
+ require_ip_active(port);
assert(port->ip_receiver_name == name);
assert(port->ip_receiver == space);
- port->ip_sorights++;
- ip_reference(port);
+ ipc_port_make_sonce_locked(port);
ip_unlock(port);
- *objectp = (ipc_object_t) port;
+ *objectp = ip_to_object(port);
*sorightp = IP_NULL;
break;
}
* situation which is, "This is a valid receive right,
* but it's also a kobject and you can't move it."
*/
+ mach_port_guard_exception(name, 0, 0, kGUARD_EXC_IMMOVABLE);
return KERN_INVALID_CAPABILITY;
}
- port = (ipc_port_t) entry->ie_object;
+ port = ip_object_to_port(entry->ie_object);
assert(port != IP_NULL);
ip_lock(port);
- assert(ip_active(port));
+ require_ip_active(port);
assert(port->ip_receiver_name == name);
assert(port->ip_receiver == space);
+ if (port->ip_immovable_receive) {
+ assert(port->ip_receiver != ipc_space_kernel);
+ ip_unlock(port);
+ assert(current_task() != kernel_task);
+ mach_port_guard_exception(name, 0, 0, kGUARD_EXC_IMMOVABLE);
+ return KERN_INVALID_CAPABILITY;
+ }
+
+ if (guard_flags != NULL) {
+ kr = ipc_right_copyin_check_guard_locked(name, port, context, guard_flags);
+ if (kr != KERN_SUCCESS) {
+ ip_unlock(port);
+ return kr;
+ }
+ }
+
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);
- ipc_hash_insert(space, (ipc_object_t) port,
+ ipc_hash_insert(space, ip_to_object(port),
name, entry);
ip_reference(port);
} else {
entry->ie_object = IO_NULL;
}
entry->ie_bits = bits & ~MACH_PORT_TYPE_RECEIVE;
+ entry->ie_bits |= MACH_PORT_TYPE_EX_RECEIVE;
ipc_entry_modified(space, name, entry);
+ /* ipc_port_clear_receiver unguards the port and clears the ip_immovable_receive bit */
(void)ipc_port_clear_receiver(port, FALSE); /* don't destroy the port/mqueue */
+ if (guard_flags != NULL) {
+ /* this flag will be cleared during copyout */
+ *guard_flags = *guard_flags | MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND;
+ }
#if IMPORTANCE_INHERITANCE
/*
ip_unlock(port);
- *objectp = (ipc_object_t) port;
+ *objectp = ip_to_object(port);
*sorightp = request;
break;
}
assert(IE_BITS_UREFS(bits) > 0);
- port = (ipc_port_t) entry->ie_object;
+ port = ip_object_to_port(entry->ie_object);
assert(port != IP_NULL);
if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
goto invalid_right;
}
- assert(port->ip_srights > 0);
+ if (!allow_imm_send && port->ip_immovable_send) {
+ ip_unlock(port);
+ mach_port_guard_exception(name, 0, 0, kGUARD_EXC_IMMOVABLE);
+ return KERN_INVALID_CAPABILITY;
+ }
- port->ip_srights++;
- ip_reference(port);
+ ipc_port_copy_send_locked(port);
ip_unlock(port);
- *objectp = (ipc_object_t) port;
+ *objectp = ip_to_object(port);
*sorightp = IP_NULL;
break;
}
assert(IE_BITS_UREFS(bits) > 0);
- port = (ipc_port_t) entry->ie_object;
+ port = ip_object_to_port(entry->ie_object);
assert(port != IP_NULL);
if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
goto invalid_right;
}
- assert(port->ip_srights > 0);
+ if (!allow_imm_send && port->ip_immovable_send) {
+ ip_unlock(port);
+ mach_port_guard_exception(name, 0, 0, kGUARD_EXC_IMMOVABLE);
+ return KERN_INVALID_CAPABILITY;
+ }
if (IE_BITS_UREFS(bits) == 1) {
+ assert(port->ip_srights > 0);
if (bits & MACH_PORT_TYPE_RECEIVE) {
assert(port->ip_receiver_name == name);
assert(port->ip_receiver == space);
request = ipc_right_request_cancel_macro(space, port,
name, entry);
- ipc_hash_delete(space, (ipc_object_t) port,
+ ipc_hash_delete(space, ip_to_object(port),
name, entry);
entry->ie_object = IO_NULL;
/* transfer entry's reference to caller */
entry->ie_bits = bits & ~
(IE_BITS_UREFS_MASK | MACH_PORT_TYPE_SEND);
} else {
- port->ip_srights++;
- ip_reference(port);
+ ipc_port_copy_send_locked(port);
/* if urefs are pegged due to overflow, leave them pegged */
if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
entry->ie_bits = bits - 1; /* decrement urefs */
ipc_entry_modified(space, name, entry);
ip_unlock(port);
- *objectp = (ipc_object_t) port;
+ *objectp = ip_to_object(port);
*sorightp = request;
break;
}
assert(IE_BITS_UREFS(bits) > 0);
- port = (ipc_port_t) entry->ie_object;
+ port = ip_object_to_port(entry->ie_object);
assert(port != IP_NULL);
if (ipc_right_check(space, port, name, entry, flags)) {
goto invalid_right;
}
+ if (!allow_imm_send && port->ip_immovable_send) {
+ ip_unlock(port);
+ mach_port_guard_exception(name, 0, 0, kGUARD_EXC_IMMOVABLE);
+ return KERN_INVALID_CAPABILITY;
+ }
+
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
assert(IE_BITS_UREFS(bits) == 1);
assert(port->ip_sorights > 0);
entry->ie_bits = bits & ~
(IE_BITS_UREFS_MASK | MACH_PORT_TYPE_SEND_ONCE);
ipc_entry_modified(space, name, entry);
- *objectp = (ipc_object_t) port;
+ *objectp = ip_to_object(port);
*sorightp = request;
break;
}
return KERN_SUCCESS;
}
-/*
- * Routine: ipc_right_copyin_undo
- * Purpose:
- * Undoes the effects of an ipc_right_copyin
- * of a send/send-once right that is dead.
- * (Object is either IO_DEAD or a dead port.)
- * Conditions:
- * The space is write-locked and active.
- */
-
-void
-ipc_right_copyin_undo(
- ipc_space_t space,
- mach_port_name_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;
-
- bits = entry->ie_bits;
-
- assert(is_active(space));
-
- 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(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));
-
- 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(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);
- /* if urefs are pegged due to overflow, leave them pegged */
- if (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);
- /* if urefs are pegged due to overflow, leave them pegged */
- if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
- 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, IPC_RIGHT_COPYIN_FLAGS_NONE);
- /* object is dead so it is not locked */
- }
- ipc_entry_modified(space, name, entry);
- /* release the reference acquired by copyin */
-
- if (object != IO_DEAD) {
- io_release(object);
- }
-}
-
/*
* Routine: ipc_right_copyin_two_move_sends
* Purpose:
goto invalid_right;
}
- port = (ipc_port_t) entry->ie_object;
+ port = ip_object_to_port(entry->ie_object);
assert(port != IP_NULL);
if (ipc_right_check(space, port, name, entry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
}
/* port is locked and active */
- assert(port->ip_srights > 0);
+ if (urefs > 2) {
+ /*
+ * We are moving 2 urefs as naked send rights, which is decomposed as:
+ * - two copy sends (which doesn't affect the make send count)
+ * - decrementing the local urefs twice.
+ */
+ ipc_port_copy_send_locked(port);
+ ipc_port_copy_send_locked(port);
+ /* if urefs are pegged due to overflow, leave them pegged */
+ if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
+ entry->ie_bits = bits - 2; /* decrement urefs */
+ }
+ } else {
+ /*
+ * We have exactly 2 send rights for this port in this space,
+ * which means that we will liberate the naked send right held
+ * by this entry.
+ *
+ * However refcounting rules around entries are that naked send rights
+ * on behalf of spaces do not have an associated port reference,
+ * so we need to donate one ...
+ */
+ ipc_port_copy_send_locked(port);
- 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);
+ /* ... that we inject manually when the entry stays alive */
ip_reference(port);
} else {
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND);
+ /* ... that we steal from the entry when it dies */
request = ipc_right_request_cancel_macro(space, port,
name, entry);
- port->ip_srights++;
- ip_reference(port);
- ipc_hash_delete(space, (ipc_object_t) port,
+ ipc_hash_delete(space, ip_to_object(port),
name, entry);
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);
- /* if urefs are pegged due to overflow, leave them pegged */
- if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX) {
- entry->ie_bits = bits - 2; /* decrement urefs */
- }
}
ipc_entry_modified(space, name, entry);
ip_unlock(port);
- *objectp = (ipc_object_t) port;
+ *objectp = ip_to_object(port);
*sorightp = request;
return KERN_SUCCESS;
* Conditions:
* The space is write-locked and active.
* The object is returned with two refs/rights.
+ * Msgt_one refers to the dest_type
* Returns:
* KERN_SUCCESS Acquired an object.
* KERN_INVALID_RIGHT Name doesn't denote correct right(s).
*/
kern_return_t
ipc_right_copyin_two(
- ipc_space_t space,
- mach_port_name_t name,
- ipc_entry_t entry,
- mach_msg_type_name_t msgt_one,
- mach_msg_type_name_t msgt_two,
- ipc_object_t *objectp,
- ipc_port_t *sorightp,
- ipc_port_t *releasep)
+ ipc_space_t space,
+ mach_port_name_t name,
+ ipc_entry_t entry,
+ mach_msg_type_name_t msgt_one,
+ mach_msg_type_name_t msgt_two,
+ ipc_object_t *objectp,
+ ipc_port_t *sorightp,
+ ipc_port_t *releasep)
{
kern_return_t kr;
int assertcnt = 0;
assert(MACH_MSG_TYPE_PORT_ANY_SEND(msgt_one));
assert(MACH_MSG_TYPE_PORT_ANY_SEND(msgt_two));
-
- /*
- * Pre-validate the second disposition is possible all by itself.
- */
- if (!ipc_right_copyin_check(space, name, entry, msgt_two)) {
- return KERN_INVALID_CAPABILITY;
- }
-
/*
* This is a little tedious to make atomic, because
* there are 25 combinations of valid dispositions.
ipc_object_t object_two;
kr = ipc_right_copyin(space, name, entry,
- msgt_one, IPC_RIGHT_COPYIN_FLAGS_NONE,
+ msgt_one, IPC_RIGHT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND,
objectp, sorightp, releasep,
- &assertcnt);
+ &assertcnt, 0, NULL);
assert(assertcnt == 0);
if (kr != KERN_SUCCESS) {
return kr;
kr = ipc_right_copyin(space, name, entry,
msgt_two, IPC_RIGHT_COPYIN_FLAGS_NONE,
&object_two, sorightp, releasep,
- &assertcnt);
+ &assertcnt, 0, NULL);
assert(assertcnt == 0);
assert(kr == KERN_SUCCESS);
assert(*sorightp == IP_NULL);
}
kr = ipc_right_copyin(space, name, entry,
- msgt_name, IPC_RIGHT_COPYIN_FLAGS_NONE,
+ msgt_name, IPC_RIGHT_COPYIN_FLAGS_ALLOW_IMMOVABLE_SEND,
objectp, sorightp, releasep,
- &assertcnt);
+ &assertcnt, 0, NULL);
assert(assertcnt == 0);
if (kr != KERN_SUCCESS) {
return kr;
* that's OK. Neither right will be usable to send
* a message anyway.
*/
- (void)ipc_port_copy_send((ipc_port_t)*objectp);
+ (void)ipc_port_copy_send(ip_object_to_port(*objectp));
}
return KERN_SUCCESS;
* because user-reference overflow isn't a possibility.
*
* If copying out the object would cause the user-reference
- * count in the entry to overflow, and overflow is TRUE,
- * then instead the user-reference count is left pegged
- * to its maximum value and the copyout succeeds anyway.
+ * count in the entry to overflow, then the user-reference
+ * count is left pegged to its maximum value and the copyout
+ * succeeds anyway.
* Conditions:
* The space is write-locked and active.
* The object is locked and active.
mach_port_name_t name,
ipc_entry_t entry,
mach_msg_type_name_t msgt_name,
- __unused boolean_t overflow,
+ mach_port_context_t *context,
+ mach_msg_guard_flags_t *guard_flags,
ipc_object_t object)
{
ipc_entry_bits_t bits;
assert(io_active(object));
assert(entry->ie_object == object);
- port = (ipc_port_t) object;
+ port = ip_object_to_port(object);
switch (msgt_name) {
case MACH_MSG_TYPE_PORT_SEND_ONCE:
/* entry is locked holding ref, so can use port */
- ipc_hash_insert(space, (ipc_object_t) port,
- name, entry);
+ ipc_hash_insert(space, ip_to_object(port), name, entry);
}
entry->ie_bits = (bits | MACH_PORT_TYPE_SEND) + 1; /* increment urefs */
case MACH_MSG_TYPE_PORT_RECEIVE: {
ipc_port_t dest;
- turnstile_inheritor_t inheritor = TURNSTILE_INHERITOR_NULL;
- struct turnstile *ts = TURNSTILE_NULL;
-
#if IMPORTANCE_INHERITANCE
natural_t assertcnt = port->ip_impcount;
#endif /* IMPORTANCE_INHERITANCE */
port->ip_receiver_name = name;
port->ip_receiver = space;
- assert((bits & MACH_PORT_TYPE_RECEIVE) == 0);
+ struct knote *kn = current_thread()->ith_knote;
- /* Update the port's turnstile linkage to WL turnstile */
- ts = port_send_turnstile(port);
- if (ts) {
- struct knote *kn = current_thread()->ith_knote;
- if (ITH_KNOTE_VALID(kn, MACH_MSG_TYPE_PORT_RECEIVE)) {
- inheritor = filt_machport_stash_port(kn, port, NULL);
- if (inheritor) {
- turnstile_reference(inheritor);
- IMQ_SET_INHERITOR(&port->ip_messages, inheritor);
- }
+ if ((guard_flags != NULL) && ((*guard_flags & MACH_MSG_GUARD_FLAGS_IMMOVABLE_RECEIVE) != 0)) {
+ assert(port->ip_immovable_receive == 0);
+ port->ip_guarded = 1;
+ port->ip_strict_guard = 0;
+ /* pseudo receive shouldn't set the receive right as immovable in the sender's space */
+ if (kn != ITH_KNOTE_PSEUDO) {
+ port->ip_immovable_receive = 1;
}
- turnstile_reference(ts);
- turnstile_update_inheritor(ts, inheritor,
- (TURNSTILE_IMMEDIATE_UPDATE | TURNSTILE_INHERITOR_TURNSTILE));
+ port->ip_context = current_thread()->ith_msg_addr;
+ *context = port->ip_context;
+ *guard_flags = *guard_flags & ~MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND;
}
- imq_unlock(&port->ip_messages);
-
+ assert((bits & MACH_PORT_TYPE_RECEIVE) == 0);
if (bits & MACH_PORT_TYPE_SEND) {
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND);
assert(IE_BITS_UREFS(bits) > 0);
assert(port->ip_srights > 0);
-
- ip_unlock(port);
- ip_release(port);
-
- /* entry is locked holding ref, so can use port */
- ipc_hash_delete(space, (ipc_object_t) port, name, entry);
} else {
assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE);
assert(IE_BITS_UREFS(bits) == 0);
+ }
- /* transfer ref to entry */
- ip_unlock(port);
+ boolean_t sync_bootstrap_checkin = FALSE;
+ if (kn != ITH_KNOTE_PSEUDO && port->ip_sync_bootstrap_checkin) {
+ sync_bootstrap_checkin = TRUE;
}
- entry->ie_bits = bits | MACH_PORT_TYPE_RECEIVE;
- ipc_entry_modified(space, name, entry);
+ if (!ITH_KNOTE_VALID(kn, MACH_MSG_TYPE_PORT_RECEIVE)) {
+ kn = NULL;
+ }
+ ipc_port_adjust_port_locked(port, kn, sync_bootstrap_checkin);
+ /* port & message queue are unlocked */
- if (ts) {
- turnstile_update_inheritor_complete(ts, TURNSTILE_INTERLOCK_NOT_HELD);
- turnstile_deallocate_safe(ts);
+ if (bits & MACH_PORT_TYPE_SEND) {
+ ip_release(port);
+
+ /* entry is locked holding ref, so can use port */
+ ipc_hash_delete(space, ip_to_object(port), name, entry);
}
+ entry->ie_bits = bits | MACH_PORT_TYPE_RECEIVE;
+ ipc_entry_modified(space, name, entry);
if (dest != IP_NULL) {
#if IMPORTANCE_INHERITANCE
}
return KERN_SUCCESS;
}
-
-/*
- * Routine: ipc_right_rename
- * Purpose:
- * Transfer an entry from one name to another.
- * The old entry is deallocated.
- * Conditions:
- * The space is write-locked and active.
- * The new entry is unused. Upon return,
- * the space is unlocked.
- * Returns:
- * KERN_SUCCESS Moved entry to new name.
- */
-
-kern_return_t
-ipc_right_rename(
- ipc_space_t space,
- mach_port_name_t oname,
- ipc_entry_t oentry,
- mach_port_name_t nname,
- ipc_entry_t nentry)
-{
- ipc_port_request_index_t request = oentry->ie_request;
- ipc_entry_bits_t bits = oentry->ie_bits;
- ipc_object_t object = oentry->ie_object;
- ipc_port_t release_port = IP_NULL;
-
- assert(is_active(space));
- assert(oname != nname);
-
- /*
- * If IE_BITS_COMPAT, we can't allow the entry to be renamed
- * if the port is dead. (This would foil ipc_port_destroy.)
- * Instead we should fail because oentry shouldn't exist.
- * Note IE_BITS_COMPAT implies ie_request != 0.
- */
-
- if (request != IE_REQ_NONE) {
- ipc_port_t port;
-
- assert(bits & MACH_PORT_TYPE_PORT_RIGHTS);
- port = (ipc_port_t) object;
- assert(port != IP_NULL);
-
- if (ipc_right_check(space, port, oname, oentry, IPC_RIGHT_COPYIN_FLAGS_NONE)) {
- request = IE_REQ_NONE;
- object = IO_NULL;
- bits = oentry->ie_bits;
- release_port = port;
- assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
- assert(oentry->ie_request == IE_REQ_NONE);
- } else {
- /* port is locked and active */
-
- ipc_port_request_rename(port, request, oname, nname);
- ip_unlock(port);
- oentry->ie_request = IE_REQ_NONE;
- }
- }
-
- /* initialize nentry before letting ipc_hash_insert see it */
-
- assert((nentry->ie_bits & IE_BITS_RIGHT_MASK) == 0);
- nentry->ie_bits |= bits & IE_BITS_RIGHT_MASK;
- nentry->ie_request = request;
- nentry->ie_object = object;
-
- switch (IE_BITS_TYPE(bits)) {
- case MACH_PORT_TYPE_SEND: {
- ipc_port_t port;
-
- port = (ipc_port_t) object;
- assert(port != IP_NULL);
-
- /* remember, there are no other share entries possible */
- /* or we can't do the rename. Therefore we do not need */
- /* to check the other subspaces */
- ipc_hash_delete(space, (ipc_object_t) port, oname, oentry);
- ipc_hash_insert(space, (ipc_object_t) port, nname, nentry);
- break;
- }
-
- case MACH_PORT_TYPE_RECEIVE:
- case MACH_PORT_TYPE_SEND_RECEIVE: {
- ipc_port_t port;
-
- port = (ipc_port_t) object;
- assert(port != IP_NULL);
-
- ip_lock(port);
- imq_lock(&port->ip_messages);
- assert(ip_active(port));
- assert(port->ip_receiver_name == oname);
- assert(port->ip_receiver == space);
-
- port->ip_receiver_name = nname;
- imq_unlock(&port->ip_messages);
- ip_unlock(port);
- break;
- }
-
- case MACH_PORT_TYPE_PORT_SET: {
- ipc_pset_t pset;
-
- pset = (ipc_pset_t) object;
- assert(pset != IPS_NULL);
-
- ips_lock(pset);
- assert(ips_active(pset));
-
- ips_unlock(pset);
- break;
- }
-
- case MACH_PORT_TYPE_SEND_ONCE:
- case MACH_PORT_TYPE_DEAD_NAME:
- break;
-
- default:
- panic("ipc_right_rename: strange rights");
- }
-
- assert(oentry->ie_request == IE_REQ_NONE);
- oentry->ie_object = IO_NULL;
- ipc_entry_dealloc(space, oname, oentry);
- ipc_entry_modified(space, nname, nentry);
- is_write_unlock(space);
-
- if (release_port != IP_NULL) {
- ip_release(release_port);
- }
-
- return KERN_SUCCESS;
-}