boolean_t ipc_port_check_circularity( ipc_port_t port, ipc_port_t dest) { ipc_port_t base; #if IMPORTANCE_INHERITANCE task_t task = TASK_NULL; task_t release_task = TASK_NULL; int assertcnt = 0; #endif /* IMPORTANCE_INHERITANCE */ assert(port != IP_NULL); assert(dest != IP_NULL); if (port == dest) return TRUE; base = dest; /* * First try a quick check that can run in parallel. * No circularity if dest is not in transit. */ ip_lock(port); if (ip_lock_try(dest)) { if (!ip_active(dest) || (dest->ip_receiver_name != MACH_PORT_NULL) || (dest->ip_destination == IP_NULL)) goto not_circular; /* dest is in transit; further checking necessary */ ip_unlock(dest); } ip_unlock(port); ipc_port_multiple_lock(); /* massive serialization */ /* * Search for the end of the chain (a port not in transit), * acquiring locks along the way. */ for (;;) { ip_lock(base); if (!ip_active(base) || (base->ip_receiver_name != MACH_PORT_NULL) || (base->ip_destination == IP_NULL)) break; base = base->ip_destination; } /* all ports in chain from dest to base, inclusive, are locked */ if (port == base) { /* circularity detected! */ ipc_port_multiple_unlock(); /* port (== base) is in limbo */ assert(ip_active(port)); assert(port->ip_receiver_name == MACH_PORT_NULL); assert(port->ip_destination == IP_NULL); while (dest != IP_NULL) { ipc_port_t next; /* dest is in transit or in limbo */ assert(ip_active(dest)); assert(dest->ip_receiver_name == MACH_PORT_NULL); next = dest->ip_destination; ip_unlock(dest); dest = next; } return TRUE; } /* * The guarantee: lock port while the entire chain is locked. * Once port is locked, we can take a reference to dest, * add port to the chain, and unlock everything. */ ip_lock(port); ipc_port_multiple_unlock(); not_circular: /* port is in limbo */ assert(ip_active(port)); assert(port->ip_receiver_name == MACH_PORT_NULL); assert(port->ip_destination == IP_NULL); ip_reference(dest); port->ip_destination = dest; #if IMPORTANCE_INHERITANCE /* must have been in limbo or still bound to a task */ assert(port->ip_tempowner != 0); if (port->ip_taskptr != 0) { /* * We delayed dropping assertions from a specific task. * Cache that info now (we'll drop assertions and the * task reference below). */ release_task = port->ip_imp_task; port->ip_imp_task = TASK_NULL; port->ip_taskptr = 0; } assertcnt = port->ip_impcount; /* take the port out of limbo w.r.t. assertions */ port->ip_tempowner = 0; #endif /* IMPORTANCE_INHERITANCE */ /* now unlock chain */ ip_unlock(port); for (;;) { #if IMPORTANCE_INHERITANCE /* every port along chain track assertions behind it */ dest->ip_impcount += assertcnt; #endif /* IMPORTANCE_INHERITANCE */ if (dest == base) break; /* port is in transit */ assert(ip_active(dest)); assert(dest->ip_receiver_name == MACH_PORT_NULL); assert(dest->ip_destination != IP_NULL); #if IMPORTANCE_INHERITANCE assert(dest->ip_tempowner == 0); #endif /* IMPORTANCE_INHERITANCE */ port = dest->ip_destination; ip_unlock(dest); dest = port; } /* base is not in transit */ assert(!ip_active(base) || (base->ip_receiver_name != MACH_PORT_NULL) || (base->ip_destination == IP_NULL)); #if IMPORTANCE_INHERITANCE /* * Find the task to boost (if any). * We will boost "through" ports that don't know * about inheritance to deliver receive rights that * do. */ if (ip_active(base) && (assertcnt > 0)) { if (base->ip_tempowner != 0) { if (base->ip_taskptr != 0) /* specified tempowner task */ task = base->ip_imp_task; /* otherwise don't boost current task */ } else if (base->ip_receiver_name != MACH_PORT_NULL) { ipc_space_t space = base->ip_receiver; /* only spaces with boost-accepting tasks */ if (space->is_task != TASK_NULL && space->is_task->imp_receiver != 0) task = space->is_task; } /* take reference before unlocking base */ if (task != TASK_NULL) { assert(task->imp_receiver != 0); task_reference(task); } } #endif /* IMPORTANCE_INHERITANCE */ ip_unlock(base); #if IMPORTANCE_INHERITANCE /* * Transfer assertions now that the ports are unlocked. * Avoid extra overhead if transferring to/from the same task. */ boolean_t transfer_assertions = (task != release_task) ? TRUE : FALSE; if (task != TASK_NULL) { if (transfer_assertions) task_importance_hold_internal_assertion(task, assertcnt); task_deallocate(task); task = TASK_NULL; } if (release_task != TASK_NULL) { if (transfer_assertions) task_importance_drop_internal_assertion(release_task, assertcnt); task_deallocate(release_task); release_task = TASK_NULL; } #endif /* IMPORTANCE_INHERITANCE */ return FALSE; }
boolean_t ipc_port_importance_delta( ipc_port_t port, mach_port_delta_t delta) { ipc_port_t next, base; task_t task = TASK_NULL; boolean_t dropped = FALSE; if (delta == 0) return FALSE; base = port; /* if port is in transit, have to search for end of chain */ if (ip_active(port) && port->ip_destination != IP_NULL && port->ip_receiver_name == MACH_PORT_NULL) { dropped = TRUE; ip_unlock(port); ipc_port_multiple_lock(); /* massive serialization */ ip_lock(base); while(ip_active(base) && base->ip_destination != IP_NULL && base->ip_receiver_name == MACH_PORT_NULL) { base = base->ip_destination; ip_lock(base); } ipc_port_multiple_unlock(); } /* unlock down to the base, adding a boost at each level */ for (;;) { port->ip_impcount += delta; if (port == base) break; /* port is in transit */ assert(port->ip_tempowner == 0); next = port->ip_destination; ip_unlock(port); port = next; } /* find the task (if any) to boost according to the base */ if (ip_active(base)) { if (base->ip_tempowner != 0) { if (base->ip_taskptr != 0) task = base->ip_imp_task; /* otherwise don't boost */ } else if (base->ip_receiver_name != MACH_PORT_NULL) { ipc_space_t space = base->ip_receiver; /* only spaces with boost-accepting tasks */ if (space->is_task != TASK_NULL && space->is_task->imp_receiver != 0) task = space->is_task; } } /* * Only the base is locked. If we have to hold or drop task * importance assertions, we'll have to drop that lock as well. */ if (task != TASK_NULL) { /* take a reference before unlocking base */ assert(task->imp_receiver != 0); task_reference(task); ip_unlock(base); dropped = TRUE; if (delta > 0) task_importance_hold_internal_assertion(task, delta); else task_importance_drop_internal_assertion(task, -delta); task_deallocate(task); } else if (dropped == TRUE) { ip_unlock(base); } return dropped; }
boolean_t ipc_port_check_circularity( ipc_port_t port, ipc_port_t dest) { ipc_port_t base; assert(port != IP_NULL); assert(dest != IP_NULL); if (port == dest) return TRUE; base = dest; /* * First try a quick check that can run in parallel. * No circularity if dest is not in transit. */ ip_lock(port); if (ip_lock_try(dest)) { if (!ip_active(dest) || (dest->ip_receiver_name != MACH_PORT_NULL) || (dest->ip_destination == IP_NULL)) goto not_circular; /* dest is in transit; further checking necessary */ ip_unlock(dest); } ip_unlock(port); ipc_port_multiple_lock(); /* massive serialization */ /* * Search for the end of the chain (a port not in transit), * acquiring locks along the way. */ for (;;) { ip_lock(base); if (!ip_active(base) || (base->ip_receiver_name != MACH_PORT_NULL) || (base->ip_destination == IP_NULL)) break; base = base->ip_destination; } /* all ports in chain from dest to base, inclusive, are locked */ if (port == base) { /* circularity detected! */ ipc_port_multiple_unlock(); /* port (== base) is in limbo */ assert(ip_active(port)); assert(port->ip_receiver_name == MACH_PORT_NULL); assert(port->ip_destination == IP_NULL); while (dest != IP_NULL) { ipc_port_t next; /* dest is in transit or in limbo */ assert(ip_active(dest)); assert(dest->ip_receiver_name == MACH_PORT_NULL); next = dest->ip_destination; ip_unlock(dest); dest = next; } return TRUE; } /* * The guarantee: lock port while the entire chain is locked. * Once port is locked, we can take a reference to dest, * add port to the chain, and unlock everything. */ ip_lock(port); ipc_port_multiple_unlock(); not_circular: /* port is in limbo */ assert(ip_active(port)); assert(port->ip_receiver_name == MACH_PORT_NULL); assert(port->ip_destination == IP_NULL); ip_reference(dest); port->ip_destination = dest; /* now unlock chain */ while (port != base) { ipc_port_t next; /* port is in transit */ assert(ip_active(port)); assert(port->ip_receiver_name == MACH_PORT_NULL); assert(port->ip_destination != IP_NULL); next = port->ip_destination; ip_unlock(port); port = next; } /* base is not in transit */ assert(!ip_active(base) || (base->ip_receiver_name != MACH_PORT_NULL) || (base->ip_destination == IP_NULL)); ip_unlock(base); return FALSE; }