X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/0a7de7458d150b5d4dffc935ba399be265ef0a1a..cb3231590a3c94ab4375e2228bd5e86b0cf1ad7e:/osfmk/ipc/ipc_right.c diff --git a/osfmk/ipc/ipc_right.c b/osfmk/ipc/ipc_right.c index 08a79c300..00a256e17 100644 --- a/osfmk/ipc/ipc_right.c +++ b/osfmk/ipc/ipc_right.c @@ -204,7 +204,7 @@ ipc_right_reverse( 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)) { @@ -221,17 +221,17 @@ ipc_right_reverse( 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; } @@ -301,7 +301,7 @@ ipc_right_request_alloc( 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)) { @@ -448,8 +448,8 @@ ipc_right_request_cancel( { 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; @@ -511,7 +511,7 @@ ipc_right_check( 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) || @@ -545,7 +545,7 @@ ipc_right_check( */ 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 */ @@ -622,7 +622,7 @@ ipc_right_terminate( 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); @@ -637,7 +637,7 @@ ipc_right_terminate( 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; @@ -673,6 +673,7 @@ ipc_right_terminate( 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 */ @@ -736,7 +737,7 @@ ipc_right_destroy( 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); @@ -756,7 +757,7 @@ ipc_right_destroy( 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; @@ -764,8 +765,7 @@ ipc_right_destroy( 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); @@ -813,12 +813,13 @@ ipc_right_destroy( } 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 */ @@ -885,7 +886,7 @@ ipc_right_dealloc( 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; @@ -929,7 +930,7 @@ dead_name: 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)) { @@ -941,6 +942,13 @@ dead_name: 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); @@ -965,7 +973,7 @@ dead_name: 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)) { @@ -988,8 +996,7 @@ dead_name: 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; @@ -1022,11 +1029,11 @@ dead_name: 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); @@ -1129,7 +1136,7 @@ ipc_right_delta( 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; @@ -1147,7 +1154,9 @@ ipc_right_delta( 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; } @@ -1159,7 +1168,7 @@ ipc_right_delta( goto invalid_value; } - port = (ipc_port_t) entry->ie_object; + port = ip_object_to_port(entry->ie_object); assert(port != IP_NULL); /* @@ -1169,7 +1178,7 @@ ipc_right_delta( */ ip_lock(port); - assert(ip_active(port)); + require_ip_active(port); assert(port->ip_receiver_name == name); assert(port->ip_receiver == space); @@ -1202,7 +1211,8 @@ ipc_right_delta( */ 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 { @@ -1214,7 +1224,7 @@ ipc_right_delta( * 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 */ @@ -1255,7 +1265,7 @@ ipc_right_delta( 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)) { @@ -1277,6 +1287,13 @@ ipc_right_delta( 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); @@ -1298,7 +1315,7 @@ ipc_right_delta( 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)) { @@ -1372,7 +1389,15 @@ ipc_right_delta( 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; @@ -1380,7 +1405,7 @@ ipc_right_delta( /* 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)) { @@ -1444,7 +1469,7 @@ ipc_right_delta( 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); @@ -1541,8 +1566,17 @@ ipc_right_destruct( 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; @@ -1552,11 +1586,11 @@ ipc_right_destruct( 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); @@ -1639,7 +1673,8 @@ ipc_right_destruct( */ 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 { @@ -1651,7 +1686,7 @@ ipc_right_destruct( * 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) { @@ -1719,14 +1754,14 @@ ipc_right_info( 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); } @@ -1761,27 +1796,29 @@ ipc_right_info( } /* - * 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; @@ -1795,17 +1832,8 @@ ipc_right_copyin_check( 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: @@ -1818,19 +1846,29 @@ ipc_right_copyin_check( 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; } @@ -1850,6 +1888,40 @@ ipc_right_copyin_check( 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: @@ -1871,26 +1943,32 @@ ipc_right_copyin_check( * 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; @@ -1902,20 +1980,17 @@ ipc_right_copyin( 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; } @@ -1925,19 +2000,18 @@ ipc_right_copyin( 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; } @@ -1963,24 +2037,41 @@ ipc_right_copyin( * 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 { @@ -1992,9 +2083,15 @@ ipc_right_copyin( 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 /* @@ -2017,7 +2114,7 @@ ipc_right_copyin( ip_unlock(port); - *objectp = (ipc_object_t) port; + *objectp = ip_to_object(port); *sorightp = request; break; } @@ -2035,7 +2132,7 @@ ipc_right_copyin( 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)) { @@ -2053,13 +2150,16 @@ ipc_right_copyin( 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; } @@ -2079,7 +2179,7 @@ ipc_right_copyin( 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)) { @@ -2097,9 +2197,14 @@ ipc_right_copyin( 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); @@ -2113,7 +2218,7 @@ ipc_right_copyin( 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 */ @@ -2121,8 +2226,7 @@ ipc_right_copyin( 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 */ @@ -2132,7 +2236,7 @@ ipc_right_copyin( ipc_entry_modified(space, name, entry); ip_unlock(port); - *objectp = (ipc_object_t) port; + *objectp = ip_to_object(port); *sorightp = request; break; } @@ -2152,7 +2256,7 @@ ipc_right_copyin( 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)) { @@ -2175,6 +2279,12 @@ ipc_right_copyin( 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); @@ -2186,7 +2296,7 @@ ipc_right_copyin( 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; } @@ -2235,93 +2345,6 @@ move_dead: 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: @@ -2365,7 +2388,7 @@ ipc_right_copyin_two_move_sends( 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)) { @@ -2374,45 +2397,57 @@ ipc_right_copyin_two_move_sends( } /* 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; @@ -2430,6 +2465,7 @@ invalid_right: * 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). @@ -2437,14 +2473,14 @@ invalid_right: */ 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; @@ -2452,14 +2488,6 @@ ipc_right_copyin_two( 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. @@ -2491,9 +2519,9 @@ ipc_right_copyin_two( 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; @@ -2512,7 +2540,7 @@ ipc_right_copyin_two( 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); @@ -2550,9 +2578,9 @@ ipc_right_copyin_two( } 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; @@ -2563,7 +2591,7 @@ ipc_right_copyin_two( * 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; @@ -2580,9 +2608,9 @@ ipc_right_copyin_two( * 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. @@ -2597,7 +2625,8 @@ ipc_right_copyout( 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; @@ -2610,7 +2639,7 @@ ipc_right_copyout( 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: @@ -2673,8 +2702,7 @@ ipc_right_copyout( /* 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 */ @@ -2683,9 +2711,6 @@ ipc_right_copyout( 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 */ @@ -2699,50 +2724,49 @@ ipc_right_copyout( 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 @@ -2769,137 +2793,3 @@ ipc_right_copyout( } 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; -}