This is xnu-12377.1.9. See this file in:
/*
 * Copyright (c) 2024 Apple 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@
 */

/*
 * vm/configurator_vm_behavior_set.c
 *
 * Test vm_behavior_set with many different VM states.
 */

#include "configurator/vm_configurator_tests.h"

T_GLOBAL_META(
	T_META_NAMESPACE("xnu.vm.configurator"),
	T_META_RADAR_COMPONENT_NAME("xnu"),
	T_META_RADAR_COMPONENT_VERSION("VM"),
	T_META_RUN_CONCURRENTLY(true),
	T_META_ASROOT(true),  /* required for vm submap sysctls */
	T_META_ALL_VALID_ARCHS(true)
	);

static void
write_one_memory(
	checker_list_t *checker_list,
	vm_entry_checker_t *checker)
{
	if (checker->kind == Allocation &&
	    prot_contains_all(checker->protection, VM_PROT_READ | VM_PROT_WRITE)) {
		checker_fault_for_prot_not_cow(checker_list, checker, VM_PROT_WRITE);
		memset((char *)checker->address, 0xff, checker->size);
		if (checker->object) {
			checker->object->fill_pattern.mode = Fill;
			checker->object->fill_pattern.pattern = 0xffffffffffffffff;
		}
	}
}

static void
write_memory(
	checker_list_t *checker_list,
	mach_vm_address_t start,
	mach_vm_size_t size)
{
	entry_checker_range_t limit =
	    checker_list_find_range_including_holes(checker_list, start, size);
	/* TODO: this writes beyond [start, size) */
	FOREACH_CHECKER(checker, limit) {
		write_one_memory(checker_list, checker);
	}
}

/* Test vm_behavior_set(behavior). This supports several behaviors. */
static test_result_t
vm_behavior_common_no_cow(
	checker_list_t *checker_list,
	mach_vm_address_t start,
	mach_vm_size_t size,
	vm_behavior_t behavior,
	bool has_holes)
{
	kern_return_t kr;
	test_result_t test_results[1];
	bool clip, reject_submaps;

	kern_return_t expected_kr = KERN_SUCCESS;
	if (has_holes) {
		expected_kr = KERN_INVALID_ADDRESS;
	}

	switch (behavior) {
	case VM_BEHAVIOR_DEFAULT:
		clip = true;
		reject_submaps = false;
		break;
	case VM_BEHAVIOR_FREE:
		clip = false;
		reject_submaps = false;
		break;
	case VM_BEHAVIOR_CAN_REUSE:
		clip = false;
		reject_submaps = true;
		break;
	default:
		T_FAIL("don't know whether to clip with behavior %s",
		    name_for_behavior(behavior));
		return TestFailed;
	}

	entry_checker_range_t limit;
	if (has_holes) {
		limit = checker_list_find_range_including_holes(checker_list, start, size);
	} else {
		limit = checker_list_find_range(checker_list, start, size);
		if (clip) {
			checker_clip_left(checker_list, limit.head, start);
		}
		bool rejected = false;
		if (reject_submaps) {
			FOREACH_CHECKER(checker, limit) {
				if (checker->kind == Submap) {
					expected_kr = KERN_INVALID_ADDRESS;
					rejected = true;
					break;
				}
			}
		}
		if (clip) {
			checker_clip_right(checker_list, limit.tail, start + size);
		}
	}

	kr = mach_vm_behavior_set(mach_task_self(), start, size, behavior);
	if (kr != expected_kr) {
		T_FAIL("mach_vm_behavior_set(%s) failed (%s)",
		    name_for_behavior(behavior), name_for_kr(kr));
		return TestFailed;
	}

	/* Some behaviors destroy the pages, which affects the fill. */
	if (behavior == VM_BEHAVIOR_FREE) {
		FOREACH_CHECKER(checker, limit) {
			if (checker->object && checker->object->fill_pattern.mode == Fill) {
				checker->object->fill_pattern.pattern = 0;
				checker->object->fill_pattern.mode = DontFill;
			}
		}
	}

	TEMP_CSTRING(when, "after vm_behavior_set(%s)", name_for_behavior(behavior));
	test_results[0] = verify_vm_state(checker_list, when);

	return worst_result(test_results, countof(test_results));
}

static test_result_t
vm_behavior_no_cow_maybe_rw_maybe_holes(
	checker_list_t *checker_list,
	mach_vm_address_t start,
	mach_vm_size_t size,
	vm_behavior_t behavior,
	bool rw,
	bool has_holes)
{
	test_result_t result;

	result = vm_behavior_common_no_cow(
		checker_list, start, size, behavior, has_holes);
	if (result != TestSucceeded) {
		return result;
	}

	if (rw) {
		/* write to the memory and do it again */
		write_memory(checker_list, start, size);
		result = verify_vm_state(checker_list, "after write_memory");
		if (result != TestSucceeded) {
			return result;
		}

		result = vm_behavior_common_no_cow(
			checker_list, start, size, behavior, has_holes);
		if (result != TestSucceeded) {
			return result;
		}
	}

	return result;
}

static test_result_t
vm_behavior_default_no_cow_rw_no_holes(
	checker_list_t *checker_list,
	mach_vm_address_t start,
	mach_vm_size_t size)
{
	return vm_behavior_no_cow_maybe_rw_maybe_holes(
		checker_list, start, size, VM_BEHAVIOR_DEFAULT,
		true /* rw */, false /* holes */);
}

static test_result_t
vm_behavior_default_no_cow_rw_with_holes(
	checker_list_t *checker_list,
	mach_vm_address_t start,
	mach_vm_size_t size)
{
	return vm_behavior_no_cow_maybe_rw_maybe_holes(
		checker_list, start, size, VM_BEHAVIOR_DEFAULT,
		true /* rw */, true /* holes */);
}

static test_result_t
vm_behavior_default_no_cow_ro_no_holes(
	checker_list_t *checker_list,
	mach_vm_address_t start,
	mach_vm_size_t size)
{
	return vm_behavior_no_cow_maybe_rw_maybe_holes(
		checker_list, start, size, VM_BEHAVIOR_DEFAULT,
		false /* rw */, false /* holes */);
}

static test_result_t
vm_behavior_default_no_cow_ro_with_holes(
	checker_list_t *checker_list,
	mach_vm_address_t start,
	mach_vm_size_t size)
{
	return vm_behavior_no_cow_maybe_rw_maybe_holes(
		checker_list, start, size, VM_BEHAVIOR_DEFAULT,
		false /* rw */, true /* holes */);
}


static test_result_t
vm_behavior_free_no_cow_rw_no_holes(
	checker_list_t *checker_list,
	mach_vm_address_t start,
	mach_vm_size_t size)
{
	return vm_behavior_no_cow_maybe_rw_maybe_holes(
		checker_list, start, size, VM_BEHAVIOR_FREE,
		true /* rw */, false /* holes */);
}

static test_result_t
vm_behavior_free_no_cow_rw_with_holes(
	checker_list_t *checker_list,
	mach_vm_address_t start,
	mach_vm_size_t size)
{
	return vm_behavior_no_cow_maybe_rw_maybe_holes(
		checker_list, start, size, VM_BEHAVIOR_FREE,
		true /* rw */, true /* holes */);
}

static test_result_t
vm_behavior_free_no_cow_ro_no_holes(
	checker_list_t *checker_list,
	mach_vm_address_t start,
	mach_vm_size_t size)
{
	return vm_behavior_no_cow_maybe_rw_maybe_holes(
		checker_list, start, size, VM_BEHAVIOR_FREE,
		false /* rw */, false /* holes */);
}

static test_result_t
vm_behavior_free_no_cow_ro_with_holes(
	checker_list_t *checker_list,
	mach_vm_address_t start,
	mach_vm_size_t size)
{
	return vm_behavior_no_cow_maybe_rw_maybe_holes(
		checker_list, start, size, VM_BEHAVIOR_FREE,
		false /* rw */, true /* holes */);
}


static test_result_t
vm_behavior_can_reuse_no_cow_rw_no_holes(
	checker_list_t *checker_list,
	mach_vm_address_t start,
	mach_vm_size_t size)
{
	return vm_behavior_no_cow_maybe_rw_maybe_holes(
		checker_list, start, size, VM_BEHAVIOR_CAN_REUSE,
		true /* rw */, false /* holes */);
}

static test_result_t
vm_behavior_can_reuse_no_cow_rw_with_holes(
	checker_list_t *checker_list,
	mach_vm_address_t start,
	mach_vm_size_t size)
{
	return vm_behavior_no_cow_maybe_rw_maybe_holes(
		checker_list, start, size, VM_BEHAVIOR_CAN_REUSE,
		true /* rw */, true /* holes */);
}

static test_result_t
vm_behavior_can_reuse_no_cow_ro_no_holes(
	checker_list_t *checker_list,
	mach_vm_address_t start,
	mach_vm_size_t size)
{
	return vm_behavior_no_cow_maybe_rw_maybe_holes(
		checker_list, start, size, VM_BEHAVIOR_CAN_REUSE,
		false /* rw */, false /* holes */);
}

static test_result_t
vm_behavior_can_reuse_no_cow_ro_with_holes(
	checker_list_t *checker_list,
	mach_vm_address_t start,
	mach_vm_size_t size)
{
	return vm_behavior_no_cow_maybe_rw_maybe_holes(
		checker_list, start, size, VM_BEHAVIOR_CAN_REUSE,
		false /* rw */, true /* holes */);
}


static test_result_t
vm_behavior_zero_once(
	checker_list_t *checker_list,
	mach_vm_address_t start,
	mach_vm_size_t size,
	const char *message_suffix)
{
	kern_return_t expected_kr = KERN_SUCCESS;
	kern_return_t kr;
	entry_checker_range_t limit =
	    checker_list_find_range_including_holes(checker_list, start, size);

	/*
	 * vm_behavior_set(ZERO) stops at un-writeable pages
	 * so we can't use the common code from other behaviors
	 */

	if (task_page_size_less_than_vm_page_size()) {
		/*
		 * VM_BEHAVIOR_ZERO does nothing and returns KERN_NO_ACCESS
		 * if the map's page size is less than the VM's page size.
		 */
		T_LOG("note: VM_BEHAVIOR_ZERO does nothing on this platform");
		expected_kr = KERN_NO_ACCESS;
		goto checker_update_done;
	}

	/* Check for holes first. */
	FOREACH_CHECKER(checker, limit) {
		if (checker->kind == Hole) {
			expected_kr = KERN_INVALID_ADDRESS;
			goto checker_update_done;
		}
	}

	/* Zero the checkers' fill patterns, stopping if we hit an unacceptable entry */
	FOREACH_CHECKER(checker, limit) {
		if (!prot_contains_all(checker->protection, VM_PROT_WRITE)) {
			/* stop after the first unwriteable entry */
			expected_kr = KERN_PROTECTION_FAILURE;
			goto checker_update_done;
		}
		if (checker->kind == Submap) {
			/* stop at submaps */
			expected_kr = KERN_NO_ACCESS;
			goto checker_update_done;
		}

		/* writeable allocation: memory is now zeros */
		if (checker->object && checker->object->fill_pattern.mode == Fill) {
			checker->object->fill_pattern.pattern = 0;
			checker->object->fill_pattern.mode = DontFill;
		}
	}

checker_update_done:
	kr = mach_vm_behavior_set(mach_task_self(), start, size, VM_BEHAVIOR_ZERO);
	if (kr != expected_kr) {
		T_EXPECT_MACH_ERROR(kr, expected_kr, "mach_vm_behavior_set(VM_BEHAVIOR_ZERO)");
		return TestFailed;
	}

	TEMP_CSTRING(when, "after vm_behavior_set(VM_BEHAVIOR_ZERO) %s", message_suffix);
	return verify_vm_state(checker_list, when);
}

static test_result_t
vm_behavior_zero(
	checker_list_t *checker_list,
	mach_vm_address_t start,
	mach_vm_size_t size)
{
	test_result_t result;

	result = vm_behavior_zero_once(checker_list, start, size, "first time");
	if (result != TestSucceeded) {
		return result;
	}

	/* write to the memory and do it again */
	bool any_written = false;
	entry_checker_range_t limit = checker_list_find_range_including_holes(checker_list, start, size);
	/* TODO: this writes beyond [start, size) */
	FOREACH_CHECKER(checker, limit) {
		if (checker->kind != Allocation) {
			continue;
		}
		if (prot_contains_all(checker->protection, VM_PROT_READ | VM_PROT_WRITE)) {
			any_written = true;
			write_one_memory(checker_list, checker);
		} else {
			/* stop after first unwriteable entry */
			break;
		}
	}

	if (any_written) {
		result = verify_vm_state(checker_list, "after write_memory");
		if (result != TestSucceeded) {
			return result;
		}

		result = vm_behavior_zero_once(checker_list, start, size, "second time");
		if (result != TestSucceeded) {
			return result;
		}
	}

	return result;
}


T_DECL(vm_behavior_set_default,
    "run vm_behavior_set(DEFAULT) with various vm configurations")
{
	vm_tests_t tests = {
		.single_entry_1 = vm_behavior_default_no_cow_rw_no_holes,
		.single_entry_2 = vm_behavior_default_no_cow_rw_no_holes,
		.single_entry_3 = vm_behavior_default_no_cow_rw_no_holes,
		.single_entry_4 = vm_behavior_default_no_cow_rw_no_holes,

		.multiple_entries_1 = vm_behavior_default_no_cow_rw_no_holes,
		.multiple_entries_2 = vm_behavior_default_no_cow_rw_no_holes,
		.multiple_entries_3 = vm_behavior_default_no_cow_rw_no_holes,
		.multiple_entries_4 = vm_behavior_default_no_cow_rw_no_holes,
		.multiple_entries_5 = vm_behavior_default_no_cow_rw_no_holes,
		.multiple_entries_6 = vm_behavior_default_no_cow_rw_no_holes,

		.some_holes_1 = vm_behavior_default_no_cow_rw_with_holes,
		.some_holes_2 = vm_behavior_default_no_cow_rw_with_holes,
		.some_holes_3 = vm_behavior_default_no_cow_rw_with_holes,
		.some_holes_4 = vm_behavior_default_no_cow_rw_with_holes,
		.some_holes_5 = vm_behavior_default_no_cow_rw_with_holes,
		.some_holes_6 = vm_behavior_default_no_cow_rw_with_holes,
		.some_holes_7 = vm_behavior_default_no_cow_rw_with_holes,
		.some_holes_8 = vm_behavior_default_no_cow_rw_with_holes,
		.some_holes_9 = vm_behavior_default_no_cow_rw_with_holes,
		.some_holes_10 = vm_behavior_default_no_cow_rw_with_holes,
		.some_holes_11 = vm_behavior_default_no_cow_rw_with_holes,
		.some_holes_12 = vm_behavior_default_no_cow_rw_with_holes,

		.all_holes_1 = vm_behavior_default_no_cow_rw_with_holes,
		.all_holes_2 = vm_behavior_default_no_cow_rw_with_holes,
		.all_holes_3 = vm_behavior_default_no_cow_rw_with_holes,
		.all_holes_4 = vm_behavior_default_no_cow_rw_with_holes,

		.null_entry        = vm_behavior_default_no_cow_rw_no_holes,
		.nonresident_entry = vm_behavior_default_no_cow_rw_no_holes,
		.resident_entry    = vm_behavior_default_no_cow_rw_no_holes,

		.shared_entry               = test_is_unimplemented,
		.shared_entry_discontiguous = test_is_unimplemented,
		.shared_entry_partial       = test_is_unimplemented,
		.shared_entry_pairs         = test_is_unimplemented,
		.shared_entry_x1000         = test_is_unimplemented,

		.cow_entry = test_is_unimplemented,
		.cow_unreferenced = test_is_unimplemented,
		.cow_nocow = test_is_unimplemented,
		.nocow_cow = test_is_unimplemented,
		.cow_unreadable = test_is_unimplemented,
		.cow_unwriteable = test_is_unimplemented,

		.permanent_entry = vm_behavior_default_no_cow_rw_no_holes,
		.permanent_before_permanent = vm_behavior_default_no_cow_rw_no_holes,
		.permanent_before_allocation = vm_behavior_default_no_cow_rw_no_holes,
		.permanent_before_allocation_2 = vm_behavior_default_no_cow_rw_no_holes,
		.permanent_before_hole = vm_behavior_default_no_cow_rw_with_holes,
		.permanent_after_allocation = vm_behavior_default_no_cow_rw_no_holes,
		.permanent_after_hole = vm_behavior_default_no_cow_rw_with_holes,

		.single_submap_single_entry = vm_behavior_default_no_cow_rw_no_holes,
		.single_submap_single_entry_first_pages = vm_behavior_default_no_cow_rw_no_holes,
		.single_submap_single_entry_last_pages = vm_behavior_default_no_cow_rw_no_holes,
		.single_submap_single_entry_middle_pages = vm_behavior_default_no_cow_rw_no_holes,
		.single_submap_oversize_entry_at_start = vm_behavior_default_no_cow_rw_no_holes,
		.single_submap_oversize_entry_at_end = vm_behavior_default_no_cow_rw_no_holes,
		.single_submap_oversize_entry_at_both = vm_behavior_default_no_cow_rw_no_holes,

		.submap_before_allocation = vm_behavior_default_no_cow_rw_no_holes,
		.submap_after_allocation = vm_behavior_default_no_cow_rw_no_holes,
		.submap_before_hole = vm_behavior_default_no_cow_rw_with_holes,
		.submap_after_hole = vm_behavior_default_no_cow_rw_with_holes,
		.submap_allocation_submap_one_entry = vm_behavior_default_no_cow_rw_no_holes,
		.submap_allocation_submap_two_entries = vm_behavior_default_no_cow_rw_no_holes,
		.submap_allocation_submap_three_entries = vm_behavior_default_no_cow_rw_no_holes,

		.submap_before_allocation_ro = vm_behavior_default_no_cow_ro_no_holes,
		.submap_after_allocation_ro = vm_behavior_default_no_cow_ro_no_holes,
		.submap_before_hole_ro = vm_behavior_default_no_cow_ro_with_holes,
		.submap_after_hole_ro = vm_behavior_default_no_cow_ro_with_holes,
		.submap_allocation_submap_one_entry_ro = vm_behavior_default_no_cow_ro_no_holes,
		.submap_allocation_submap_two_entries_ro = vm_behavior_default_no_cow_ro_no_holes,
		.submap_allocation_submap_three_entries_ro = vm_behavior_default_no_cow_ro_no_holes,

		.protection_single_000_000 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_single_000_r00 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_single_r00_r00 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_single_000_0w0 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_single_0w0_0w0 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_single_000_rw0 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_single_r00_rw0 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_single_0w0_rw0 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_single_rw0_rw0 = vm_behavior_default_no_cow_rw_no_holes,

		.protection_pairs_000_000 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_pairs_000_r00 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_pairs_000_0w0 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_pairs_000_rw0 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_pairs_r00_000 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_pairs_r00_r00 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_pairs_r00_0w0 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_pairs_r00_rw0 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_pairs_0w0_000 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_pairs_0w0_r00 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_pairs_0w0_0w0 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_pairs_0w0_rw0 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_pairs_rw0_000 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_pairs_rw0_r00 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_pairs_rw0_0w0 = vm_behavior_default_no_cow_ro_no_holes,
		.protection_pairs_rw0_rw0 = vm_behavior_default_no_cow_rw_no_holes,
	};

	run_vm_tests("vm_behavior_set_default", __FILE__, &tests, argc, argv);
}


T_DECL(vm_behavior_set_free,
    "run vm_behavior_set(FREE) with various vm configurations")
{
	vm_tests_t tests = {
		.single_entry_1 = vm_behavior_free_no_cow_rw_no_holes,
		.single_entry_2 = vm_behavior_free_no_cow_rw_no_holes,
		.single_entry_3 = vm_behavior_free_no_cow_rw_no_holes,
		.single_entry_4 = vm_behavior_free_no_cow_rw_no_holes,

		.multiple_entries_1 = vm_behavior_free_no_cow_rw_no_holes,
		.multiple_entries_2 = vm_behavior_free_no_cow_rw_no_holes,
		.multiple_entries_3 = vm_behavior_free_no_cow_rw_no_holes,
		.multiple_entries_4 = vm_behavior_free_no_cow_rw_no_holes,
		.multiple_entries_5 = vm_behavior_free_no_cow_rw_no_holes,
		.multiple_entries_6 = vm_behavior_free_no_cow_rw_no_holes,

		.some_holes_1 = vm_behavior_free_no_cow_rw_with_holes,
		.some_holes_2 = vm_behavior_free_no_cow_rw_with_holes,
		.some_holes_3 = vm_behavior_free_no_cow_rw_with_holes,
		.some_holes_4 = vm_behavior_free_no_cow_rw_with_holes,
		.some_holes_5 = vm_behavior_free_no_cow_rw_with_holes,
		.some_holes_6 = vm_behavior_free_no_cow_rw_with_holes,
		.some_holes_7 = vm_behavior_free_no_cow_rw_with_holes,
		.some_holes_8 = vm_behavior_free_no_cow_rw_with_holes,
		.some_holes_9 = vm_behavior_free_no_cow_rw_with_holes,
		.some_holes_10 = vm_behavior_free_no_cow_rw_with_holes,
		.some_holes_11 = vm_behavior_free_no_cow_rw_with_holes,
		.some_holes_12 = vm_behavior_free_no_cow_rw_with_holes,

		.all_holes_1 = vm_behavior_free_no_cow_rw_with_holes,
		.all_holes_2 = vm_behavior_free_no_cow_rw_with_holes,
		.all_holes_3 = vm_behavior_free_no_cow_rw_with_holes,
		.all_holes_4 = vm_behavior_free_no_cow_rw_with_holes,

		.null_entry        = vm_behavior_free_no_cow_rw_no_holes,
		.nonresident_entry = vm_behavior_free_no_cow_rw_no_holes,
		.resident_entry    = vm_behavior_free_no_cow_rw_no_holes,

		.shared_entry               = test_is_unimplemented,
		.shared_entry_discontiguous = test_is_unimplemented,
		.shared_entry_partial       = test_is_unimplemented,
		.shared_entry_pairs         = test_is_unimplemented,
		.shared_entry_x1000         = test_is_unimplemented,

		.cow_entry = test_is_unimplemented,
		.cow_unreferenced = test_is_unimplemented,
		.cow_nocow = test_is_unimplemented,
		.nocow_cow = test_is_unimplemented,
		.cow_unreadable = test_is_unimplemented,
		.cow_unwriteable = test_is_unimplemented,

		.permanent_entry = vm_behavior_free_no_cow_rw_no_holes,
		.permanent_before_permanent = vm_behavior_free_no_cow_rw_no_holes,
		.permanent_before_allocation = vm_behavior_free_no_cow_rw_no_holes,
		.permanent_before_allocation_2 = vm_behavior_free_no_cow_rw_no_holes,
		.permanent_before_hole = vm_behavior_free_no_cow_rw_with_holes,
		.permanent_after_allocation = vm_behavior_free_no_cow_rw_no_holes,
		.permanent_after_hole = vm_behavior_free_no_cow_rw_with_holes,

		.single_submap_single_entry = vm_behavior_free_no_cow_rw_no_holes,
		.single_submap_single_entry_first_pages = vm_behavior_free_no_cow_rw_no_holes,
		.single_submap_single_entry_last_pages = vm_behavior_free_no_cow_rw_no_holes,
		.single_submap_single_entry_middle_pages = vm_behavior_free_no_cow_rw_no_holes,
		.single_submap_oversize_entry_at_start = vm_behavior_free_no_cow_rw_no_holes,
		.single_submap_oversize_entry_at_end = vm_behavior_free_no_cow_rw_no_holes,
		.single_submap_oversize_entry_at_both = vm_behavior_free_no_cow_rw_no_holes,

		.submap_before_allocation = vm_behavior_free_no_cow_rw_no_holes,
		.submap_after_allocation = vm_behavior_free_no_cow_rw_no_holes,
		.submap_before_hole = vm_behavior_free_no_cow_rw_with_holes,
		.submap_after_hole = vm_behavior_free_no_cow_rw_with_holes,
		.submap_allocation_submap_one_entry = vm_behavior_free_no_cow_rw_no_holes,
		.submap_allocation_submap_two_entries = vm_behavior_free_no_cow_rw_no_holes,
		.submap_allocation_submap_three_entries = vm_behavior_free_no_cow_rw_no_holes,

		.submap_before_allocation_ro = vm_behavior_free_no_cow_ro_no_holes,
		.submap_after_allocation_ro = vm_behavior_free_no_cow_ro_no_holes,
		.submap_before_hole_ro = vm_behavior_free_no_cow_ro_with_holes,
		.submap_after_hole_ro = vm_behavior_free_no_cow_ro_with_holes,
		.submap_allocation_submap_one_entry_ro = vm_behavior_free_no_cow_ro_no_holes,
		.submap_allocation_submap_two_entries_ro = vm_behavior_free_no_cow_ro_no_holes,
		.submap_allocation_submap_three_entries_ro = vm_behavior_free_no_cow_ro_no_holes,

		.protection_single_000_000 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_single_000_r00 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_single_r00_r00 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_single_000_0w0 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_single_0w0_0w0 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_single_000_rw0 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_single_r00_rw0 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_single_0w0_rw0 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_single_rw0_rw0 = vm_behavior_free_no_cow_rw_no_holes,

		.protection_pairs_000_000 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_pairs_000_r00 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_pairs_000_0w0 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_pairs_000_rw0 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_pairs_r00_000 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_pairs_r00_r00 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_pairs_r00_0w0 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_pairs_r00_rw0 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_pairs_0w0_000 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_pairs_0w0_r00 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_pairs_0w0_0w0 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_pairs_0w0_rw0 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_pairs_rw0_000 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_pairs_rw0_r00 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_pairs_rw0_0w0 = vm_behavior_free_no_cow_ro_no_holes,
		.protection_pairs_rw0_rw0 = vm_behavior_free_no_cow_rw_no_holes,
	};

	run_vm_tests("vm_behavior_set_free", __FILE__, &tests, argc, argv);
}


T_DECL(vm_behavior_set_can_reuse,
    "run vm_behavior_set(CAN_REUSE) with various vm configurations")
{
	if (isRosetta()) {
		/*
		 * CAN_REUSE requires vm_object page alignment,
		 * but Rosetta is less aligned than that and
		 * these tests don't yet have a way to adapt.
		 */
		T_PASS("warning: TODO wrong alignment for vm_behavior_set(CAN_REUSE) "
		    "on Rosetta; just passing instead");
		return;
	}

	vm_tests_t tests = {
		.single_entry_1 = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.single_entry_2 = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.single_entry_3 = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.single_entry_4 = vm_behavior_can_reuse_no_cow_rw_no_holes,

		.multiple_entries_1 = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.multiple_entries_2 = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.multiple_entries_3 = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.multiple_entries_4 = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.multiple_entries_5 = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.multiple_entries_6 = vm_behavior_can_reuse_no_cow_rw_no_holes,

		.some_holes_1 = vm_behavior_can_reuse_no_cow_rw_with_holes,
		.some_holes_2 = vm_behavior_can_reuse_no_cow_rw_with_holes,
		.some_holes_3 = vm_behavior_can_reuse_no_cow_rw_with_holes,
		.some_holes_4 = vm_behavior_can_reuse_no_cow_rw_with_holes,
		.some_holes_5 = vm_behavior_can_reuse_no_cow_rw_with_holes,
		.some_holes_6 = vm_behavior_can_reuse_no_cow_rw_with_holes,
		.some_holes_7 = vm_behavior_can_reuse_no_cow_rw_with_holes,
		.some_holes_8 = vm_behavior_can_reuse_no_cow_rw_with_holes,
		.some_holes_9 = vm_behavior_can_reuse_no_cow_rw_with_holes,
		.some_holes_10 = vm_behavior_can_reuse_no_cow_rw_with_holes,
		.some_holes_11 = vm_behavior_can_reuse_no_cow_rw_with_holes,
		.some_holes_12 = vm_behavior_can_reuse_no_cow_rw_with_holes,

		.all_holes_1 = vm_behavior_can_reuse_no_cow_rw_with_holes,
		.all_holes_2 = vm_behavior_can_reuse_no_cow_rw_with_holes,
		.all_holes_3 = vm_behavior_can_reuse_no_cow_rw_with_holes,
		.all_holes_4 = vm_behavior_can_reuse_no_cow_rw_with_holes,

		.null_entry        = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.nonresident_entry = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.resident_entry    = vm_behavior_can_reuse_no_cow_rw_no_holes,

		.shared_entry               = test_is_unimplemented,
		.shared_entry_discontiguous = test_is_unimplemented,
		.shared_entry_partial       = test_is_unimplemented,
		.shared_entry_pairs         = test_is_unimplemented,
		.shared_entry_x1000         = test_is_unimplemented,

		.cow_entry = test_is_unimplemented,
		.cow_unreferenced = test_is_unimplemented,
		.cow_nocow = test_is_unimplemented,
		.nocow_cow = test_is_unimplemented,
		.cow_unreadable = test_is_unimplemented,
		.cow_unwriteable = test_is_unimplemented,

		.permanent_entry = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.permanent_before_permanent = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.permanent_before_allocation = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.permanent_before_allocation_2 = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.permanent_before_hole = vm_behavior_can_reuse_no_cow_rw_with_holes,
		.permanent_after_allocation = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.permanent_after_hole = vm_behavior_can_reuse_no_cow_rw_with_holes,

		.single_submap_single_entry = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.single_submap_single_entry_first_pages = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.single_submap_single_entry_last_pages = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.single_submap_single_entry_middle_pages = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.single_submap_oversize_entry_at_start = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.single_submap_oversize_entry_at_end = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.single_submap_oversize_entry_at_both = vm_behavior_can_reuse_no_cow_rw_no_holes,

		.submap_before_allocation = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.submap_after_allocation = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.submap_before_hole = vm_behavior_can_reuse_no_cow_rw_with_holes,
		.submap_after_hole = vm_behavior_can_reuse_no_cow_rw_with_holes,
		.submap_allocation_submap_one_entry = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.submap_allocation_submap_two_entries = vm_behavior_can_reuse_no_cow_rw_no_holes,
		.submap_allocation_submap_three_entries = vm_behavior_can_reuse_no_cow_rw_no_holes,

		.submap_before_allocation_ro = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.submap_after_allocation_ro = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.submap_before_hole_ro = vm_behavior_can_reuse_no_cow_ro_with_holes,
		.submap_after_hole_ro = vm_behavior_can_reuse_no_cow_ro_with_holes,
		.submap_allocation_submap_one_entry_ro = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.submap_allocation_submap_two_entries_ro = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.submap_allocation_submap_three_entries_ro = vm_behavior_can_reuse_no_cow_ro_no_holes,

		.protection_single_000_000 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_single_000_r00 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_single_r00_r00 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_single_000_0w0 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_single_0w0_0w0 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_single_000_rw0 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_single_r00_rw0 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_single_0w0_rw0 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_single_rw0_rw0 = vm_behavior_can_reuse_no_cow_rw_no_holes,

		.protection_pairs_000_000 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_pairs_000_r00 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_pairs_000_0w0 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_pairs_000_rw0 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_pairs_r00_000 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_pairs_r00_r00 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_pairs_r00_0w0 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_pairs_r00_rw0 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_pairs_0w0_000 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_pairs_0w0_r00 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_pairs_0w0_0w0 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_pairs_0w0_rw0 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_pairs_rw0_000 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_pairs_rw0_r00 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_pairs_rw0_0w0 = vm_behavior_can_reuse_no_cow_ro_no_holes,
		.protection_pairs_rw0_rw0 = vm_behavior_can_reuse_no_cow_rw_no_holes,
	};

	run_vm_tests("vm_behavior_set_can_reuse", __FILE__, &tests, argc, argv);
}


T_DECL(vm_behavior_set_zero,
    "run vm_behavior_set(ZERO) with various vm configurations")
{
	vm_tests_t tests = {
		.single_entry_1 = vm_behavior_zero,
		.single_entry_2 = vm_behavior_zero,
		.single_entry_3 = vm_behavior_zero,
		.single_entry_4 = vm_behavior_zero,

		.multiple_entries_1 = vm_behavior_zero,
		.multiple_entries_2 = vm_behavior_zero,
		.multiple_entries_3 = vm_behavior_zero,
		.multiple_entries_4 = vm_behavior_zero,
		.multiple_entries_5 = vm_behavior_zero,
		.multiple_entries_6 = vm_behavior_zero,

		.some_holes_1 = vm_behavior_zero,
		.some_holes_2 = vm_behavior_zero,
		.some_holes_3 = vm_behavior_zero,
		.some_holes_4 = vm_behavior_zero,
		.some_holes_5 = vm_behavior_zero,
		.some_holes_6 = vm_behavior_zero,
		.some_holes_7 = vm_behavior_zero,
		.some_holes_8 = vm_behavior_zero,
		.some_holes_9 = vm_behavior_zero,
		.some_holes_10 = vm_behavior_zero,
		.some_holes_11 = vm_behavior_zero,
		.some_holes_12 = vm_behavior_zero,

		.all_holes_1 = vm_behavior_zero,
		.all_holes_2 = vm_behavior_zero,
		.all_holes_3 = vm_behavior_zero,
		.all_holes_4 = vm_behavior_zero,

		.null_entry        = vm_behavior_zero,
		.nonresident_entry = vm_behavior_zero,
		.resident_entry    = vm_behavior_zero,

		.shared_entry               = test_is_unimplemented,
		.shared_entry_discontiguous = test_is_unimplemented,
		.shared_entry_partial       = test_is_unimplemented,
		.shared_entry_pairs         = test_is_unimplemented,
		.shared_entry_x1000         = test_is_unimplemented,

		.cow_entry = test_is_unimplemented,
		.cow_unreferenced = test_is_unimplemented,
		.cow_nocow = test_is_unimplemented,
		.nocow_cow = test_is_unimplemented,
		.cow_unreadable = test_is_unimplemented,
		.cow_unwriteable = test_is_unimplemented,

		.permanent_entry = vm_behavior_zero,
		.permanent_before_permanent = vm_behavior_zero,
		.permanent_before_allocation = vm_behavior_zero,
		.permanent_before_allocation_2 = vm_behavior_zero,
		.permanent_before_hole = vm_behavior_zero,
		.permanent_after_allocation = vm_behavior_zero,
		.permanent_after_hole = vm_behavior_zero,

		.single_submap_single_entry = vm_behavior_zero,
		.single_submap_single_entry_first_pages = vm_behavior_zero,
		.single_submap_single_entry_last_pages = vm_behavior_zero,
		.single_submap_single_entry_middle_pages = vm_behavior_zero,
		.single_submap_oversize_entry_at_start = vm_behavior_zero,
		.single_submap_oversize_entry_at_end = vm_behavior_zero,
		.single_submap_oversize_entry_at_both = vm_behavior_zero,

		.submap_before_allocation = vm_behavior_zero,
		.submap_after_allocation = vm_behavior_zero,
		.submap_before_hole = vm_behavior_zero,
		.submap_after_hole = vm_behavior_zero,
		.submap_allocation_submap_one_entry = vm_behavior_zero,
		.submap_allocation_submap_two_entries = vm_behavior_zero,
		.submap_allocation_submap_three_entries = vm_behavior_zero,

		.submap_before_allocation_ro = vm_behavior_zero,
		.submap_after_allocation_ro = vm_behavior_zero,
		.submap_before_hole_ro = vm_behavior_zero,
		.submap_after_hole_ro = vm_behavior_zero,
		.submap_allocation_submap_one_entry_ro = vm_behavior_zero,
		.submap_allocation_submap_two_entries_ro = vm_behavior_zero,
		.submap_allocation_submap_three_entries_ro = vm_behavior_zero,

		.protection_single_000_000 = vm_behavior_zero,
		.protection_single_000_r00 = vm_behavior_zero,
		.protection_single_r00_r00 = vm_behavior_zero,
		.protection_single_000_0w0 = vm_behavior_zero,
		.protection_single_0w0_0w0 = vm_behavior_zero,
		.protection_single_000_rw0 = vm_behavior_zero,
		.protection_single_r00_rw0 = vm_behavior_zero,
		.protection_single_0w0_rw0 = vm_behavior_zero,
		.protection_single_rw0_rw0 = vm_behavior_zero,

		.protection_pairs_000_000 = vm_behavior_zero,
		.protection_pairs_000_r00 = vm_behavior_zero,
		.protection_pairs_000_0w0 = vm_behavior_zero,
		.protection_pairs_000_rw0 = vm_behavior_zero,
		.protection_pairs_r00_000 = vm_behavior_zero,
		.protection_pairs_r00_r00 = vm_behavior_zero,
		.protection_pairs_r00_0w0 = vm_behavior_zero,
		.protection_pairs_r00_rw0 = vm_behavior_zero,
		.protection_pairs_0w0_000 = vm_behavior_zero,
		.protection_pairs_0w0_r00 = vm_behavior_zero,
		.protection_pairs_0w0_0w0 = vm_behavior_zero,
		.protection_pairs_0w0_rw0 = vm_behavior_zero,
		.protection_pairs_rw0_000 = vm_behavior_zero,
		.protection_pairs_rw0_r00 = vm_behavior_zero,
		.protection_pairs_rw0_0w0 = vm_behavior_zero,
		.protection_pairs_rw0_rw0 = vm_behavior_zero,
	};

	run_vm_tests("vm_behavior_set_zero", __FILE__, &tests, argc, argv);
}