#include <zone_debug.h>
#include <mach_assert.h>
#include <mach/port.h>
#include <mach/kern_return.h>
#include <kern/ipc_kobject.h>
#include <kern/thread.h>
#include <kern/misc_protos.h>
#include <kern/wait_queue.h>
#include <ipc/ipc_entry.h>
#include <ipc/ipc_space.h>
#include <ipc/ipc_object.h>
#include <ipc/ipc_port.h>
#include <ipc/ipc_pset.h>
#include <ipc/ipc_kmsg.h>
#include <ipc/ipc_mqueue.h>
#include <ipc/ipc_notify.h>
#include <ipc/ipc_table.h>
#include <ipc/ipc_importance.h>
#include <security/mac_mach_internal.h>
#include <string.h>
decl_lck_spin_data(, ipc_port_multiple_lock_data)
ipc_port_timestamp_t ipc_port_timestamp_data;
int ipc_portbt;
#if MACH_ASSERT
void ipc_port_init_debug(
ipc_port_t port,
uintptr_t *callstack,
unsigned int callstack_max);
void ipc_port_callstack_init_debug(
uintptr_t *callstack,
unsigned int callstack_max);
#endif
void
ipc_port_release(ipc_port_t port)
{
ip_release(port);
}
void
ipc_port_reference(ipc_port_t port)
{
ip_reference(port);
}
ipc_port_timestamp_t
ipc_port_timestamp(void)
{
return OSIncrementAtomic(&ipc_port_timestamp_data);
}
#if IMPORTANCE_INHERITANCE
kern_return_t
ipc_port_request_alloc(
ipc_port_t port,
mach_port_name_t name,
ipc_port_t soright,
boolean_t send_possible,
boolean_t immediate,
ipc_port_request_index_t *indexp,
boolean_t *importantp)
#else
kern_return_t
ipc_port_request_alloc(
ipc_port_t port,
mach_port_name_t name,
ipc_port_t soright,
boolean_t send_possible,
boolean_t immediate,
ipc_port_request_index_t *indexp)
#endif
{
ipc_port_request_t ipr, table;
ipc_port_request_index_t index;
uintptr_t mask = 0;
#if IMPORTANCE_INHERITANCE
*importantp = FALSE;
#endif
assert(ip_active(port));
assert(name != MACH_PORT_NULL);
assert(soright != IP_NULL);
table = port->ip_requests;
if (table == IPR_NULL)
return KERN_NO_SPACE;
index = table->ipr_next;
if (index == 0)
return KERN_NO_SPACE;
ipr = &table[index];
assert(ipr->ipr_name == MACH_PORT_NULL);
table->ipr_next = ipr->ipr_next;
ipr->ipr_name = name;
if (send_possible) {
mask |= IPR_SOR_SPREQ_MASK;
if (immediate) {
mask |= IPR_SOR_SPARM_MASK;
if (port->ip_sprequests == 0) {
port->ip_sprequests = 1;
#if IMPORTANCE_INHERITANCE
if (port->ip_impdonation != 0 &&
port->ip_spimportant == 0 &&
(task_is_importance_donor(current_task()))) {
port->ip_spimportant = 1;
*importantp = TRUE;
}
#endif
}
}
}
ipr->ipr_soright = IPR_SOR_MAKE(soright, mask);
*indexp = index;
return KERN_SUCCESS;
}
kern_return_t
ipc_port_request_grow(
ipc_port_t port,
ipc_table_elems_t target_size)
{
ipc_table_size_t its;
ipc_port_request_t otable, ntable;
assert(ip_active(port));
otable = port->ip_requests;
if (otable == IPR_NULL)
its = &ipc_table_requests[0];
else
its = otable->ipr_size + 1;
if (target_size != ITS_SIZE_NONE) {
if ((otable != IPR_NULL) &&
(target_size <= otable->ipr_size->its_size)) {
ip_unlock(port);
return KERN_SUCCESS;
}
while ((its->its_size) && (its->its_size < target_size)) {
its++;
}
if (its->its_size == 0) {
ip_unlock(port);
return KERN_NO_SPACE;
}
}
ip_reference(port);
ip_unlock(port);
if ((its->its_size == 0) ||
((ntable = it_requests_alloc(its)) == IPR_NULL)) {
ip_release(port);
return KERN_RESOURCE_SHORTAGE;
}
ip_lock(port);
if (ip_active(port) && (port->ip_requests == otable) &&
((otable == IPR_NULL) || (otable->ipr_size+1 == its))) {
ipc_table_size_t oits;
ipc_table_elems_t osize, nsize;
ipc_port_request_index_t free, i;
if (otable != IPR_NULL) {
oits = otable->ipr_size;
osize = oits->its_size;
free = otable->ipr_next;
(void) memcpy((void *)(ntable + 1),
(const void *)(otable + 1),
(osize - 1) * sizeof(struct ipc_port_request));
} else {
osize = 1;
oits = 0;
free = 0;
}
nsize = its->its_size;
assert(nsize > osize);
for (i = osize; i < nsize; i++) {
ipc_port_request_t ipr = &ntable[i];
ipr->ipr_name = MACH_PORT_NULL;
ipr->ipr_next = free;
free = i;
}
ntable->ipr_next = free;
ntable->ipr_size = its;
port->ip_requests = ntable;
ip_unlock(port);
ip_release(port);
if (otable != IPR_NULL) {
it_requests_free(oits, otable);
}
} else {
ip_unlock(port);
ip_release(port);
it_requests_free(its, ntable);
}
return KERN_SUCCESS;
}
#if IMPORTANCE_INHERITANCE
boolean_t
ipc_port_request_sparm(
ipc_port_t port,
__assert_only mach_port_name_t name,
ipc_port_request_index_t index,
mach_msg_option_t option)
#else
boolean_t
ipc_port_request_sparm(
ipc_port_t port,
__assert_only mach_port_name_t name,
ipc_port_request_index_t index)
#endif
{
if (index != IE_REQ_NONE) {
ipc_port_request_t ipr, table;
assert(ip_active(port));
table = port->ip_requests;
assert(table != IPR_NULL);
ipr = &table[index];
assert(ipr->ipr_name == name);
if (IPR_SOR_SPREQ(ipr->ipr_soright)) {
ipr->ipr_soright = IPR_SOR_MAKE(ipr->ipr_soright, IPR_SOR_SPARM_MASK);
port->ip_sprequests = 1;
#if IMPORTANCE_INHERITANCE
if (((option & MACH_SEND_NOIMPORTANCE) == 0) &&
(port->ip_impdonation != 0) &&
(port->ip_spimportant == 0) &&
(((option & MACH_SEND_IMPORTANCE) != 0) ||
(task_is_importance_donor(current_task())))) {
port->ip_spimportant = 1;
return TRUE;
}
#else
return TRUE;
#endif
}
}
return FALSE;
}
mach_port_type_t
ipc_port_request_type(
ipc_port_t port,
__assert_only mach_port_name_t name,
ipc_port_request_index_t index)
{
ipc_port_request_t ipr, table;
mach_port_type_t type = 0;
table = port->ip_requests;
assert (table != IPR_NULL);
assert(index != IE_REQ_NONE);
ipr = &table[index];
assert(ipr->ipr_name == name);
if (IP_VALID(IPR_SOR_PORT(ipr->ipr_soright))) {
type |= MACH_PORT_TYPE_DNREQUEST;
if (IPR_SOR_SPREQ(ipr->ipr_soright)) {
type |= MACH_PORT_TYPE_SPREQUEST;
if (!IPR_SOR_SPARMED(ipr->ipr_soright)) {
type |= MACH_PORT_TYPE_SPREQUEST_DELAYED;
}
}
}
return type;
}
ipc_port_t
ipc_port_request_cancel(
ipc_port_t port,
__assert_only mach_port_name_t name,
ipc_port_request_index_t index)
{
ipc_port_request_t ipr, table;
ipc_port_t request = IP_NULL;
assert(ip_active(port));
table = port->ip_requests;
assert(table != IPR_NULL);
assert (index != IE_REQ_NONE);
ipr = &table[index];
assert(ipr->ipr_name == name);
request = IPR_SOR_PORT(ipr->ipr_soright);
ipr->ipr_name = MACH_PORT_NULL;
ipr->ipr_next = table->ipr_next;
table->ipr_next = index;
return request;
}
void
ipc_port_pdrequest(
ipc_port_t port,
ipc_port_t notify,
ipc_port_t *previousp)
{
ipc_port_t previous;
assert(ip_active(port));
previous = port->ip_pdrequest;
port->ip_pdrequest = notify;
ip_unlock(port);
*previousp = previous;
}
void
ipc_port_nsrequest(
ipc_port_t port,
mach_port_mscount_t sync,
ipc_port_t notify,
ipc_port_t *previousp)
{
ipc_port_t previous;
mach_port_mscount_t mscount;
assert(ip_active(port));
previous = port->ip_nsrequest;
mscount = port->ip_mscount;
if ((port->ip_srights == 0) && (sync <= mscount) &&
(notify != IP_NULL)) {
port->ip_nsrequest = IP_NULL;
ip_unlock(port);
ipc_notify_no_senders(notify, mscount);
} else {
port->ip_nsrequest = notify;
ip_unlock(port);
}
*previousp = previous;
}
void
ipc_port_clear_receiver(
ipc_port_t port,
queue_t links)
{
spl_t s;
assert(ip_active(port));
if (port->ip_pset_count != 0) {
ipc_pset_remove_from_all(port, links);
assert(port->ip_pset_count == 0);
}
s = splsched();
imq_lock(&port->ip_messages);
ipc_mqueue_changed(&port->ip_messages);
ipc_port_set_mscount(port, 0);
port->ip_messages.imq_seqno = 0;
port->ip_context = port->ip_guarded = port->ip_strict_guard = 0;
imq_unlock(&port->ip_messages);
splx(s);
}
void
ipc_port_init(
ipc_port_t port,
ipc_space_t space,
mach_port_name_t name)
{
port->ip_receiver = space;
port->ip_receiver_name = name;
port->ip_mscount = 0;
port->ip_srights = 0;
port->ip_sorights = 0;
port->ip_nsrequest = IP_NULL;
port->ip_pdrequest = IP_NULL;
port->ip_requests = IPR_NULL;
port->ip_pset_count = 0;
port->ip_premsg = IKM_NULL;
port->ip_context = 0;
port->ip_sprequests = 0;
port->ip_spimportant = 0;
port->ip_impdonation = 0;
port->ip_tempowner = 0;
port->ip_guarded = 0;
port->ip_strict_guard = 0;
port->ip_impcount = 0;
port->ip_reserved = 0;
ipc_mqueue_init(&port->ip_messages, FALSE );
}
kern_return_t
ipc_port_alloc(
ipc_space_t space,
mach_port_name_t *namep,
ipc_port_t *portp)
{
ipc_port_t port;
mach_port_name_t name;
kern_return_t kr;
#if MACH_ASSERT
uintptr_t buf[IP_CALLSTACK_MAX];
ipc_port_callstack_init_debug(&buf[0], IP_CALLSTACK_MAX);
#endif
kr = ipc_object_alloc(space, IOT_PORT,
MACH_PORT_TYPE_RECEIVE, 0,
&name, (ipc_object_t *) &port);
if (kr != KERN_SUCCESS)
return kr;
ipc_port_init(port, space, name);
#if MACH_ASSERT
ipc_port_init_debug(port, &buf[0], IP_CALLSTACK_MAX);
#endif
is_write_unlock(space);
*namep = name;
*portp = port;
return KERN_SUCCESS;
}
kern_return_t
ipc_port_alloc_name(
ipc_space_t space,
mach_port_name_t name,
ipc_port_t *portp)
{
ipc_port_t port;
kern_return_t kr;
#if MACH_ASSERT
uintptr_t buf[IP_CALLSTACK_MAX];
ipc_port_callstack_init_debug(&buf[0], IP_CALLSTACK_MAX);
#endif
kr = ipc_object_alloc_name(space, IOT_PORT,
MACH_PORT_TYPE_RECEIVE, 0,
name, (ipc_object_t *) &port);
if (kr != KERN_SUCCESS)
return kr;
ipc_port_init(port, space, name);
#if MACH_ASSERT
ipc_port_init_debug(port, &buf[0], IP_CALLSTACK_MAX);
#endif
*portp = port;
return KERN_SUCCESS;
}
void
ipc_port_spnotify(
ipc_port_t port)
{
ipc_port_request_index_t index = 0;
ipc_table_elems_t size = 0;
#if IMPORTANCE_INHERITANCE
boolean_t dropassert = FALSE;
#endif
if (port->ip_sprequests == 0)
return;
ip_lock(port);
#if IMPORTANCE_INHERITANCE
if (port->ip_spimportant != 0) {
port->ip_spimportant = 0;
if (ipc_port_impcount_delta(port, -1, IP_NULL) == -1) {
dropassert = TRUE;
}
}
#endif
if (port->ip_sprequests == 0) {
ip_unlock(port);
goto out;
}
port->ip_sprequests = 0;
revalidate:
if (ip_active(port)) {
ipc_port_request_t requests;
requests = port->ip_requests;
assert(requests != IPR_NULL);
if (size == 0)
size = requests->ipr_size->its_size;
while (++index < size) {
ipc_port_request_t ipr = &requests[index];
mach_port_name_t name = ipr->ipr_name;
ipc_port_t soright = IPR_SOR_PORT(ipr->ipr_soright);
boolean_t armed = IPR_SOR_SPARMED(ipr->ipr_soright);
if (MACH_PORT_VALID(name) && armed && IP_VALID(soright)) {
ipr->ipr_soright = IP_NULL;
ip_unlock(port);
ipc_notify_send_possible(soright, name);
ip_lock(port);
goto revalidate;
}
}
}
ip_unlock(port);
out:
#if IMPORTANCE_INHERITANCE
if (dropassert == TRUE && ipc_importance_task_is_any_receiver_type(current_task()->task_imp_base)) {
ipc_importance_task_drop_internal_assertion(current_task()->task_imp_base, 1);
}
#endif
return;
}
void
ipc_port_dnnotify(
ipc_port_t port)
{
ipc_port_request_t requests = port->ip_requests;
assert(!ip_active(port));
if (requests != IPR_NULL) {
ipc_table_size_t its = requests->ipr_size;
ipc_table_elems_t size = its->its_size;
ipc_port_request_index_t index;
for (index = 1; index < size; index++) {
ipc_port_request_t ipr = &requests[index];
mach_port_name_t name = ipr->ipr_name;
ipc_port_t soright = IPR_SOR_PORT(ipr->ipr_soright);
if (MACH_PORT_VALID(name) && IP_VALID(soright)) {
ipc_notify_dead_name(soright, name);
}
}
}
}
void
ipc_port_destroy(
ipc_port_t port)
{
ipc_port_t pdrequest, nsrequest;
ipc_mqueue_t mqueue;
ipc_kmsg_t kmsg;
#if IMPORTANCE_INHERITANCE
ipc_importance_task_t release_imp_task = IIT_NULL;
thread_t self = current_thread();
boolean_t top = (self->ith_assertions == 0);
natural_t assertcnt = 0;
#endif
assert(ip_active(port));
assert(port->ip_pset_count == 0);
assert(port->ip_mscount == 0);
pdrequest = port->ip_pdrequest;
#if IMPORTANCE_INHERITANCE
if (port->ip_tempowner != 0) {
assert(top);
release_imp_task = port->ip_imp_task;
if (IIT_NULL != release_imp_task) {
port->ip_imp_task = IIT_NULL;
assertcnt = port->ip_impcount;
}
} else {
assertcnt = port->ip_impcount;
if (pdrequest != IP_NULL)
port->ip_tempowner = 1;
}
if (top)
self->ith_assertions = assertcnt;
#endif
if (pdrequest != IP_NULL) {
port->ip_pdrequest = IP_NULL;
port->ip_receiver_name = MACH_PORT_NULL;
port->ip_destination = IP_NULL;
ip_unlock(port);
ipc_notify_port_destroyed(pdrequest, port);
goto drop_assertions;
}
port->ip_object.io_bits &= ~IO_BITS_ACTIVE;
port->ip_timestamp = ipc_port_timestamp();
nsrequest = port->ip_nsrequest;
if (IP_PREALLOC(port)) {
ipc_port_t inuse_port;
kmsg = port->ip_premsg;
assert(kmsg != IKM_NULL);
inuse_port = ikm_prealloc_inuse_port(kmsg);
IP_CLEAR_PREALLOC(port, kmsg);
ip_unlock(port);
if (inuse_port != IP_NULL) {
assert(inuse_port == port);
} else {
ipc_kmsg_free(kmsg);
}
} else {
ip_unlock(port);
}
if (nsrequest != IP_NULL)
ipc_notify_send_once(nsrequest);
mqueue = &port->ip_messages;
ipc_mqueue_destroy(mqueue);
ipc_port_dnnotify(port);
ipc_kobject_destroy(port);
ip_release(port);
drop_assertions:
#if IMPORTANCE_INHERITANCE
if (release_imp_task != IIT_NULL) {
if (assertcnt > 0) {
assert(top);
self->ith_assertions = 0;
assert(ipc_importance_task_is_any_receiver_type(release_imp_task));
ipc_importance_task_drop_internal_assertion(release_imp_task, assertcnt);
}
ipc_importance_task_release(release_imp_task);
} else if (assertcnt > 0) {
if (top) {
self->ith_assertions = 0;
release_imp_task = current_task()->task_imp_base;
if (ipc_importance_task_is_any_receiver_type(release_imp_task)) {
ipc_importance_task_drop_internal_assertion(release_imp_task, assertcnt);
}
}
}
#endif
}
boolean_t
ipc_port_check_circularity(
ipc_port_t port,
ipc_port_t dest)
{
ipc_port_t base;
#if IMPORTANCE_INHERITANCE
ipc_importance_task_t imp_task = IIT_NULL;
ipc_importance_task_t release_imp_task = IIT_NULL;
int assertcnt = 0;
#endif
assert(port != IP_NULL);
assert(dest != IP_NULL);
if (port == dest)
return TRUE;
base = dest;
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;
ip_unlock(dest);
}
ip_unlock(port);
ipc_port_multiple_lock();
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;
}
if (port == base) {
ipc_port_multiple_unlock();
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;
assert(ip_active(dest));
assert(dest->ip_receiver_name == MACH_PORT_NULL);
next = dest->ip_destination;
ip_unlock(dest);
dest = next;
}
return TRUE;
}
ip_lock(port);
ipc_port_multiple_unlock();
not_circular:
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
assert(port->ip_tempowner != 0);
release_imp_task = port->ip_imp_task;
if (IIT_NULL != release_imp_task) {
port->ip_imp_task = IIT_NULL;
}
assertcnt = port->ip_impcount;
port->ip_tempowner = 0;
#endif
ip_unlock(port);
for (;;) {
#if IMPORTANCE_INHERITANCE
dest->ip_impcount += assertcnt;
#endif
if (dest == base)
break;
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
port = dest->ip_destination;
ip_unlock(dest);
dest = port;
}
assert(!ip_active(base) ||
(base->ip_receiver_name != MACH_PORT_NULL) ||
(base->ip_destination == IP_NULL));
#if IMPORTANCE_INHERITANCE
if (ip_active(base) && (assertcnt > 0)) {
if (base->ip_tempowner != 0) {
if (IIT_NULL != base->ip_imp_task) {
imp_task = base->ip_imp_task;
assert(ipc_importance_task_is_any_receiver_type(imp_task));
}
} else if (base->ip_receiver_name != MACH_PORT_NULL) {
ipc_space_t space = base->ip_receiver;
if (space->is_task != TASK_NULL &&
ipc_importance_task_is_any_receiver_type(space->is_task->task_imp_base))
imp_task = space->is_task->task_imp_base;
}
if (imp_task != IIT_NULL) {
ipc_importance_task_reference(imp_task);
}
}
#endif
ip_unlock(base);
#if IMPORTANCE_INHERITANCE
boolean_t transfer_assertions = (imp_task != release_imp_task) ? TRUE : FALSE;
if (imp_task != IIT_NULL) {
if (transfer_assertions)
ipc_importance_task_hold_internal_assertion(imp_task, assertcnt);
ipc_importance_task_release(imp_task);
imp_task = IIT_NULL;
}
if (release_imp_task != IIT_NULL) {
if (transfer_assertions)
ipc_importance_task_drop_internal_assertion(release_imp_task, assertcnt);
ipc_importance_task_release(release_imp_task);
release_imp_task = IIT_NULL;
}
#endif
return FALSE;
}
mach_port_delta_t
ipc_port_impcount_delta(
ipc_port_t port,
mach_port_delta_t delta,
ipc_port_t __unused base)
{
mach_port_delta_t absdelta;
if (!ip_active(port)) {
return 0;
}
if (delta >= 0) {
port->ip_impcount += delta;
return delta;
}
absdelta = 0 - delta;
if (port->ip_impcount >= absdelta) {
port->ip_impcount -= absdelta;
return delta;
}
#if DEVELOPMENT || DEBUG
if (port->ip_receiver_name != MACH_PORT_NULL) {
task_t target_task = port->ip_receiver->is_task;
ipc_importance_task_t target_imp = target_task->task_imp_base;
const char *target_procname;
int target_pid;
if (target_imp != IIT_NULL) {
target_procname = target_imp->iit_procname;
target_pid = target_imp->iit_bsd_pid;
} else {
target_procname = "unknown";
target_pid = -1;
}
printf("Over-release of importance assertions for port 0x%x receiver pid %d (%s), "
"dropping %d assertion(s) but port only has %d remaining.\n",
port->ip_receiver_name,
target_imp->iit_bsd_pid, target_imp->iit_procname,
absdelta, port->ip_impcount);
} else if (base != IP_NULL) {
task_t target_task = base->ip_receiver->is_task;
ipc_importance_task_t target_imp = target_task->task_imp_base;
const char *target_procname;
int target_pid;
if (target_imp != IIT_NULL) {
target_procname = target_imp->iit_procname;
target_pid = target_imp->iit_bsd_pid;
} else {
target_procname = "unknown";
target_pid = -1;
}
printf("Over-release of importance assertions for port %p "
"enqueued on port 0x%x with receiver pid %d (%s), "
"dropping %d assertion(s) but port only has %d remaining.\n",
port, base->ip_receiver_name,
target_imp->iit_bsd_pid, target_imp->iit_procname,
absdelta, port->ip_impcount);
}
#endif
delta = 0 - port->ip_impcount;
port->ip_impcount = 0;
return delta;
}
#if IMPORTANCE_INHERITANCE
boolean_t
ipc_port_importance_delta_internal(
ipc_port_t port,
mach_port_delta_t *deltap,
ipc_importance_task_t *imp_task)
{
ipc_port_t next, base;
boolean_t dropped = FALSE;
*imp_task = IIT_NULL;
if (*deltap == 0)
return FALSE;
base = port;
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();
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();
}
for (;;) {
*deltap = ipc_port_impcount_delta(port, *deltap, base);
if (port == base) {
break;
}
assert(port->ip_tempowner == 0);
next = port->ip_destination;
ip_unlock(port);
port = next;
}
if (ip_active(base)) {
if (base->ip_tempowner != 0) {
if (IIT_NULL != base->ip_imp_task)
*imp_task = base->ip_imp_task;
} else if (base->ip_receiver_name != MACH_PORT_NULL) {
ipc_space_t space = base->ip_receiver;
if (space->is_task != TASK_NULL &&
ipc_importance_task_is_any_receiver_type(space->is_task->task_imp_base)) {
*imp_task = space->is_task->task_imp_base;
}
}
}
if (*imp_task != IIT_NULL) {
ipc_importance_task_reference(*imp_task);
}
if (dropped == TRUE) {
ip_unlock(base);
}
return dropped;
}
#endif
#if IMPORTANCE_INHERITANCE
boolean_t
ipc_port_importance_delta(
ipc_port_t port,
mach_port_delta_t delta)
{
ipc_importance_task_t imp_task = IIT_NULL;
boolean_t dropped;
dropped = ipc_port_importance_delta_internal(port, &delta, &imp_task);
if (IIT_NULL == imp_task)
return dropped;
if (!dropped) {
dropped = TRUE;
ip_unlock(port);
}
assert(ipc_importance_task_is_any_receiver_type(imp_task));
if (delta > 0)
ipc_importance_task_hold_internal_assertion(imp_task, delta);
else
ipc_importance_task_drop_internal_assertion(imp_task, -delta);
ipc_importance_task_release(imp_task);
return dropped;
}
#endif
ipc_port_t
ipc_port_lookup_notify(
ipc_space_t space,
mach_port_name_t name)
{
ipc_port_t port;
ipc_entry_t entry;
assert(is_active(space));
entry = ipc_entry_lookup(space, name);
if (entry == IE_NULL)
return IP_NULL;
if ((entry->ie_bits & MACH_PORT_TYPE_RECEIVE) == 0)
return IP_NULL;
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);
ip_reference(port);
port->ip_sorights++;
ip_unlock(port);
return port;
}
ipc_port_t
ipc_port_make_send_locked(
ipc_port_t port)
{
assert(ip_active(port));
port->ip_mscount++;
port->ip_srights++;
ip_reference(port);
return port;
}
ipc_port_t
ipc_port_make_send(
ipc_port_t port)
{
if (!IP_VALID(port))
return port;
ip_lock(port);
if (ip_active(port)) {
port->ip_mscount++;
port->ip_srights++;
ip_reference(port);
ip_unlock(port);
return port;
}
ip_unlock(port);
return IP_DEAD;
}
ipc_port_t
ipc_port_copy_send(
ipc_port_t port)
{
ipc_port_t sright;
if (!IP_VALID(port))
return port;
ip_lock(port);
if (ip_active(port)) {
assert(port->ip_srights > 0);
ip_reference(port);
port->ip_srights++;
sright = port;
} else
sright = IP_DEAD;
ip_unlock(port);
return sright;
}
mach_port_name_t
ipc_port_copyout_send(
ipc_port_t sright,
ipc_space_t space)
{
mach_port_name_t name;
if (IP_VALID(sright)) {
kern_return_t kr;
kr = ipc_object_copyout(space, (ipc_object_t) sright,
MACH_MSG_TYPE_PORT_SEND, TRUE, &name);
if (kr != KERN_SUCCESS) {
ipc_port_release_send(sright);
if (kr == KERN_INVALID_CAPABILITY)
name = MACH_PORT_DEAD;
else
name = MACH_PORT_NULL;
}
} else
name = CAST_MACH_PORT_TO_NAME(sright);
return name;
}
void
ipc_port_release_send(
ipc_port_t port)
{
ipc_port_t nsrequest = IP_NULL;
mach_port_mscount_t mscount;
if (!IP_VALID(port))
return;
ip_lock(port);
assert(port->ip_srights > 0);
port->ip_srights--;
if (!ip_active(port)) {
ip_unlock(port);
ip_release(port);
return;
}
if (port->ip_srights == 0 &&
port->ip_nsrequest != IP_NULL) {
nsrequest = port->ip_nsrequest;
port->ip_nsrequest = IP_NULL;
mscount = port->ip_mscount;
ip_unlock(port);
ip_release(port);
ipc_notify_no_senders(nsrequest, mscount);
} else {
ip_unlock(port);
ip_release(port);
}
}
ipc_port_t
ipc_port_make_sonce_locked(
ipc_port_t port)
{
assert(ip_active(port));
port->ip_sorights++;
ip_reference(port);
return port;
}
ipc_port_t
ipc_port_make_sonce(
ipc_port_t port)
{
if (!IP_VALID(port))
return port;
ip_lock(port);
if (ip_active(port)) {
port->ip_sorights++;
ip_reference(port);
ip_unlock(port);
return port;
}
ip_unlock(port);
return IP_DEAD;
}
void
ipc_port_release_sonce(
ipc_port_t port)
{
if (!IP_VALID(port))
return;
ip_lock(port);
assert(port->ip_sorights > 0);
port->ip_sorights--;
ip_unlock(port);
ip_release(port);
}
void
ipc_port_release_receive(
ipc_port_t port)
{
ipc_port_t dest;
if (!IP_VALID(port))
return;
ip_lock(port);
assert(ip_active(port));
assert(port->ip_receiver_name == MACH_PORT_NULL);
dest = port->ip_destination;
ipc_port_destroy(port);
if (dest != IP_NULL)
ip_release(dest);
}
ipc_port_t
ipc_port_alloc_special(
ipc_space_t space)
{
ipc_port_t port;
port = (ipc_port_t) io_alloc(IOT_PORT);
if (port == IP_NULL)
return IP_NULL;
#if MACH_ASSERT
uintptr_t buf[IP_CALLSTACK_MAX];
ipc_port_callstack_init_debug(&buf[0], IP_CALLSTACK_MAX);
#endif
bzero((char *)port, sizeof(*port));
io_lock_init(&port->ip_object);
port->ip_references = 1;
port->ip_object.io_bits = io_makebits(TRUE, IOT_PORT, 0);
ipc_port_init(port, space, 1);
#if MACH_ASSERT
ipc_port_init_debug(port, &buf[0], IP_CALLSTACK_MAX);
#endif
return port;
}
void
ipc_port_dealloc_special(
ipc_port_t port,
__assert_only ipc_space_t space)
{
ip_lock(port);
assert(ip_active(port));
assert(port->ip_receiver == space);
port->ip_receiver_name = MACH_PORT_NULL;
port->ip_receiver = IS_NULL;
ipc_port_set_mscount(port, 0);
port->ip_messages.imq_seqno = 0;
ipc_port_destroy(port);
}
void
ipc_port_finalize(
ipc_port_t port)
{
ipc_port_request_t requests = port->ip_requests;
assert(!ip_active(port));
if (requests != IPR_NULL) {
ipc_table_size_t its = requests->ipr_size;
it_requests_free(its, requests);
port->ip_requests = IPR_NULL;
}
#if MACH_ASSERT
ipc_port_track_dealloc(port);
#endif
}
#if MACH_ASSERT
#include <kern/machine.h>
queue_head_t port_alloc_queue;
lck_spin_t port_alloc_queue_lock;
unsigned long port_count = 0;
unsigned long port_count_warning = 20000;
unsigned long port_timestamp = 0;
void db_port_stack_trace(
ipc_port_t port);
void db_ref(
int refs);
int db_port_walk(
unsigned int verbose,
unsigned int display,
unsigned int ref_search,
unsigned int ref_target);
void
ipc_port_debug_init(void)
{
queue_init(&port_alloc_queue);
lck_spin_init(&port_alloc_queue_lock, &ipc_lck_grp, &ipc_lck_attr);
if (!PE_parse_boot_argn("ipc_portbt", &ipc_portbt, sizeof (ipc_portbt)))
ipc_portbt = 0;
}
#ifdef MACH_BSD
extern int proc_pid(struct proc*);
#endif
void
ipc_port_init_debug(
ipc_port_t port,
uintptr_t *callstack,
unsigned int callstack_max)
{
unsigned int i;
port->ip_thread = current_thread();
port->ip_timetrack = port_timestamp++;
for (i = 0; i < callstack_max; ++i)
port->ip_callstack[i] = callstack[i];
for (i = 0; i < IP_NSPARES; ++i)
port->ip_spares[i] = 0;
#ifdef MACH_BSD
task_t task = current_task();
if (task != TASK_NULL) {
struct proc* proc = (struct proc*) get_bsdtask_info(task);
if (proc)
port->ip_spares[0] = proc_pid(proc);
}
#endif
#if 0
lck_spin_lock(&port_alloc_queue_lock);
++port_count;
if (port_count_warning > 0 && port_count >= port_count_warning)
assert(port_count < port_count_warning);
queue_enter(&port_alloc_queue, port, ipc_port_t, ip_port_links);
lck_spin_unlock(&port_alloc_queue_lock);
#endif
}
void
ipc_port_callstack_init_debug(
uintptr_t *callstack,
unsigned int callstack_max)
{
unsigned int i;
for (i=0; i < callstack_max; i++)
callstack[i] = 0;
if (ipc_portbt)
machine_callstack(callstack, callstack_max);
}
#if 1
void
ipc_port_track_dealloc(
__unused ipc_port_t port)
{
}
#else
void
ipc_port_track_dealloc(
ipc_port_t port)
{
lck_spin_lock(&port_alloc_queue_lock);
assert(port_count > 0);
--port_count;
queue_remove(&port_alloc_queue, port, ipc_port_t, ip_port_links);
lck_spin_unlock(&port_alloc_queue_lock);
}
#endif
#endif