+/*
+ * Routine: mach_port_guard_locked [helper routine]
+ * Purpose:
+ * Sets a new guard for a locked port.
+ * Conditions:
+ * Port Locked.
+ * Returns:
+ * KERN_SUCCESS Port Guarded.
+ * KERN_INVALID_ARGUMENT Port already contains a context/guard.
+ */
+static kern_return_t
+mach_port_guard_locked(
+ ipc_port_t port,
+ uint64_t guard,
+ boolean_t strict)
+{
+ if (port->ip_context)
+ return KERN_INVALID_ARGUMENT;
+
+ port->ip_context = guard;
+ port->ip_guarded = 1;
+ port->ip_strict_guard = (strict)?1:0;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: mach_port_unguard_locked [helper routine]
+ * Purpose:
+ * Removes guard for a locked port.
+ * Conditions:
+ * Port Locked.
+ * Returns:
+ * KERN_SUCCESS Port Unguarded.
+ * KERN_INVALID_ARGUMENT Port is either unguarded already or guard mismatch.
+ * This also raises a EXC_GUARD exception.
+ */
+static kern_return_t
+mach_port_unguard_locked(
+ ipc_port_t port,
+ mach_port_name_t name,
+ uint64_t guard)
+{
+ /* Port locked and active */
+ if (!port->ip_guarded) {
+ /* Port already unguarded; Raise exception */
+ mach_port_guard_exception(name, guard, 0, kGUARD_EXC_UNGUARDED);
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ if (port->ip_context != guard) {
+ /* Incorrect guard; Raise exception */
+ mach_port_guard_exception(name, guard, port->ip_context, kGUARD_EXC_INCORRECT_GUARD);
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ port->ip_context = 0;
+ port->ip_guarded = port->ip_strict_guard = 0;
+ return KERN_SUCCESS;
+}
+
+
+/*
+ * Routine: mach_port_guard_exception [helper routine]
+ * Purpose:
+ * Marks the thread with AST_GUARD for mach port guard violation.
+ * Also saves exception info in thread structure.
+ * Conditions:
+ * None.
+ * Returns:
+ * KERN_FAILURE Thread marked with AST_GUARD.
+ */
+kern_return_t
+mach_port_guard_exception(
+ mach_port_name_t name,
+ uint64_t inguard,
+ uint64_t portguard,
+ unsigned reason)
+{
+ thread_t t = current_thread();
+ uint64_t code, subcode;
+
+ /* Log exception info to syslog */
+ printf( "Mach Port Guard Exception - "
+ "Thread: 0x%x, "
+ "Port Name: 0x%x, "
+ "Expected Guard: 0x%x, "
+ "Received Guard: 0x%x\n",
+ (unsigned)t,
+ (unsigned)name,
+ (unsigned)portguard,
+ (unsigned)inguard);
+
+ /*
+ * EXC_GUARD namespace for mach ports
+ *
+ *
+ * Mach Port guards use the exception codes like
+ *
+ * code:
+ * +----------------------------------------------------------------+
+ * |[63:61] GUARD_TYPE_MACH_PORT | [60:32] flavor | [31:0] port name|
+ * +----------------------------------------------------------------+
+ *
+ * subcode:
+ * +----------------------------------------------------------------+
+ * | [63:0] guard value |
+ * +----------------------------------------------------------------+
+ */
+
+ code = (((uint64_t)GUARD_TYPE_MACH_PORT) << 61) |
+ (((uint64_t)reason) << 32) |
+ ((uint64_t)name);
+ subcode = (uint64_t)(portguard);
+
+ t->guard_exc_info.code = code;
+ t->guard_exc_info.subcode = subcode;
+
+ /* Mark thread with AST_GUARD */
+ thread_guard_violation(t, GUARD_TYPE_MACH_PORT);
+ return KERN_FAILURE;
+}
+
+
+/*
+ * Routine: mach_port_guard_ast
+ * Purpose:
+ * Raises an exception for mach port guard violation.
+ * Conditions:
+ * None.
+ * Returns:
+ * None.
+ */
+
+void
+mach_port_guard_ast(thread_t t)
+{
+ mach_exception_data_type_t code[EXCEPTION_CODE_MAX];
+
+ code[0] = t->guard_exc_info.code;
+ code[1] = t->guard_exc_info.subcode;
+
+ /* Raise an EXC_GUARD exception */
+ exception_triage(EXC_GUARD, code, EXCEPTION_CODE_MAX);
+
+ /* Terminate task which caused the exception */
+ (void) task_terminate_internal(current_task());
+ return;
+}
+
+/*
+ * Routine: mach_port_construct [kernel call]
+ * Purpose:
+ * Constructs a mach port with the provided set of options.
+ * Conditions:
+ * None.
+ * Returns:
+ * KERN_SUCCESS The right is allocated.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ * KERN_NO_SPACE No room in space for another right.
+ * KERN_FAILURE Illegal option values requested.
+ */
+
+kern_return_t
+mach_port_construct(
+ ipc_space_t space,
+ mach_port_options_t *options,
+ uint64_t context,
+ mach_port_name_t *name)
+{
+ kern_return_t kr;
+ ipc_port_t port;
+
+ if (space == IS_NULL)
+ return (KERN_INVALID_TASK);
+
+ /* Allocate a new port in the IPC space */
+ kr = ipc_port_alloc(space, name, &port);
+ if (kr != KERN_SUCCESS)
+ return kr;
+
+ /* Port locked and active */
+ if (options->flags & MPO_CONTEXT_AS_GUARD) {
+ kr = mach_port_guard_locked(port, (uint64_t) context, (options->flags & MPO_STRICT));
+ /* A newly allocated and locked port should always be guarded successfully */
+ assert(kr == KERN_SUCCESS);
+ } else {
+ port->ip_context = context;
+ }
+
+ /* Unlock port */
+ ip_unlock(port);
+
+ /* Set port attributes as requested */
+
+ if (options->flags & MPO_QLIMIT) {
+ kr = mach_port_set_attributes(space, *name, MACH_PORT_LIMITS_INFO,
+ (mach_port_info_t)&options->mpl, sizeof(options->mpl)/sizeof(int));
+ if (kr != KERN_SUCCESS)
+ goto cleanup;
+ }
+
+ if (options->flags & MPO_TEMPOWNER) {
+ kr = mach_port_set_attributes(space, *name, MACH_PORT_TEMPOWNER, NULL, 0);
+ if (kr != KERN_SUCCESS)
+ goto cleanup;
+ }
+
+ if (options->flags & MPO_IMPORTANCE_RECEIVER) {
+ kr = mach_port_set_attributes(space, *name, MACH_PORT_IMPORTANCE_RECEIVER, NULL, 0);
+ if (kr != KERN_SUCCESS)
+ goto cleanup;
+ }
+
+ if (options->flags & MPO_DENAP_RECEIVER) {
+ kr = mach_port_set_attributes(space, *name, MACH_PORT_DENAP_RECEIVER, NULL, 0);
+ if (kr != KERN_SUCCESS)
+ goto cleanup;
+ }
+
+ if (options->flags & MPO_INSERT_SEND_RIGHT) {
+ kr = ipc_object_copyin(space, *name, MACH_MSG_TYPE_MAKE_SEND, (ipc_object_t *)&port);
+ if (kr != KERN_SUCCESS)
+ goto cleanup;
+
+ kr = mach_port_insert_right(space, *name, port, MACH_MSG_TYPE_PORT_SEND);
+ if (kr != KERN_SUCCESS)
+ goto cleanup;
+ }
+
+ return KERN_SUCCESS;
+
+cleanup:
+ /* Attempt to destroy port. If its already destroyed by some other thread, we're done */
+ (void) mach_port_destruct(space, *name, 0, context);
+ return kr;
+}
+
+/*
+ * Routine: mach_port_destruct [kernel call]
+ * Purpose:
+ * Destroys a mach port with appropriate guard
+ * Conditions:
+ * None.
+ * Returns:
+ * KERN_SUCCESS The name is destroyed.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME The name doesn't denote a right.
+ * KERN_INVALID_RIGHT The right isn't correct.
+ * KERN_INVALID_VALUE The delta for send right is incorrect.
+ * KERN_INVALID_ARGUMENT Port is either unguarded already or guard mismatch.
+ * This also raises a EXC_GUARD exception.
+ */
+
+kern_return_t
+mach_port_destruct(
+ ipc_space_t space,
+ mach_port_name_t name,
+ mach_port_delta_t srdelta,
+ uint64_t guard)
+{
+ kern_return_t kr;
+ ipc_entry_t entry;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ if (!MACH_PORT_VALID(name))
+ return KERN_INVALID_NAME;
+
+ /* Remove reference for receive right */
+ kr = ipc_right_lookup_write(space, name, &entry);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* space is write-locked and active */
+ kr = ipc_right_destruct(space, name, entry, srdelta, guard); /* unlocks */
+
+ return kr;
+}
+
+/*
+ * Routine: mach_port_guard [kernel call]
+ * Purpose:
+ * Guard a mach port with specified guard value.
+ * The context field of the port is used as the guard.
+ * Conditions:
+ * None.
+ * Returns:
+ * KERN_SUCCESS The name is destroyed.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME The name doesn't denote a right.
+ * KERN_INVALID_RIGHT The right isn't correct.
+ * KERN_INVALID_ARGUMENT Port already contains a context/guard.
+ */
+kern_return_t
+mach_port_guard(
+ ipc_space_t space,
+ mach_port_name_t name,
+ uint64_t guard,
+ boolean_t strict)
+{
+ kern_return_t kr;
+ ipc_port_t port;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ if (!MACH_PORT_VALID(name))
+ return KERN_INVALID_NAME;
+
+ /* Guard can be applied only to receive rights */
+ kr = ipc_port_translate_receive(space, name, &port);
+ if (kr != KERN_SUCCESS)
+ return kr;
+
+ /* Port locked and active */
+ kr = mach_port_guard_locked(port, guard, strict);
+ ip_unlock(port);
+
+ return kr;
+
+}
+
+/*
+ * Routine: mach_port_unguard [kernel call]
+ * Purpose:
+ * Unguard a mach port with specified guard value.
+ * Conditions:
+ * None.
+ * Returns:
+ * KERN_SUCCESS The name is destroyed.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME The name doesn't denote a right.
+ * KERN_INVALID_RIGHT The right isn't correct.
+ * KERN_INVALID_ARGUMENT Port is either unguarded already or guard mismatch.
+ * This also raises a EXC_GUARD exception.
+ */
+kern_return_t
+mach_port_unguard(
+ ipc_space_t space,
+ mach_port_name_t name,
+ uint64_t guard)
+{
+
+ kern_return_t kr;
+ ipc_port_t port;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ if (!MACH_PORT_VALID(name))
+ return KERN_INVALID_NAME;
+
+ kr = ipc_port_translate_receive(space, name, &port);
+ if (kr != KERN_SUCCESS)
+ return kr;
+
+ /* Port locked and active */
+ kr = mach_port_unguard_locked(port, name, guard);
+ ip_unlock(port);
+ return kr;
+}
+