This is xnu-12377.1.9. See this file in:
/*
 * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 *
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */
/*
 * @OSF_COPYRIGHT@
 */
/*
 * Mach Operating System
 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
 * All Rights Reserved.
 *
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 *
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * Carnegie Mellon requests users of this software to return to
 *
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 */
/*
 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
 * support for mandatory and extensible security protections.  This notice
 * is included in support of clause 2.2 (b) of the Apple Public License,
 * Version 2.0.
 */
/*
 */
/*
 *	File:	kern/ipc_kobject.h
 *	Author:	Rich Draves
 *	Date:	1989
 *
 *	Declarations for letting a port represent a kernel object.
 */

#ifndef _KERN_IPC_KOBJECT_H_
#define _KERN_IPC_KOBJECT_H_

#ifdef MACH_KERNEL_PRIVATE
#include <ipc/ipc_kmsg.h>
#include <ipc/ipc_port.h>
#include <kern/startup.h>
#endif /* MACH_KERNEL_PRIVATE */
#include <mach/machine/vm_types.h>
#include <mach/mach_types.h>
#include <ipc/ipc_types.h>

__BEGIN_DECLS
#pragma GCC visibility push(hidden)

typedef ipc_object_type_t ipc_kobject_type_t;

/* set the bitstring index for kobject */
extern kern_return_t ipc_kobject_set_kobjidx(
	int                         msgid,
	int                         index);

#ifdef MACH_KERNEL_PRIVATE

/*!
 * @typedef ipc_kobject_ops_t
 *
 * @brief
 * Describes the operations for a given kobject.
 *
 * @field iko_ko_type
 * An @c IOT_* value.
 *
 * @field iko_op_stable
 * The kobject/port association is stable:
 * - ipc_kobject_dealloc_port() cannot be called
 *   while there are outstanding send rights,
 * - ipc_kobject_enable() is never called.
 * - ipc_kobject_disable() is never called.
 *
 * @field iko_op_permanent
 * The port is never destroyed.
 * This doesn't necessarily imply iko_op_stable.
 *
 * @field iko_op_no_senders
 * A callback to run when a NO_SENDERS notification fires.
 *
 * This callback is called each time a kobject port reaches 0 send rights
 * (from a non 0 value). There is no need to actively arm no-senders.
 *
 * Kobjects that destroy their port on no senders only are guaranteed
 * to be called with an active port only.
 *
 * However kobject ports that can be destroyed concurrently need
 * to be prepared for no senders to fail to acquire the kobject port.
 *
 * When this callback is set, @c ipc_kobject_dealloc_port()
 * will not implicitly call @c ipc_kobject_disable().
 *
 * The callback runs after the port has been marked inactive,
 * hence @c ipc_kobject_get_raw() needs to be used to get to the port.
 *
 * @field iko_op_label_free
 * How to free the label on this kobject port (if it supports one).
 *
 * @field iko_op_movable_send
 * Whether send rights created to this kobject are movable
 */
typedef const struct ipc_kobject_ops {
	ipc_kobject_type_t iko_op_type;
	unsigned long
	    iko_op_stable               : 1,
	    iko_op_permanent            : 1,
	    iko_op_movable_send         : 1;
	const char        *iko_op_name;
	void (*iko_op_no_senders)(ipc_port_t port, mach_port_mscount_t mscount);
	void (*iko_op_label_free)(ipc_object_label_t label);
} *ipc_kobject_ops_t;

#define IPC_KOBJECT_DEFINE(type, ...) \
	__startup_data \
	static struct ipc_kobject_ops ipc_kobject_ops_##type = { \
	    .iko_op_type = type, \
	    .iko_op_name = #type, \
	    __VA_ARGS__ \
	}; \
	STARTUP_ARG(MACH_IPC, STARTUP_RANK_FIRST, ipc_kobject_register_startup, \
	    &ipc_kobject_ops_##type)

struct ipc_kobject_label {
	ipc_label_t   ikol_label;       /* [private] mandatory access label */
	ipc_port_t XNU_PTRAUTH_SIGNED_PTR("ipc_kobject_label.ikol_alt_port") ikol_alt_port;
};

extern ipc_object_label_t ipc_kobject_label_alloc(
	ipc_object_type_t       otype,
	ipc_label_t             label_tag,
	ipc_port_t              alt_port);

extern void ipc_kobject_label_free(
	ipc_object_label_t      label);

__options_decl(ipc_kobject_alloc_options_t, uint32_t, {
	/* Just make the naked port */
	IPC_KOBJECT_ALLOC_NONE      = 0x00000000,
	/* Make a send right */
	IPC_KOBJECT_ALLOC_MAKE_SEND = 0x00000001,
});

/* Allocates a kobject port, never fails */
extern ipc_port_t ipc_kobject_alloc_port(
	ipc_kobject_t               kobject,
	ipc_object_label_t          label,
	ipc_kobject_alloc_options_t options);

__attribute__((always_inline, overloadable))
static inline ipc_port_t
ipc_kobject_alloc_port(
	ipc_kobject_t               kobject,
	ipc_object_type_t           otype,
	ipc_kobject_alloc_options_t options)
{
	return ipc_kobject_alloc_port(kobject, IPC_OBJECT_LABEL(otype), options);
}

/*!
 * @function ipc_kobject_make_send_lazy_alloc_port()
 *
 * @brief
 * Make a send once for a kobject port, lazily allocating the port.
 *
 * @discussion
 * A location owning this port is passed in port_store.
 * If no port exists, a port is made lazily.
 *
 * A send right is made for the port, and if this is the first one
 * (possibly not for the first time), then the no-more-senders
 * notification is rearmed.
 *
 * When a notification is armed, the kobject must donate
 * one of its references to the port. It is expected
 * the no-more-senders notification will consume this reference.
 *
 * In order to use this function, the kobject type requested must:
 * - be use stable objects (iko_op_stable is true),
 * - have a no-senders callback (iko_op_no_senders is set).
 *
 * @returns
 * - true, if this was the first send right made for this port,
 *   and an object reference must be donated to the port;
 * - false otherwise.
 */
extern bool ipc_kobject_make_send_lazy_alloc_port(
	ipc_port_t                 *port_store,
	ipc_kobject_t               kobject,
	ipc_kobject_type_t          type);

/*!
 * @function ipc_kobject_is_mscount_current()
 *
 * @brief
 * Returns whether the current make-send count is the current one.
 *
 * @discussion
 * This is meant to be called from the context of a no-senders notification
 * callout to determine whether the object/port has since been rematerialized.
 *
 * Most kobjects are uniquely owned by their port, and the object is otherwise
 * not reachable from any place in the system (see semaphores, eventlink, etc),
 * and die when the port has no more senders.
 *
 * However some kobjects might still be reachable from other means,
 * and can make new send rights in a way that isn't synchronized with Mach IPC.
 * (See IKOT_TASK_RESUME for an example of that).
 *
 * This function allows for such kobject types to verify under the
 * synchronization it uses whether this no-senders callout is the last one,
 * or if there has been new send rights made concurrently.
 *
 * @param port          The target port.
 * @param mscount       The make-send count for which the no-senders
 *                      notification was issued.
 */
extern bool ipc_kobject_is_mscount_current(
	ipc_port_t                  port,
	mach_port_mscount_t         mscount);

extern bool ipc_kobject_is_mscount_current_locked(
	ipc_port_t                  port,
	mach_port_mscount_t         mscount);

/*!
 * @function ipc_kobject_copy_send()
 *
 * @brief
 * Copies a naked send right for the specified kobject port.
 *
 * @decription
 * This function will validate that the specified port is pointing
 * to the expected kobject pointer and type (by calling ipc_kobject_require()).
 *
 * @param port          The target port.
 * @param kobject       The kobject pointer this port should be associated to.
 * @param kotype        The kobject type this port should have.
 *
 * @returns
 * - IP_DEAD            if @c port was dead.
 * - @c port            if @c port was valid, in which case
 *                      a naked send right was made.
 */
extern ipc_port_t ipc_kobject_copy_send(
	ipc_port_t                  port,
	ipc_kobject_t               kobject,
	ipc_kobject_type_t          kotype) __result_use_check;

/*!
 * @function ipc_kobject_make_send()
 *
 * @brief
 * Makes a naked send right for the specified kobject port.
 *
 * @decription
 * @see ipc_port_make_send_any_locked() for a general warning about
 * making send rights.
 *
 * This function will validate that the specified port is pointing
 * to the expected kobject pointer and type (by calling ipc_kobject_require()).
 *
 * @param port          The target port.
 * @param kobject       The kobject pointer this port should be associated to.
 * @param kotype        The kobject type this port should have.
 *
 * @returns
 * - IP_DEAD            if @c port was dead.
 * - @c port            if @c port was valid, in which case
 *                      a naked send right was made.
 */
extern ipc_port_t ipc_kobject_make_send(
	ipc_port_t                  port,
	ipc_kobject_t               kobject,
	ipc_kobject_type_t          kotype) __result_use_check;

#define IPC_KOBJECT_NO_MSCOUNT      (~0ull)

extern ipc_kobject_t ipc_kobject_dealloc_port_and_unlock(
	ipc_port_t                  port,
	uint64_t                    mscount,
	ipc_kobject_type_t          type);

extern ipc_kobject_t ipc_kobject_dealloc_port(
	ipc_port_t                  port,
	uint64_t                    mscount,
	ipc_kobject_type_t          type);

extern void         ipc_kobject_enable(
	ipc_port_t                  port,
	ipc_kobject_t               kobject,
	ipc_kobject_type_t          type);

/*!
 * @function ipc_kobject_require()
 *
 * @brief
 * Asserts that a given port is of the specified type
 * with the expected kobject pointer.
 *
 * @decription
 * Port type confusion can lead to catastrophic system compromise,
 * this function can be used in choke points to ensure ports are
 * what they're expected to be before their use.
 *
 * @note It is allowed for the kobject pointer to be NULL,
 *       as in some cases ipc_kobject_disable() can be raced with this check.
 *
 * @param port          The target port.
 * @param kobject       The kobject pointer this port should be associated to.
 * @param kotype        The kobject type this port should have.
 */
extern void         ipc_kobject_require(
	ipc_port_t                  port,
	ipc_kobject_t               kobject,
	ipc_kobject_type_t          kotype);

extern ipc_kobject_t ipc_kobject_get_raw(
	ipc_port_t                  port,
	ipc_kobject_type_t          type);

extern ipc_kobject_t ipc_kobject_get_locked(
	ipc_port_t                  port,
	ipc_kobject_type_t          type);

extern ipc_kobject_t ipc_kobject_get_stable(
	ipc_port_t                  port,
	ipc_kobject_type_t          type);

extern ipc_kobject_t ipc_kobject_disable_locked(
	ipc_port_t                  port,
	ipc_kobject_type_t          type);

extern ipc_kobject_t ipc_kobject_disable(
	ipc_port_t                  port,
	ipc_kobject_type_t          type);

/* Check if a kobject can be copied out to a given space */
extern bool     ipc_kobject_label_check_or_substitute(
	ipc_space_t                 space,
	ipc_port_t                  port,
	ipc_object_label_t         *label,
	mach_msg_type_name_t        msgt_name,
	ipc_port_t                 *subst_portp) __result_use_check;

/*!
 * @brief
 * Evaluate a port for substitution and kobject label rules.
 *
 * @discussion
 * This function has a really cumbersome calling convention.
 *
 * If it returns false, then it means that some policy was violated,
 * in that case, @c port has been unlocked, and @c label put.
 *
 * If it returns true, and subst_portp is not IP_NULL, then @c port
 * has been unlocked, and @c label put, and the caller is expected
 * to redrive evaluation with that substitution port.
 *
 * If it returns true, and subst_port is IP_NULL, then @c port
 * is still locked, and @c label still valid, and the caller is expected
 * to proceed further.
 *
 * @param space         The current space
 * @param port          The port to evaluate (must be locked and active)
 * @param label         (In/out) the label for @c port.
 * @param msgt_name     The disposition for @c port in the message.
 * @param subst_portp   (out) an optional substitution port,
 *                      to replace @c port with.
 */
__result_use_check
static inline bool
ip_label_check_or_substitute(
	ipc_space_t                 space,
	ipc_port_t                  port,
	ipc_object_label_t         *label,
	mach_msg_type_name_t        msgt_name,
	ipc_port_t                 *subst_portp)
{
	if (!io_is_kobject_type(label->io_type) || !label->iol_kobject) {
		*subst_portp = IP_NULL;
		return true;
	}
	return ipc_kobject_label_check_or_substitute(space, port, label, msgt_name, subst_portp);
}

/* implementation details */

__startup_func
extern void ipc_kobject_register_startup(
	ipc_kobject_ops_t           ops);

/* Dispatch a kernel server function */
extern ipc_kmsg_t ipc_kobject_server(
	ipc_port_t                  receiver,
	ipc_kmsg_t                  request,
	mach_msg_option64_t         option);

#define null_conversion(port)   (port)

extern void ipc_kobject_notify_send_once_and_unlock(
	ipc_port_t                  port);

extern kern_return_t uext_server(
	ipc_port_t                  receiver,
	ipc_kmsg_t                  request,
	ipc_kmsg_t                  *reply);

#endif /* MACH_KERNEL_PRIVATE */
#if XNU_KERNEL_PRIVATE

/*!
 * @function ipc_typed_port_copyin_send()
 *
 * @brief
 * Copies in a naked send right for the specified typed port.
 *
 * @decription
 * This function will validate that the specified port is pointing
 * to the expected kobject type, unless @c kotype is IOT_ANY,
 * in which case any right is accepted.
 *
 * @param space         The space to copyin in from.
 * @param name          The name to copyin.
 * @param kotype        The kobject type this port should have.
 * @param port          The resulting port or IP_NULL.
 *
 * @returns
 * - KERN_SUCCESS       Acquired an object, possibly IP_DEAD.
 * - KERN_INVALID_TASK  The space is dead.
 * - KERN_INVALID_NAME  Name doesn't exist in space.
 * - KERN_INVALID_RIGHT Name doesn't denote correct right.
 * - KERN_INVALID_CAPABILITY
 *                      The right isn't of the right kobject type.
 */
extern kern_return_t ipc_typed_port_copyin_send(
	ipc_space_t                 space,
	mach_port_name_t            name,
	ipc_kobject_type_t          kotype,
	ipc_port_t                 *port);

/*!
 * @function ipc_typed_port_release_send()
 *
 * @brief
 * Release a send right for a typed port.
 *
 * @description
 * This is an alias for ipc_port_release_send() that the BSD side can use.
 * If @c kotype is IOT_ANY, any right is accepted.
 */
extern void       ipc_typed_port_release_send(
	ipc_port_t                  port,
	ipc_kobject_type_t          kotype);

#endif /* XNU_KERNEL_PRIVATE */
#pragma GCC visibility pop
__END_DECLS

#endif /* _KERN_IPC_KOBJECT_H_ */