This is xnu-11215.1.10. See this file in:
#include <darwintest.h>
#include <darwintest_utils.h>

#include <mach/mach.h>
#include <mach/mach_types.h>
#include <mach/mach_port.h>
#include <mach/message.h>
#include <mach/mach_error.h>
#include <mach/vm_map.h>


T_GLOBAL_META(
	T_META_NAMESPACE("xnu.ipc"),
	T_META_CHECK_LEAKS(false),
	T_META_RUN_CONCURRENTLY(true),
	T_META_RADAR_COMPONENT_NAME("xnu"),
	T_META_RADAR_COMPONENT_VERSION("IPC"));

/*
 * This file checks the basics of the MACH IPC basic transport mechanism
 */

#pragma mark helpers

#define DEFAULT_CONTEXT ((mach_port_context_t)0x42424242)

static mach_port_name_t
t_port_construct_full(
	uint32_t                mpo_flags,
	mach_port_msgcount_t    qlimit)
{
	mach_port_options_t opts = {
		.flags = mpo_flags | MPO_QLIMIT,
		.mpl.mpl_qlimit = qlimit,
	};
	mach_port_name_t name;
	kern_return_t kr;

	kr = mach_port_construct(mach_task_self(), &opts, DEFAULT_CONTEXT, &name);
	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_construct");

	return name;
}
#define t_port_construct()      t_port_construct_full(MPO_INSERT_SEND_RIGHT, 1)


static void
t_port_destruct_full(
	mach_port_name_t       *name,
	uint16_t                srights,
	mach_port_context_t     ctx)
{
	kern_return_t kr;

	kr = mach_port_destruct(mach_task_self(), *name, -srights, ctx);
	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_construct");

	*name = MACH_PORT_NULL;
}
#define t_port_destruct(name)   t_port_destruct_full(name, 1, 0)

static mach_port_name_t
t_make_sonce(
	mach_port_name_t        port)
{
	mach_msg_type_name_t disp;
	mach_port_name_t name;
	kern_return_t kr;

	kr = mach_port_extract_right(mach_task_self(), port,
	    MACH_MSG_TYPE_MAKE_SEND_ONCE, &name, &disp);
	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "make-send-once");
	T_QUIET; T_ASSERT_EQ(disp, MACH_MSG_TYPE_PORT_SEND_ONCE,
	    "check make-sonce");

	return name;
}

static void
t_deallocate_sonce(
	mach_port_name_t        port)
{
	kern_return_t kr;

	kr = mach_port_deallocate(mach_task_self(), port);
	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "dealloc-send-once");
}

static void
t_vm_deallocate(
	void                   *addr,
	vm_size_t               size)
{
	kern_return_t kr;

	kr = vm_deallocate(mach_task_self(), (vm_address_t)addr, size);
	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_deallocate");
}

static kern_return_t
t_receive(
	mach_port_name_t        name,
	mach_msg_header_t      *msg,
	mach_msg_size_t         size,
	mach_msg_option64_t     opts)
{
	opts |= MACH64_RCV_GUARDED_DESC | MACH64_RCV_MSG;
	return mach_msg2(msg, opts, *msg, 0, size, name, 0, 0);
}

__attribute__((overloadable))
static kern_return_t
t_send(
	mach_port_name_t        dest,
	mach_msg_header_t      *msg,
	void                   *upto,
	mach_msg_option64_t     opts)
{
	mach_msg_size_t    size = (mach_msg_size_t)((char *)upto - (char *)msg);

	opts |= MACH64_SEND_MSG | MACH64_SEND_MQ_CALL;

	msg->msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND, 0, 0, 0);
	msg->msgh_size = size;
	msg->msgh_remote_port = dest;
	msg->msgh_local_port = MACH_PORT_NULL;
	msg->msgh_voucher_port = MACH_PORT_NULL;
	msg->msgh_id = 42;
	return mach_msg2(msg, opts, *msg, size, 0, 0, 0, 0);
}

__attribute__((overloadable))
static kern_return_t
t_send(
	mach_port_name_t        dest,
	mach_msg_base_t        *base,
	void                   *upto,
	mach_msg_option64_t     opts)
{
	mach_msg_header_t *msg = &base->header;
	mach_msg_size_t    size = (mach_msg_size_t)((char *)upto - (char *)msg);

	opts |= MACH64_SEND_MSG | MACH64_SEND_MQ_CALL;

	msg->msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND, 0, 0,
	    MACH_MSGH_BITS_COMPLEX);
	msg->msgh_size = size;
	msg->msgh_remote_port = dest;
	msg->msgh_local_port = MACH_PORT_NULL;
	msg->msgh_voucher_port = MACH_PORT_NULL;
	msg->msgh_id = 42;
	return mach_msg2(msg, opts, *msg, size, 0, 0, 0, 0);
}

static void
t_fill_port(
	mach_port_name_t        dest,
	size_t                  n)
{
	for (size_t i = 0; i < n; i++) {
		mach_msg_header_t hdr;
		kern_return_t kr;

		kr = t_send(dest, &hdr, &hdr + 1, MACH64_SEND_TIMEOUT);
		T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "send to fill");
	}
}

static vm_size_t
t_check_0xff(
	const void             *addr,
	vm_size_t               size)
{
	for (size_t i = 0; i < size; i++) {
		if (((uint8_t *)addr)[i] != 0xff) {
			return i;
		}
	}

	return ~0ul;
}

#pragma mark trailer checks

T_DECL(mach_msg_trailer, "check trailer generation")
{
	mach_port_name_t rcv_name;
	security_token_t sec_token;
	audit_token_t audit_token;
	mach_msg_type_number_t count;
	kern_return_t kr;

	rcv_name = t_port_construct();

	count = TASK_SECURITY_TOKEN_COUNT;
	kr = task_info(mach_task_self(), TASK_SECURITY_TOKEN, (task_info_t)&sec_token, &count);
	T_ASSERT_MACH_SUCCESS(kr, "task_info(TASK_SECURITY_TOKEN)");

	count = TASK_AUDIT_TOKEN_COUNT;
	kr = task_info(mach_task_self(), TASK_AUDIT_TOKEN, (task_info_t)&audit_token, &count);
	T_ASSERT_MACH_SUCCESS(kr, "task_info(TASK_AUDIT_TOKEN)");

	for (int i = 0; i <= MACH_RCV_TRAILER_LABELS; i++) {
		mach_msg_option64_t topts = (mach_msg_option64_t)MACH_RCV_TRAILER_ELEMENTS(i);
		mach_msg_size_t     tsize = REQUESTED_TRAILER_SIZE(topts);
		struct {
			mach_msg_header_t      hdr;
			mach_msg_max_trailer_t trailer;
			uint32_t               sentinel;
		} buf;

		switch (i) {
		case MACH_RCV_TRAILER_NULL:
		case MACH_RCV_TRAILER_SEQNO:
		case MACH_RCV_TRAILER_SENDER:
		case MACH_RCV_TRAILER_AUDIT:
		case MACH_RCV_TRAILER_CTX:
		case MACH_RCV_TRAILER_AV:
			break;
		default:
			continue;
		}

		memset(&buf, 0xff, sizeof(buf));
		kr = t_send(rcv_name, &buf.hdr, &buf.trailer, MACH64_MSG_OPTION_NONE);
		T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "sending message with trailer %d", i);

		kr = t_receive(rcv_name, &buf.hdr, sizeof(buf), topts);
		T_ASSERT_MACH_SUCCESS(kr, "receiving message with trailer %d", i);

		T_EXPECT_EQ(buf.hdr.msgh_size, sizeof(buf.hdr), "msgh_size");
		T_EXPECT_EQ(buf.trailer.msgh_trailer_type, MACH_MSG_TRAILER_FORMAT_0, "msgh_trailer_type");
		T_EXPECT_EQ(buf.trailer.msgh_trailer_size, tsize, "msgh_trailer_size");
		if (tsize > offsetof(mach_msg_max_trailer_t, msgh_sender)) {
			T_EXPECT_EQ(memcmp(&buf.trailer.msgh_sender, &sec_token,
			    sizeof(sec_token)), 0, "msgh_sender");
		}
		if (tsize > offsetof(mach_msg_max_trailer_t, msgh_audit)) {
			T_EXPECT_EQ(memcmp(&buf.trailer.msgh_audit, &audit_token,
			    sizeof(audit_token)), 0, "msgh_audit");
		}
		if (tsize > offsetof(mach_msg_max_trailer_t, msgh_context)) {
			T_EXPECT_EQ(buf.trailer.msgh_context, DEFAULT_CONTEXT,
			    "msgh_context");
		}
		if (tsize > offsetof(mach_msg_max_trailer_t, msgh_ad)) {
			T_EXPECT_EQ(buf.trailer.msgh_ad, 0, "msgh_ad");
		}
		if (tsize > offsetof(mach_msg_max_trailer_t, msgh_labels)) {
			T_EXPECT_EQ(buf.trailer.msgh_labels.sender, 0, "msgh_labels");
		}

		T_QUIET; T_EXPECT_EQ(t_check_0xff((char *)&buf.trailer + tsize,
		    sizeof(buf.trailer) + sizeof(buf.sentinel) - tsize), ~0ul,
		    "should be unmodified");
	}

	t_port_destruct(&rcv_name);
}

#pragma mark descriptor checks

static const mach_msg_type_name_t port_dispositions[] = {
	MACH_MSG_TYPE_MOVE_RECEIVE,
	MACH_MSG_TYPE_MOVE_SEND,
	MACH_MSG_TYPE_MOVE_SEND_ONCE,
	MACH_MSG_TYPE_COPY_SEND,
	MACH_MSG_TYPE_MAKE_SEND,
	MACH_MSG_TYPE_MAKE_SEND_ONCE,
	0,
};

struct msg_complex_port {
	mach_msg_base_t         base;
	mach_msg_port_descriptor_t dsc;
	mach_msg_max_trailer_t  trailer;
};

struct msg_complex_guarded_port {
	mach_msg_base_t         base;
	mach_msg_guarded_port_descriptor_t dsc;
	mach_msg_max_trailer_t  trailer;
};

struct msg_complex_port_array {
	mach_msg_base_t         base;
	mach_msg_ool_ports_descriptor_t dsc;
	mach_msg_max_trailer_t  trailer;
	mach_port_name_t        array[2];
};

struct msg_complex_memory {
	mach_msg_base_t         base;
	mach_msg_ool_descriptor_t dsc;
	mach_msg_max_trailer_t  trailer;
};

static void
t_fill_complex_port_msg(
	struct msg_complex_port *msg,
	mach_msg_type_name_t    disp,
	mach_port_name_t        name)
{
	*msg = (struct msg_complex_port){
		.base.body.msgh_descriptor_count = 1,
		.dsc = {
			.type        = MACH_MSG_PORT_DESCRIPTOR,
			.disposition = disp,
			.name        = name,
		},
	};
}

static void
t_fill_complex_port_guarded_msg(
	struct msg_complex_guarded_port *msg,
	mach_msg_type_name_t    disp,
	mach_port_name_t        name,
	mach_msg_guard_flags_t  flags)
{
	*msg = (struct msg_complex_guarded_port){
		.base.body.msgh_descriptor_count = 1,
		.dsc = {
			.type        = MACH_MSG_GUARDED_PORT_DESCRIPTOR,
			.disposition = disp,
			.name        = name,
			.context     = DEFAULT_CONTEXT,
			.flags       = flags,
		},
	};
	if (flags & MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND) {
		msg->dsc.context = 0;
	}
}

static void
t_fill_complex_memory_msg(
	struct msg_complex_memory *msg,
	vm_address_t            memory,
	mach_msg_size_t         size,
	bool                    move)
{
	*msg = (struct msg_complex_memory){
		.base.body.msgh_descriptor_count = 1,
		.dsc = {
			.type        = MACH_MSG_OOL_DESCRIPTOR,
			.address     = (void *)memory,
			.size        = size,
			.deallocate  = move,
		},
	};
}

static void
t_fill_complex_port_array_msg(
	struct msg_complex_port_array *msg,
	mach_msg_type_name_t    disp,
	mach_port_name_t        name1,
	mach_port_name_t        name2)
{
	*msg = (struct msg_complex_port_array){
		.base.body.msgh_descriptor_count = 1,
		.dsc = {
			.type        = MACH_MSG_OOL_PORTS_DESCRIPTOR,
			.disposition = disp,
			.address     = &msg->array,
			.count       = 2,
			.deallocate  = false,
		},
		.array[0] = name1,
		.array[1] = name2,
	};
}

static void
t_mach_msg_descriptor_port(bool pseudo_receive)
{
	mach_port_name_t rcv_name, port;
	kern_return_t kr;

	rcv_name = t_port_construct();
	port     = t_port_construct();

	if (pseudo_receive) {
		t_fill_port(rcv_name, 1);
	}

	for (size_t i = 0; i < port_dispositions[i]; i++) {
		mach_msg_type_name_t disp = port_dispositions[i];
		mach_port_name_t name = port;
		struct msg_complex_port msg;

		if (disp == MACH_MSG_TYPE_MOVE_SEND_ONCE) {
			name = t_make_sonce(port);
		}

		t_fill_complex_port_msg(&msg, disp, name);

		kr = t_send(rcv_name, &msg.base, &msg.trailer, MACH64_SEND_TIMEOUT);
		if (pseudo_receive) {
			T_ASSERT_MACH_ERROR(kr, MACH_SEND_TIMED_OUT,
			    "pseudo-rcv(disposition:%d)", disp);
		} else {
			T_ASSERT_MACH_SUCCESS(kr, "send(disposition:%d)", disp);

			kr = t_receive(rcv_name, &msg.base.header, sizeof(msg),
			    MACH64_MSG_OPTION_NONE);
			T_ASSERT_MACH_SUCCESS(kr, "recv(disposition:%d)", disp);
		}

		switch (disp) {
		case MACH_MSG_TYPE_MOVE_RECEIVE:
			disp = MACH_MSG_TYPE_PORT_RECEIVE;
			break;
		case MACH_MSG_TYPE_MOVE_SEND:
		case MACH_MSG_TYPE_COPY_SEND:
		case MACH_MSG_TYPE_MAKE_SEND:
			disp = MACH_MSG_TYPE_PORT_SEND;
			break;
		case MACH_MSG_TYPE_MOVE_SEND_ONCE:
		case MACH_MSG_TYPE_MAKE_SEND_ONCE:
			disp = MACH_MSG_TYPE_PORT_SEND_ONCE;
			break;
		}

		T_ASSERT_EQ(msg.base.header.msgh_bits & MACH_MSGH_BITS_COMPLEX,
		    MACH_MSGH_BITS_COMPLEX, "verify complex");
		T_ASSERT_EQ(msg.base.body.msgh_descriptor_count, 1u, "verify dsc count");
		T_ASSERT_EQ((mach_msg_descriptor_type_t)msg.dsc.type, MACH_MSG_PORT_DESCRIPTOR, "verify type");
		T_ASSERT_EQ((mach_msg_type_name_t)msg.dsc.disposition, disp, "verify disposition");
		if (disp == MACH_MSG_TYPE_PORT_RECEIVE ||
		    disp == MACH_PORT_TYPE_SEND) {
			T_ASSERT_EQ(msg.dsc.name, name, "verify name");
		}

		if (disp == MACH_MSG_TYPE_PORT_SEND_ONCE) {
			t_deallocate_sonce(msg.dsc.name);
		}
	}

	t_port_destruct_full(&port, 3, 0); /* did a COPY_SEND and a MAKE_SEND */
	t_port_destruct(&rcv_name);
}

T_DECL(mach_msg_descriptor_port, "check port descriptors")
{
	T_LOG("regular receive");
	t_mach_msg_descriptor_port(false);
	T_LOG("pseudo receive");
	t_mach_msg_descriptor_port(true);
}

static void
t_mach_msg_descriptor_guarded_port(bool pseudo_receive)
{
	mach_port_name_t rcv_name, port;
	kern_return_t kr;

	rcv_name = t_port_construct();

	if (pseudo_receive) {
		t_fill_port(rcv_name, 1);
	}

	static const mach_msg_guard_flags_t test_flags[] = {
		MACH_MSG_GUARD_FLAGS_IMMOVABLE_RECEIVE,
		MACH_MSG_GUARD_FLAGS_IMMOVABLE_RECEIVE | MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND,
		0,
	};

	for (size_t i = 0; test_flags[i]; i++) {
		struct msg_complex_guarded_port msg;
		mach_port_context_t ctx;

		if (test_flags[i] & MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND) {
			port = t_port_construct();
		} else {
			port = t_port_construct_full(MPO_INSERT_SEND_RIGHT | MPO_CONTEXT_AS_GUARD, 1);
		}

		t_fill_complex_port_guarded_msg(&msg, MACH_MSG_TYPE_MOVE_RECEIVE,
		    port, test_flags[i]);

		kr = t_send(rcv_name, &msg.base, &msg.trailer, MACH64_SEND_TIMEOUT);
		if (pseudo_receive) {
			T_ASSERT_MACH_ERROR(kr, MACH_SEND_TIMED_OUT, "pseudo-rcv");
		} else {
			T_ASSERT_MACH_SUCCESS(kr, "send");

			kr = t_receive(rcv_name, &msg.base.header, sizeof(msg),
			    MACH64_MSG_OPTION_NONE);
			T_ASSERT_MACH_SUCCESS(kr, "recv");
		}

		T_ASSERT_EQ(msg.base.header.msgh_bits & MACH_MSGH_BITS_COMPLEX,
		    MACH_MSGH_BITS_COMPLEX, "verify complex");
		T_ASSERT_EQ(msg.base.body.msgh_descriptor_count, 1u, "verify dsc count");
		T_ASSERT_EQ((mach_msg_descriptor_type_t)msg.dsc.type,
		    MACH_MSG_GUARDED_PORT_DESCRIPTOR, "verify type");
		T_ASSERT_EQ((mach_msg_type_name_t)msg.dsc.disposition,
		    MACH_MSG_TYPE_PORT_RECEIVE, "verify disposition");
		T_ASSERT_EQ(msg.dsc.name, port, "verify name");
		ctx = (mach_port_context_t)&msg.base;
		T_ASSERT_EQ(msg.dsc.context, ctx, "verify context");
		t_port_destruct_full(&port, 1, ctx);
	}

	t_port_destruct(&rcv_name);
}

T_DECL(mach_msg_descriptor_guarded_port, "check guarded port descriptors")
{
	T_LOG("regular receive");
	t_mach_msg_descriptor_guarded_port(false);
	T_LOG("pseudo receive");
	t_mach_msg_descriptor_guarded_port(true);
}

static void
t_mach_msg_descriptor_port_array(bool pseudo_receive)
{
	mach_port_name_t rcv_name, port1, port2;
	kern_return_t kr;

	rcv_name = t_port_construct();
	port1    = t_port_construct();
	port2    = t_port_construct();

	if (pseudo_receive) {
		t_fill_port(rcv_name, 1);
	}

	for (size_t i = 0; i < port_dispositions[i]; i++) {
		mach_msg_type_name_t disp = port_dispositions[i];
		mach_port_name_t name1 = port1;
		mach_port_name_t name2 = port2;
		struct msg_complex_port_array msg;
		mach_port_name_t *array;

		if (disp == MACH_MSG_TYPE_MOVE_SEND_ONCE) {
			name1 = t_make_sonce(port1);
			name2 = t_make_sonce(port2);
		}

		t_fill_complex_port_array_msg(&msg, disp, name1, name2);

		kr = t_send(rcv_name, &msg.base, &msg.trailer, MACH64_SEND_TIMEOUT);
		if (pseudo_receive) {
			T_ASSERT_MACH_ERROR(kr, MACH_SEND_TIMED_OUT,
			    "pseudo-rcv(disposition:%d)", disp);
		} else {
			T_ASSERT_MACH_SUCCESS(kr, "send(disposition:%d)", disp);

			kr = t_receive(rcv_name, &msg.base.header, sizeof(msg),
			    MACH64_MSG_OPTION_NONE);
			T_ASSERT_MACH_SUCCESS(kr, "recv(disposition:%d)", disp);
		}

		switch (disp) {
		case MACH_MSG_TYPE_MOVE_RECEIVE:
			disp = MACH_MSG_TYPE_PORT_RECEIVE;
			break;
		case MACH_MSG_TYPE_MOVE_SEND:
		case MACH_MSG_TYPE_COPY_SEND:
		case MACH_MSG_TYPE_MAKE_SEND:
			disp = MACH_MSG_TYPE_PORT_SEND;
			break;
		case MACH_MSG_TYPE_MOVE_SEND_ONCE:
		case MACH_MSG_TYPE_MAKE_SEND_ONCE:
			disp = MACH_MSG_TYPE_PORT_SEND_ONCE;
			break;
		}

		array = msg.dsc.address;

		T_ASSERT_EQ(msg.base.header.msgh_bits & MACH_MSGH_BITS_COMPLEX,
		    MACH_MSGH_BITS_COMPLEX, "verify complex");
		T_ASSERT_EQ(msg.base.body.msgh_descriptor_count, 1u, "verify dsc count");
		T_ASSERT_EQ((mach_msg_descriptor_type_t)msg.dsc.type, MACH_MSG_OOL_PORTS_DESCRIPTOR, "verify type");
		T_ASSERT_EQ((mach_msg_type_name_t)msg.dsc.disposition, disp, "verify disposition");
		T_ASSERT_EQ(msg.dsc.count, 2u, "verify count");
		T_ASSERT_EQ((bool)msg.dsc.deallocate, true, "verify deallocate");

		if (disp == MACH_MSG_TYPE_PORT_RECEIVE ||
		    disp == MACH_PORT_TYPE_SEND) {
			T_ASSERT_EQ(array[0], name1, "verify name");
			T_ASSERT_EQ(array[1], name2, "verify name");
		}

		if (disp == MACH_MSG_TYPE_PORT_SEND_ONCE) {
			t_deallocate_sonce(array[0]);
			t_deallocate_sonce(array[1]);
		}

		t_vm_deallocate(array, sizeof(array[0]) * msg.dsc.count);
	}

	t_port_destruct_full(&port1, 3, 0); /* did a COPY_SEND and a MAKE_SEND */
	t_port_destruct_full(&port2, 3, 0); /* did a COPY_SEND and a MAKE_SEND */
	t_port_destruct(&rcv_name);
}

T_DECL(mach_msg_descriptor_port_array, "check port array descriptors")
{
	T_LOG("regular receive");
	t_mach_msg_descriptor_port_array(false);
	T_LOG("pseudo receive");
	t_mach_msg_descriptor_port_array(true);
}

static void
t_mach_msg_descriptor_memory(bool pseudo_receive)
{
	mach_port_name_t rcv_name;
	struct msg_complex_memory msg;
	kern_return_t kr;
	vm_address_t addr;
	mach_msg_size_t size = 1u << 20;

	rcv_name = t_port_construct();

	if (pseudo_receive) {
		t_fill_port(rcv_name, 1);
	}

	kr = vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE);
	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_allocate(1M)");

	memset((void *)addr, 0xff, size);

	for (size_t n = 0; n < 2; n++) {
		t_fill_complex_memory_msg(&msg, addr, size, n > 0);

		kr = t_send(rcv_name, &msg.base, &msg.trailer, MACH64_SEND_TIMEOUT);
		if (pseudo_receive) {
			T_ASSERT_MACH_ERROR(kr, MACH_SEND_TIMED_OUT, "pseudo-rcv");
		} else {
			T_ASSERT_MACH_SUCCESS(kr, "send");

			kr = t_receive(rcv_name, &msg.base.header, sizeof(msg),
			    MACH64_MSG_OPTION_NONE);
			T_ASSERT_MACH_SUCCESS(kr, "recv");
		}

		T_ASSERT_EQ(msg.base.header.msgh_bits & MACH_MSGH_BITS_COMPLEX,
		    MACH_MSGH_BITS_COMPLEX, "verify complex");
		T_ASSERT_EQ(msg.base.body.msgh_descriptor_count, 1u, "verify dsc count");
		T_ASSERT_EQ((mach_msg_descriptor_type_t)msg.dsc.type,
		    MACH_MSG_OOL_DESCRIPTOR, "verify type");
		T_ASSERT_EQ(msg.dsc.size, size, "verify dsc count");
		T_ASSERT_EQ(t_check_0xff(msg.dsc.address, size), ~0ul,
		    "check content");

		if (n == 0) {
			t_vm_deallocate(msg.dsc.address, size);
		} else {
			addr = (vm_address_t)msg.dsc.address;
		}
	}

	t_vm_deallocate((void *)addr, size);
	t_port_destruct(&rcv_name);
}

T_DECL(mach_msg_descriptor_memory, "check memory descriptors")
{
	T_LOG("regular receive");
	t_mach_msg_descriptor_memory(false);
	T_LOG("pseudo receive");
	t_mach_msg_descriptor_memory(true);
}