Beispiel #1
0
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;
}
Beispiel #2
0
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;
}
Beispiel #3
0
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;
}