/* * 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_test.c * * Test vm_configurator itself. * * Verify that the VM states generated by vm_configurator are correct. * This is intended to catch bugs in vm_configurator's * template and checker system, as well as bugs in individual * configurations used by other tests. */ #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) ); /* * Return true if [start, start + size) is a VM entry at the given submap depth. */ __attribute__((overloadable)) static bool is_entry(mach_vm_address_t start, mach_vm_size_t size, uint32_t submap_depth) { mach_vm_address_t entry_start = start; mach_vm_size_t entry_size; vm_region_submap_info_data_64_t info; if (!get_info_for_address(&entry_start, &entry_size, &info, submap_depth)) { return false; /* not mapped */ } if (entry_start != start || entry_size != size) { return false; /* mapped, but wrong extent */ } return true; } /* * Return true if [start, start + size) is a VM entry at submap depth 0. */ __attribute__((overloadable, used)) static bool is_entry(mach_vm_address_t start, mach_vm_size_t size) { return is_entry(start, size, 0); } /* * Return true if [start, start + size) is an unallocated hole * at the given submap depth. */ __attribute__((overloadable)) static bool is_hole(mach_vm_address_t start, mach_vm_size_t size, uint32_t submap_depth) { mach_vm_address_t entry_start = start; mach_vm_size_t entry_size; vm_region_submap_info_data_64_t info; if (get_info_for_address(&entry_start, &entry_size, &info, submap_depth)) { /* start address was mapped */ return false; } else if (entry_start < start + size) { /* some address before the end of the expected hole was mapped */ return false; } /* [start, start + size) was entirely unmapped */ return true; } /* * Return true if [start, start + size) is an unallocated hole at submap depth 0. * Unallocate space inside a submap does not count. */ __attribute__((overloadable, used)) static bool is_hole(mach_vm_address_t start, mach_vm_size_t size) { return is_hole(start, size, 0 /* submap_depth */); } /* * Verify the memory and the checker for an expected hole. */ static void assert_hole_checker_and_entry( vm_entry_checker_t *checker, mach_vm_address_t start, mach_vm_size_t size) { assert(start % PAGE_SIZE == 0); assert(size % PAGE_SIZE == 0); assert(checker->kind == Hole); assert(checker->address == start); assert(checker->size == size); assert(is_hole(start, size, checker->submap_depth)); } /* * Verify the checker for an expected allocated entry. * Does not verify the actual VM state. */ static void assert_allocation_checker( vm_entry_checker_t *checker, mach_vm_address_t start, mach_vm_size_t size) { assert(start % PAGE_SIZE == 0); assert(size % PAGE_SIZE == 0); assert(checker->kind == Allocation); assert(checker->address == start); assert(checker->size == size); } /* * Verify the actual VM state for an expected allocated entry. * Does not verify the matching checker. * Returns the vm_region output for the memory. */ static void assert_allocation_entry( mach_vm_address_t start, mach_vm_size_t size, uint32_t submap_depth, vm_region_submap_info_data_64_t * const out_info) { mach_vm_address_t entry_start = start; mach_vm_size_t entry_size; assert(get_info_for_address(&entry_start, &entry_size, out_info, submap_depth)); assert(entry_start == start); assert(entry_size == size); } /* * Verify the memory and the checker for an expected allocated entry. * Returns the vm_region output for the memory. */ static void assert_allocation_checker_and_entry( vm_entry_checker_t *checker, mach_vm_address_t start, mach_vm_size_t size, vm_region_submap_info_data_64_t * const out_info) { assert_allocation_checker(checker, start, size); assert_allocation_entry(start, size, checker->submap_depth, out_info); } /* * Verify that checker_list consists of a pattern of entries and holes * Each allocation or hole is assumed to be DEFAULT_ENTRY_SIZE in length. * "##.#..#": allocation, allocation, hole, allocation, hole, hole, allocation */ static void assert_allocation_and_hole_pattern( checker_list_t *checker_list, const char *pattern) { mach_vm_address_t base = checker_range_start_address(checker_list->entries); assert(checker_range_count(checker_list->entries) == strlen(pattern)); for (size_t i = 0; i < strlen(pattern); i++) { vm_region_submap_info_data_64_t info; mach_vm_address_t entry_address = base + i * DEFAULT_ENTRY_SIZE; vm_entry_checker_t *checker = checker_list_nth(checker_list, i); switch (pattern[i]) { case '#': assert_allocation_checker_and_entry(checker, entry_address, DEFAULT_ENTRY_SIZE, &info); break; case '.': assert_hole_checker_and_entry(checker, entry_address, DEFAULT_ENTRY_SIZE); break; default: T_ASSERT_FAIL("pattern character '%c' is neither '#' nor '.'", pattern[i]); break; } } } static void assert_checker_and_entry_protection_equals( vm_entry_checker_t *checker, vm_region_submap_info_data_64_t *info, int protection, int max_protection) { assert(checker->protection == protection); assert(checker->max_protection == max_protection); assert(info->protection == protection); assert(info->max_protection == max_protection); } /* * Verify the memory and the checker for an expected permanent entry. * This is destructive because it attempts to deallocate the permanent entry * which makes its memory inaccessible, and updates the checker to match. */ static void destructively_assert_permanent_checker_and_entry( vm_entry_checker_t *checker, mach_vm_address_t start, mach_vm_size_t size) { assert(start % PAGE_SIZE == 0); assert(size % PAGE_SIZE == 0); assert(checker->permanent == true); assert(checker->address == start); assert(checker->size == size); /* * Permanent memory is indistinguishable in vm_region output. * We can only try to deallocate it and then see if it is still there. */ /* check that it exists */ assert(is_entry(start, size, checker->submap_depth)); /* try to deallocate it */ kern_return_t kr = mach_vm_deallocate(mach_task_self(), start, size); assert(kr == 0); /* check that it still exists */ assert(is_entry(start, size, checker->submap_depth)); /* update the checker because the memory is now inaccessible */ checker->protection = VM_PROT_NONE; checker->max_protection = VM_PROT_NONE; } /* * Verify the memory and checker for an expected non-permanent allocation. * This is destructive because it deallocates the memory * and updates the checker to match. */ static void destructively_assert_nonpermanent_checker_and_entry( checker_list_t *list, vm_entry_checker_t *checker, mach_vm_address_t start, mach_vm_size_t size) { assert(start % PAGE_SIZE == 0); assert(size % PAGE_SIZE == 0); assert(checker->permanent == false); assert(checker->address == start); assert(checker->size == size); /* * Permanent memory is indistinguishable in vm_region output. * We can only try to deallocate it and then see if it is still there. */ /* check that it exists */ assert(is_entry(start, size, checker->submap_depth)); /* try to deallocate it */ kern_return_t kr = mach_vm_deallocate(mach_task_self(), start, size); assert(kr == 0); /* check that it no longer exists */ assert(!is_entry(start, size, checker->submap_depth)); assert(is_hole(start, size, 0 /* submap_depth */)); /* * Update the checker to match the now-deallocated memory. * The checker should be replaced by a hole checker. * * Save the checker's index first so we can * look up and verify the hole checker after. */ unsigned index = 0; while (checker_list_nth(list, index) != checker) { index++; } checker_list_free_checker(list, checker); vm_entry_checker_t *new_hole = checker_list_nth(list, index); assert_hole_checker_and_entry(new_hole, start, size); } /* * Verify the memory and the checker for an expected submap entry. * Does not examine the contents of the submap. * Returns the vm_region output for the entry in the parent map. */ static void assert_submap_checker_and_entry( vm_entry_checker_t *checker, mach_vm_address_t start, mach_vm_size_t size, vm_region_submap_info_data_64_t * const out_info) { assert(start % PAGE_SIZE == 0); assert(size % PAGE_SIZE == 0); assert(checker->kind == Submap); assert(checker->address == start); assert(checker->size == size); assert(checker->submap_depth == 0); /* nested submaps not allowed */ mach_vm_address_t entry_start = start; mach_vm_size_t entry_size; assert(get_info_for_address(&entry_start, &entry_size, out_info, checker->submap_depth)); assert(entry_start == start); assert(entry_size == size); } static test_result_t test_single_entry_1( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { /* test range is a single allocation */ vm_entry_checker_t *checker = checker_list_nth(checker_list, 0); vm_region_submap_info_data_64_t info; assert_allocation_checker_and_entry(checker, start, size, &info); return TestSucceeded; } static test_result_t test_single_entry_2( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { vm_entry_checker_t *checker = checker_list_nth(checker_list, 0); vm_region_submap_info_data_64_t info; /* test range excludes the end of the allocation */ assert(size == DEFAULT_ENTRY_SIZE - DEFAULT_PARTIAL_ENTRY_SIZE); assert_allocation_checker_and_entry(checker, start, DEFAULT_ENTRY_SIZE, &info); return TestSucceeded; } static test_result_t test_single_entry_3( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { vm_entry_checker_t *checker = checker_list_nth(checker_list, 0); vm_region_submap_info_data_64_t info; /* test range excludes the start of the allocation */ assert(size == DEFAULT_ENTRY_SIZE - DEFAULT_PARTIAL_ENTRY_SIZE); assert_allocation_checker_and_entry(checker, start - DEFAULT_PARTIAL_ENTRY_SIZE, DEFAULT_ENTRY_SIZE, &info); return TestSucceeded; } static test_result_t test_single_entry_4( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { vm_entry_checker_t *checker = checker_list_nth(checker_list, 0); vm_region_submap_info_data_64_t info; /* test range excludes the start and end of the allocation */ assert(size == DEFAULT_ENTRY_SIZE - DEFAULT_PARTIAL_ENTRY_SIZE); assert_allocation_checker_and_entry(checker, start - DEFAULT_PARTIAL_ENTRY_SIZE / 2, DEFAULT_ENTRY_SIZE, &info); return TestSucceeded; } static test_result_t test_multiple_entries_1( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_allocation_and_hole_pattern(checker_list, "##"); assert(start == checker_range_start_address(checker_list->entries)); assert(size == DEFAULT_ENTRY_SIZE * 2); return TestSucceeded; } static test_result_t test_multiple_entries_2( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_allocation_and_hole_pattern(checker_list, "###"); assert(start == checker_range_start_address(checker_list->entries)); assert(size == DEFAULT_ENTRY_SIZE * 3); return TestSucceeded; } static test_result_t test_multiple_entries_3( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_allocation_and_hole_pattern(checker_list, "############" "############" "############" "############"); assert(start == checker_range_start_address(checker_list->entries)); assert(size == DEFAULT_ENTRY_SIZE * 4 * 12); return TestSucceeded; } static test_result_t test_multiple_entries_4( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_allocation_and_hole_pattern(checker_list, "###"); assert(start == checker_range_start_address(checker_list->entries)); assert(size == DEFAULT_ENTRY_SIZE * 3 - DEFAULT_PARTIAL_ENTRY_SIZE); return TestSucceeded; } static test_result_t test_multiple_entries_5( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_allocation_and_hole_pattern(checker_list, "###"); assert(start == checker_range_start_address(checker_list->entries) + DEFAULT_PARTIAL_ENTRY_SIZE); assert(size == DEFAULT_ENTRY_SIZE * 3 - DEFAULT_PARTIAL_ENTRY_SIZE); return TestSucceeded; } static test_result_t test_multiple_entries_6( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_allocation_and_hole_pattern(checker_list, "###"); assert(start == checker_range_start_address(checker_list->entries) + DEFAULT_PARTIAL_ENTRY_SIZE / 2); assert(size == DEFAULT_ENTRY_SIZE * 3 - DEFAULT_PARTIAL_ENTRY_SIZE); return TestSucceeded; } static test_result_t test_some_holes_1( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_allocation_and_hole_pattern(checker_list, ".#"); assert(start == checker_range_start_address(checker_list->entries)); assert(size == DEFAULT_ENTRY_SIZE * 2); return TestSucceeded; } static test_result_t test_some_holes_2( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_allocation_and_hole_pattern(checker_list, ".###"); assert(start == checker_range_start_address(checker_list->entries)); assert(size == DEFAULT_ENTRY_SIZE * 4); return TestSucceeded; } static test_result_t test_some_holes_3( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_allocation_and_hole_pattern(checker_list, ".#"); assert(start == checker_range_start_address(checker_list->entries)); assert(size == DEFAULT_ENTRY_SIZE * 2 - DEFAULT_PARTIAL_ENTRY_SIZE); return TestSucceeded; } static test_result_t test_some_holes_4( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_allocation_and_hole_pattern(checker_list, ".###"); assert(start == checker_range_start_address(checker_list->entries)); assert(size == DEFAULT_ENTRY_SIZE * 4 - DEFAULT_PARTIAL_ENTRY_SIZE); return TestSucceeded; } static test_result_t test_some_holes_5( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_allocation_and_hole_pattern(checker_list, "#."); assert(start == checker_range_start_address(checker_list->entries)); assert(size == DEFAULT_ENTRY_SIZE * 2); return TestSucceeded; } static test_result_t test_some_holes_6( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_allocation_and_hole_pattern(checker_list, "###."); assert(start == checker_range_start_address(checker_list->entries)); assert(size == DEFAULT_ENTRY_SIZE * 4); return TestSucceeded; } static test_result_t test_some_holes_7( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_allocation_and_hole_pattern(checker_list, "#."); assert(start == checker_range_start_address(checker_list->entries) + DEFAULT_PARTIAL_ENTRY_SIZE); assert(size == DEFAULT_ENTRY_SIZE * 2 - DEFAULT_PARTIAL_ENTRY_SIZE); return TestSucceeded; } static test_result_t test_some_holes_8( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_allocation_and_hole_pattern(checker_list, "###."); assert(start == checker_range_start_address(checker_list->entries) + DEFAULT_PARTIAL_ENTRY_SIZE); assert(size == DEFAULT_ENTRY_SIZE * 4 - DEFAULT_PARTIAL_ENTRY_SIZE); return TestSucceeded; } static test_result_t test_some_holes_9( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_allocation_and_hole_pattern(checker_list, "#.#"); assert(start == checker_range_start_address(checker_list->entries)); assert(size == DEFAULT_ENTRY_SIZE * 3); return TestSucceeded; } static test_result_t test_some_holes_10( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_allocation_and_hole_pattern(checker_list, "#.#.#"); assert(start == checker_range_start_address(checker_list->entries)); assert(size == DEFAULT_ENTRY_SIZE * 5); return TestSucceeded; } static test_result_t test_some_holes_11( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_allocation_and_hole_pattern(checker_list, "##.##.##"); assert(start == checker_range_start_address(checker_list->entries)); assert(size == DEFAULT_ENTRY_SIZE * 8); return TestSucceeded; } static test_result_t test_some_holes_12( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_allocation_and_hole_pattern(checker_list, "###.###.###"); assert(start == checker_range_start_address(checker_list->entries)); assert(size == DEFAULT_ENTRY_SIZE * 11); return TestSucceeded; } static test_result_t test_all_holes_1( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_allocation_and_hole_pattern(checker_list, "#.#"); assert(start == checker_range_start_address(checker_list->entries) + DEFAULT_ENTRY_SIZE); assert(size == DEFAULT_ENTRY_SIZE); return TestSucceeded; } static test_result_t test_all_holes_2( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_allocation_and_hole_pattern(checker_list, "#."); assert(start == checker_range_start_address(checker_list->entries) + DEFAULT_ENTRY_SIZE); assert(size == DEFAULT_PARTIAL_ENTRY_SIZE); return TestSucceeded; } static test_result_t test_all_holes_3( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_allocation_and_hole_pattern(checker_list, ".#"); assert(start == checker_range_start_address(checker_list->entries) + DEFAULT_PARTIAL_ENTRY_SIZE); assert(size == DEFAULT_PARTIAL_ENTRY_SIZE); return TestSucceeded; } static test_result_t test_all_holes_4( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_allocation_and_hole_pattern(checker_list, "."); assert(start == checker_range_start_address(checker_list->entries) + DEFAULT_PARTIAL_ENTRY_SIZE / 2); assert(size == DEFAULT_PARTIAL_ENTRY_SIZE); return TestSucceeded; } static test_result_t test_null_entry( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { vm_entry_checker_t *checker = checker_list_nth(checker_list, 0); vm_region_submap_info_data_64_t info; assert_allocation_checker_and_entry(checker, start, size, &info); /* entry's object is null */ assert(info.object_id_full == 0); assert(checker->object->object_id == 0); return TestSucceeded; } static test_result_t test_nonresident_entry( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { vm_entry_checker_t *checker = checker_list_nth(checker_list, 0); vm_region_submap_info_data_64_t info; assert_allocation_checker_and_entry(checker, start, size, &info); /* entry has an object, but its pages are not resident */ assert(info.object_id_full != 0); assert(info.pages_resident == 0); assert(checker->object->object_id != 0); assert(checker->pages_resident == 0); return TestSucceeded; } static test_result_t test_resident_entry( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { vm_entry_checker_t *checker = checker_list_nth(checker_list, 0); vm_region_submap_info_data_64_t info; assert_allocation_checker_and_entry(checker, start, size, &info); /* entry has an object and its pages are resident */ assert(info.pages_resident == size / PAGE_SIZE); assert(checker->pages_resident == size / PAGE_SIZE); assert(checker->object->object_id != 0); return TestSucceeded; } /* common code for two-shared-entry tests */ static void test_one_shared_pair( checker_list_t *checker_list, mach_vm_address_t left_entry_start, mach_vm_address_t right_entry_start, mach_vm_size_t size, mach_vm_address_t right_object_offset) { /* * Two entries, both have the same object with refcount two. * Right entry's object offset varies. */ vm_entry_checker_t *left_checker = checker_list_find_allocation(checker_list, left_entry_start); vm_entry_checker_t *right_checker = checker_list_find_allocation(checker_list, right_entry_start); assert(left_checker); assert(right_checker); vm_region_submap_info_data_64_t left_info, right_info; assert_allocation_checker_and_entry(left_checker, left_entry_start, size, &left_info); assert_allocation_checker_and_entry(right_checker, right_entry_start, size, &right_info); assert(left_info.object_id_full != 0); assert(left_info.object_id_full == right_info.object_id_full); assert(left_info.ref_count == 2); assert(right_info.ref_count == 2); assert(left_info.share_mode == SM_TRUESHARED); assert(right_info.share_mode == SM_TRUESHARED); assert(left_info.offset == 0); assert(right_info.offset == right_object_offset); assert(left_info.user_tag != right_info.user_tag); assert(left_checker->object == right_checker->object); /* checker doesn't distinguish SM_SHARED from SM_TRUESHARED */ assert(checker_share_mode(left_checker) == SM_SHARED); assert(checker_share_mode(right_checker) == SM_SHARED); assert(left_checker->object_offset == 0); assert(right_checker->object_offset == right_object_offset); assert(left_checker->user_tag != right_checker->user_tag); } static test_result_t test_shared_entry( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { /* entries are both at object offset 0 */ test_one_shared_pair(checker_list, start, start + size, size, 0); return TestSucceeded; } static test_result_t test_shared_entry_discontiguous( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { /* * right entry's object offset begins * after the left entry's range ends */ test_one_shared_pair(checker_list, start, start + size, size, DEFAULT_ENTRY_SIZE); return TestSucceeded; } static test_result_t test_shared_entry_partial( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { /* * right entry's object offset begins * inside the left entry's range */ test_one_shared_pair(checker_list, start, start + size, size, DEFAULT_PARTIAL_ENTRY_SIZE); return TestSucceeded; } static test_result_t test_shared_entry_pairs( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { /* * two shared pairs */ mach_vm_size_t entry_size = size / 4; mach_vm_address_t one = start; mach_vm_address_t two = one + entry_size; mach_vm_address_t three = two + entry_size; mach_vm_address_t four = three + entry_size; test_one_shared_pair(checker_list, one, four, entry_size, 0); test_one_shared_pair(checker_list, two, three, entry_size, 0); return TestSucceeded; } static test_result_t test_shared_entry_x1000( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { /* many entries, all of which share the same object */ entry_checker_range_t limit = checker_list_find_range(checker_list, start, size); assert(checker_range_count(limit) == 1000); uint64_t shared_object_id = 0; FOREACH_CHECKER(checker, limit) { assert(checker->object); assert(checker->object->object_id_mode == object_has_known_id); if (!shared_object_id) { assert(checker->object->object_id != 0); shared_object_id = checker->object->object_id; } assert(checker->object->object_id == shared_object_id); assert(get_object_id_for_address(checker->address) == shared_object_id); } return TestSucceeded; } /* common code for two-shared-entry tests */ static test_result_t test_cow_entry( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { /* * two entries, both have the same object and its refcount is two. * [start, start + size) is only the first entry */ vm_entry_checker_t *left_checker = checker_list_nth(checker_list, 0); vm_region_submap_info_data_64_t left_info; assert_allocation_checker_and_entry(left_checker, start, size, &left_info); vm_entry_checker_t *right_checker = checker_list_nth(checker_list, 1); vm_region_submap_info_data_64_t right_info; assert_allocation_checker_and_entry(right_checker, start + size, size, &right_info); assert(left_info.object_id_full != 0); assert(left_info.object_id_full == right_info.object_id_full); assert(left_info.ref_count == 2); assert(right_info.ref_count == 2); assert(left_info.share_mode == SM_COW); assert(right_info.share_mode == SM_COW); assert(left_info.offset == 0); assert(right_info.offset == 0); assert(left_checker->object == right_checker->object); assert(checker_share_mode(left_checker) == SM_COW); assert(checker_share_mode(right_checker) == SM_COW); assert(left_checker->object_offset == 0); assert(right_checker->object_offset == 0); return TestSucceeded; } static test_result_t test_cow_unreferenced( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { /* * one COW entry with refcount 1 */ vm_entry_checker_t *checker = checker_list_nth(checker_list, 0); vm_region_submap_info_data_64_t info; assert_allocation_checker_and_entry(checker, start, size, &info); assert(info.share_mode == SM_COW); assert(info.object_id_full != 0); assert(info.ref_count == 1); assert(info.offset == 0); assert(checker->object_offset == 0); assert(checker_share_mode(checker) == SM_COW); assert(checker->object); assert(checker->object->self_ref_count == 1); return TestSucceeded; } /* common checks for cow_nocow and nocow_cow */ static test_result_t test_cow_nocow_common( vm_entry_checker_t *cow_checker, vm_entry_checker_t *plain_checker, mach_vm_address_t cow_start_address, mach_vm_address_t plain_start_address, mach_vm_size_t entry_size) { /* two entries: one is COW, one is not COW */ vm_region_submap_info_data_64_t cow_info, plain_info; assert_allocation_checker_and_entry(cow_checker, cow_start_address, entry_size, &cow_info); assert_allocation_checker_and_entry(plain_checker, plain_start_address, entry_size, &plain_info); assert(cow_info.share_mode == SM_COW); assert(plain_info.share_mode == SM_PRIVATE); assert(cow_info.object_id_full != 0); assert(cow_info.object_id_full != plain_info.object_id_full); assert(cow_info.ref_count == 2); assert(cow_info.offset == 0); assert(checker_share_mode(cow_checker) == SM_COW); assert(checker_share_mode(plain_checker) == SM_PRIVATE); assert(cow_checker->object != NULL); assert(cow_checker->object != plain_checker->object); assert(cow_checker->object_offset == 0); return TestSucceeded; } static test_result_t test_cow_nocow( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { /* * two entries: first is COW, second is not */ vm_entry_checker_t *cow_checker = checker_list_nth(checker_list, 0); vm_entry_checker_t *plain_checker = checker_list_nth(checker_list, 1); assert(size % 2 == 0); mach_vm_address_t cow_start = start; mach_vm_address_t plain_start = start + size / 2; return test_cow_nocow_common(cow_checker, plain_checker, cow_start, plain_start, size / 2); } static test_result_t test_nocow_cow( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { /* * two entries: first is not COW, second is COW */ vm_entry_checker_t *plain_checker = checker_list_nth(checker_list, 0); vm_entry_checker_t *cow_checker = checker_list_nth(checker_list, 1); assert(size % 2 == 0); mach_vm_address_t plain_start = start; mach_vm_address_t cow_start = start + size / 2; return test_cow_nocow_common(cow_checker, plain_checker, cow_start, plain_start, size / 2); } static test_result_t test_cow_unreadable( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { /* * COW entry, unreadable */ vm_entry_checker_t *checker = checker_list_nth(checker_list, 0); vm_region_submap_info_data_64_t info; assert_allocation_checker_and_entry(checker, start, size, &info); assert(info.share_mode == SM_COW); assert(info.protection == VM_PROT_NONE); assert(info.ref_count == 2); assert(checker_share_mode(checker) == SM_COW); assert(checker->protection == VM_PROT_NONE); assert(checker->object != NULL); assert(checker->object->self_ref_count == 2); return TestSucceeded; } static test_result_t test_cow_unwriteable( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { /* * COW entry, readable but unwriteable */ vm_entry_checker_t *checker = checker_list_nth(checker_list, 0); vm_region_submap_info_data_64_t info; assert_allocation_checker_and_entry(checker, start, size, &info); assert(info.share_mode == SM_COW); assert(info.protection == VM_PROT_READ); assert(info.ref_count == 2); assert(checker_share_mode(checker) == SM_COW); assert(checker->protection == VM_PROT_READ); assert(checker->object != NULL); assert(checker->object->self_ref_count == 2); return TestSucceeded; } static test_result_t test_permanent_entry( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { destructively_assert_permanent_checker_and_entry( checker_list_nth(checker_list, 0), start, size); return TestSucceeded; } static test_result_t test_permanent_before_permanent( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { destructively_assert_permanent_checker_and_entry( checker_list_nth(checker_list, 0), start, size / 2); destructively_assert_permanent_checker_and_entry( checker_list_nth(checker_list, 1), start + size / 2, size / 2); return TestSucceeded; } static test_result_t test_permanent_before_allocation( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { destructively_assert_permanent_checker_and_entry( checker_list_nth(checker_list, 0), start, size / 2); destructively_assert_nonpermanent_checker_and_entry( checker_list, checker_list_nth(checker_list, 1), start + size / 2, size / 2); assert_hole_checker_and_entry( checker_list_nth(checker_list, 2), start + size, DEFAULT_ENTRY_SIZE); return TestSucceeded; } static test_result_t test_permanent_before_allocation_2( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { vm_region_submap_info_data_64_t info; destructively_assert_permanent_checker_and_entry( checker_list_nth(checker_list, 0), start, size / 2); destructively_assert_nonpermanent_checker_and_entry( checker_list, checker_list_nth(checker_list, 1), start + size / 2, size / 2); assert_allocation_checker_and_entry( checker_list_nth(checker_list, 2), start + size, DEFAULT_ENTRY_SIZE, &info); return TestSucceeded; } static test_result_t test_permanent_before_hole( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { destructively_assert_permanent_checker_and_entry( checker_list_nth(checker_list, 0), start, size / 2); assert_hole_checker_and_entry( checker_list_nth(checker_list, 1), start + size / 2, size / 2); return TestSucceeded; } static test_result_t test_permanent_after_allocation( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { destructively_assert_nonpermanent_checker_and_entry( checker_list, checker_list_nth(checker_list, 0), start, size / 2); destructively_assert_permanent_checker_and_entry( checker_list_nth(checker_list, 1), start + size / 2, size / 2); return TestSucceeded; } static test_result_t test_permanent_after_hole( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { assert_hole_checker_and_entry( checker_list_nth(checker_list, 0), start, size / 2); destructively_assert_permanent_checker_and_entry( checker_list_nth(checker_list, 1), start + size / 2, size / 2); return TestSucceeded; } static test_result_t test_single_submap_single_entry_common( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { vm_region_submap_info_data_64_t info; vm_entry_checker_t *submap_parent = checker_list_nth(checker_list, 0); assert_submap_checker_and_entry(submap_parent, start, size, &info); checker_list_t *submap_checkers DEFER_UNSLIDE = checker_get_and_slide_submap_checkers(submap_parent); vm_entry_checker_t *submap_content = checker_list_nth(submap_checkers, 0); assert_allocation_checker_and_entry(submap_content, start, size, &info); return TestSucceeded; } static test_result_t test_single_submap_single_entry( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { return test_single_submap_single_entry_common( checker_list, start, size); } static test_result_t test_single_submap_single_entry_first_pages( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { /* tested range excludes the last 1/2 of the real entry */ mach_vm_size_t submap_size = size * 2; mach_vm_address_t submap_start = start; return test_single_submap_single_entry_common( checker_list, submap_start, submap_size); } static test_result_t test_single_submap_single_entry_last_pages( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { /* tested range excludes the first 1/2 of the real entry */ mach_vm_size_t submap_size = size * 2; mach_vm_address_t submap_start = start - submap_size / 2; return test_single_submap_single_entry_common( checker_list, submap_start, submap_size); } static test_result_t test_single_submap_single_entry_middle_pages( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { /* tested range excludes the first 1/4 and last 1/4 of the real entry */ mach_vm_size_t submap_size = size * 2; mach_vm_address_t submap_start = start - submap_size / 4; return test_single_submap_single_entry_common( checker_list, submap_start, submap_size); } static test_result_t test_single_submap_oversize_entry_common( checker_list_t *checker_list, mach_vm_address_t parent_start, mach_vm_size_t parent_size, mach_vm_address_t parent_offset, mach_vm_size_t submap_size) { vm_region_submap_info_data_64_t parent_info, content_info; vm_entry_checker_t *submap_parent = checker_list_nth(checker_list, 0); assert_submap_checker_and_entry(submap_parent, parent_start, parent_size, &parent_info); assert(submap_parent->object_offset == parent_offset); assert(parent_info.offset == parent_offset); checker_list_t *submap_checkers DEFER_UNSLIDE = checker_get_and_slide_submap_checkers(submap_parent); /* * Actual entry in submap is clamped to the parent map submap view * by vm_region. Checker for that entry is unchanged. */ vm_entry_checker_t *submap_content = checker_list_nth(submap_checkers, 0); assert_allocation_checker(submap_content, parent_start - parent_offset, submap_size); assert(submap_content->submap_depth == 1); assert_allocation_entry(parent_start, parent_size, 1 /* submap_depth */, &content_info); assert(submap_content->object_offset == 0); assert(content_info.offset == 0); return TestSucceeded; } static test_result_t test_single_submap_oversize_entry_at_start( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { /* * parent map: [start, start+size] * submap: [0 (size) size*2] */ return test_single_submap_oversize_entry_common(checker_list, start, size, size /* parent_offset */, size * 2 /* submap_size */); } static test_result_t test_single_submap_oversize_entry_at_end( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { /* * parent map: [start, start+size] * submap: [0 (size) size*2] */ return test_single_submap_oversize_entry_common(checker_list, start, size, 0 /* parent_offset */, size * 2 /* submap_size */); } static test_result_t test_single_submap_oversize_entry_at_both( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { /* * parent map: [start, start+size] * submap: [0 (size / 2) size*2] */ return test_single_submap_oversize_entry_common(checker_list, start, size, size / 2 /* parent_offset */, size * 2 /* submap_size */); } static test_result_t test_submap_before_allocation_common( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size, int submap_protection) { vm_region_submap_info_data_64_t submap_parent_info; vm_entry_checker_t *submap_parent = checker_list_nth(checker_list, 0); assert_submap_checker_and_entry(submap_parent, start, size / 2, &submap_parent_info); vm_region_submap_info_data_64_t allocation_info; vm_entry_checker_t *allocation = checker_list_nth(checker_list, 1); assert_allocation_checker_and_entry(allocation, start + size / 2, size / 2, &allocation_info); checker_list_t *submap_checkers DEFER_UNSLIDE = checker_get_and_slide_submap_checkers(submap_parent); vm_region_submap_info_data_64_t submap_content_info; vm_entry_checker_t *submap_content = checker_list_nth(submap_checkers, 0); assert_allocation_checker(submap_content, start, size / 2); assert_allocation_entry(start, size / 2, 1 /* submap_depth */, &submap_content_info); assert_checker_and_entry_protection_equals(submap_content, &submap_content_info, submap_protection, submap_protection); return TestSucceeded; } static test_result_t test_submap_before_allocation( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { return test_submap_before_allocation_common(checker_list, start, size, VM_PROT_READ | VM_PROT_WRITE); } static test_result_t test_submap_before_allocation_ro( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { return test_submap_before_allocation_common(checker_list, start, size, VM_PROT_READ); } static test_result_t test_submap_after_allocation_common( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size, int submap_protection) { vm_region_submap_info_data_64_t allocation_info; vm_entry_checker_t *allocation = checker_list_nth(checker_list, 0); assert_allocation_checker_and_entry(allocation, start, size / 2, &allocation_info); vm_region_submap_info_data_64_t submap_parent_info; vm_entry_checker_t *submap_parent = checker_list_nth(checker_list, 1); assert_submap_checker_and_entry(submap_parent, start + size / 2, size / 2, &submap_parent_info); checker_list_t *submap_checkers DEFER_UNSLIDE = checker_get_and_slide_submap_checkers(submap_parent); vm_region_submap_info_data_64_t submap_content_info; vm_entry_checker_t *submap_content = checker_list_nth(submap_checkers, 0); assert_allocation_checker(submap_content, start + size / 2, size / 2); assert_allocation_entry(start + size / 2, size / 2, 1 /* submap_depth */, &submap_content_info); assert_checker_and_entry_protection_equals(submap_content, &submap_content_info, submap_protection, submap_protection); return TestSucceeded; } static test_result_t test_submap_after_allocation( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { return test_submap_after_allocation_common(checker_list, start, size, VM_PROT_READ | VM_PROT_WRITE); } static test_result_t test_submap_after_allocation_ro( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { return test_submap_after_allocation_common(checker_list, start, size, VM_PROT_READ); } static test_result_t test_submap_before_hole_common( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size, int submap_protection) { vm_region_submap_info_data_64_t submap_parent_info; vm_entry_checker_t *submap_parent = checker_list_nth(checker_list, 0); assert_submap_checker_and_entry(submap_parent, start, size / 2, &submap_parent_info); vm_entry_checker_t *hole = checker_list_nth(checker_list, 1); assert_hole_checker_and_entry(hole, start + size / 2, size / 2); checker_list_t *submap_checkers DEFER_UNSLIDE = checker_get_and_slide_submap_checkers(submap_parent); vm_region_submap_info_data_64_t submap_content_info; vm_entry_checker_t *submap_content = checker_list_nth(submap_checkers, 0); assert_allocation_checker(submap_content, start, size / 2); assert_allocation_entry(start, size / 2, 1 /* submap_depth */, &submap_content_info); assert_checker_and_entry_protection_equals(submap_content, &submap_content_info, submap_protection, submap_protection); return TestSucceeded; } static test_result_t test_submap_before_hole( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { return test_submap_before_hole_common(checker_list, start, size, VM_PROT_READ | VM_PROT_WRITE); } static test_result_t test_submap_before_hole_ro( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { return test_submap_before_hole_common(checker_list, start, size, VM_PROT_READ); } static test_result_t test_submap_after_hole_common( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size, int submap_protection) { vm_entry_checker_t *hole = checker_list_nth(checker_list, 0); assert_hole_checker_and_entry(hole, start, size / 2); vm_region_submap_info_data_64_t submap_parent_info; vm_entry_checker_t *submap_parent = checker_list_nth(checker_list, 1); assert_submap_checker_and_entry(submap_parent, start + size / 2, size / 2, &submap_parent_info); checker_list_t *submap_checkers DEFER_UNSLIDE = checker_get_and_slide_submap_checkers(submap_parent); vm_region_submap_info_data_64_t submap_content_info; vm_entry_checker_t *submap_content = checker_list_nth(submap_checkers, 0); assert_allocation_checker(submap_content, start + size / 2, size / 2); assert_allocation_entry(start + size / 2, size / 2, 1 /* submap_depth */, &submap_content_info); assert_checker_and_entry_protection_equals(submap_content, &submap_content_info, submap_protection, submap_protection); return TestSucceeded; } static test_result_t test_submap_after_hole( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { return test_submap_after_hole_common(checker_list, start, size, VM_PROT_READ | VM_PROT_WRITE); } static test_result_t test_submap_after_hole_ro( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { return test_submap_after_hole_common(checker_list, start, size, VM_PROT_READ); } /* * Verify that the checker list consists of three entries, * a submap mapping, an allocation, and a submap mapping, * all of default size. */ static void assert_submap_allocation_submap( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { vm_region_submap_info_data_64_t info; vm_entry_checker_t *checker; mach_vm_size_t offset; assert(checker_range_count(checker_list->entries) == 3); offset = DEFAULT_ENTRY_SIZE * 0; checker = checker_list_nth(checker_list, 0); assert_submap_checker_and_entry(checker, start + offset, DEFAULT_ENTRY_SIZE, &info); assert(checker->object_offset == offset); assert(info.offset == offset); offset = DEFAULT_ENTRY_SIZE * 1; checker = checker_list_nth(checker_list, 1); assert_allocation_checker_and_entry(checker, start + offset, DEFAULT_ENTRY_SIZE, &info); offset = DEFAULT_ENTRY_SIZE * 2; checker = checker_list_nth(checker_list, 2); assert_submap_checker_and_entry(checker, start + offset, DEFAULT_ENTRY_SIZE, &info); assert(checker->object_offset == offset); assert(info.offset == offset); offset = DEFAULT_ENTRY_SIZE * 3; assert(size == offset); } static test_result_t test_submap_allocation_submap_one_entry_common( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size, int submap_protection) { /* parent map is submap - allocation - submap */ assert_submap_allocation_submap(checker_list, start, size); /* submap is one allocation entry */ checker_list_t *submap_checkers DEFER_UNSLIDE = checker_get_and_slide_submap_checkers(checker_list_nth(checker_list, 0)); assert(checker_range_count(submap_checkers->entries) == 1); vm_entry_checker_t *submap_content = checker_list_nth(submap_checkers, 0); assert_allocation_checker(submap_content, start, size); assert(submap_content->protection == submap_protection); assert(submap_content->max_protection == submap_protection); return TestSucceeded; } static test_result_t test_submap_allocation_submap_one_entry( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { return test_submap_allocation_submap_one_entry_common(checker_list, start, size, VM_PROT_READ | VM_PROT_WRITE); } static test_result_t test_submap_allocation_submap_one_entry_ro( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { return test_submap_allocation_submap_one_entry_common(checker_list, start, size, VM_PROT_READ); } static test_result_t test_submap_allocation_submap_two_entries_common( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size, int submap_protection) { /* parent map is submap - allocation - submap */ assert_submap_allocation_submap(checker_list, start, size); /* submap is two allocation entries */ checker_list_t *submap_checkers DEFER_UNSLIDE = checker_get_and_slide_submap_checkers(checker_list_nth(checker_list, 0)); assert(checker_range_count(submap_checkers->entries) == 2); vm_entry_checker_t *submap_content = checker_list_nth(submap_checkers, 0); assert_allocation_checker(submap_content, start, size / 2); assert(submap_content->protection == submap_protection); assert(submap_content->max_protection == submap_protection); submap_content = checker_list_nth(submap_checkers, 1); assert_allocation_checker(submap_content, start + size / 2, size / 2); assert(submap_content->protection == submap_protection); assert(submap_content->max_protection == submap_protection); return TestSucceeded; } static test_result_t test_submap_allocation_submap_two_entries( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { return test_submap_allocation_submap_two_entries_common(checker_list, start, size, VM_PROT_READ | VM_PROT_WRITE); } static test_result_t test_submap_allocation_submap_two_entries_ro( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { return test_submap_allocation_submap_two_entries_common(checker_list, start, size, VM_PROT_READ); } static test_result_t test_submap_allocation_submap_three_entries_common( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size, int submap_protection) { /* parent map is submap - allocation - submap */ assert_submap_allocation_submap(checker_list, start, size); /* submap is three allocation entries */ checker_list_t *submap_checkers DEFER_UNSLIDE = checker_get_and_slide_submap_checkers(checker_list_nth(checker_list, 0)); assert(checker_range_count(submap_checkers->entries) == 3); vm_entry_checker_t *submap_content = checker_list_nth(submap_checkers, 0); assert_allocation_checker(submap_content, start, size / 3); assert(submap_content->protection == submap_protection); assert(submap_content->max_protection == submap_protection); submap_content = checker_list_nth(submap_checkers, 1); assert_allocation_checker(submap_content, start + size / 3, size / 3); assert(submap_content->protection == submap_protection); assert(submap_content->max_protection == submap_protection); submap_content = checker_list_nth(submap_checkers, 2); assert_allocation_checker(submap_content, start + size / 3 * 2, size / 3); assert(submap_content->protection == submap_protection); assert(submap_content->max_protection == submap_protection); return TestSucceeded; } static test_result_t test_submap_allocation_submap_three_entries( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { return test_submap_allocation_submap_three_entries_common(checker_list, start, size, VM_PROT_READ | VM_PROT_WRITE); } static test_result_t test_submap_allocation_submap_three_entries_ro( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size) { return test_submap_allocation_submap_three_entries_common(checker_list, start, size, VM_PROT_READ); } static void assert_protection( mach_vm_address_t address, vm_prot_t prot, bool check_max, uint32_t submap_depth) { mach_vm_address_t info_address = address; mach_vm_size_t info_size; vm_region_submap_info_data_64_t info; assert(get_info_for_address(&info_address, &info_size, &info, submap_depth)); assert(info_address == address); if (check_max) { T_QUIET; T_ASSERT_EQ(prot, info.max_protection, "entry max protection"); } else { T_QUIET; T_ASSERT_EQ(prot, info.protection, "entry protection"); } } static test_result_t test_protection_single_common( checker_list_t *checker_list, mach_vm_address_t address, vm_prot_t prot, vm_prot_t max) { vm_entry_checker_t *checker = checker_list_find_allocation(checker_list, address); T_QUIET; T_ASSERT_NOTNULL(checker, "checker"); T_QUIET; T_ASSERT_EQ(checker->protection, prot, "checker protection"); T_QUIET; T_ASSERT_EQ(checker->max_protection, max, "checker max protection"); assert_protection(address, prot, false /* check max */, 0 /* submap depth */); assert_protection(address, max, true /* check max */, 0 /* submap depth */); return TestSucceeded; } static test_result_t test_protection_pair_common( checker_list_t *checker_list, mach_vm_address_t address, vm_prot_t left_prot, vm_prot_t right_prot) { vm_entry_checker_t *left_checker = checker_list_find_allocation(checker_list, address); vm_entry_checker_t *right_checker = left_checker->next; T_QUIET; T_ASSERT_NOTNULL(left_checker, "checker"); T_QUIET; T_ASSERT_EQ(left_checker->protection, left_prot, "left entry protection"); T_QUIET; T_ASSERT_EQ(right_checker->protection, right_prot, "right entry protection"); assert_protection(left_checker->address, left_prot, false /* check max */, 0 /* submap depth */); assert_protection(right_checker->address, right_prot, false /* check max */, 0 /* submap depth */); return TestSucceeded; } static test_result_t test_protection_single_000_000( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_single_common( checker_list, start, VM_PROT_NONE, VM_PROT_NONE); } static test_result_t test_protection_single_000_r00( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_single_common( checker_list, start, VM_PROT_NONE, VM_PROT_READ); } static test_result_t test_protection_single_000_0w0( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_single_common( checker_list, start, VM_PROT_NONE, VM_PROT_WRITE); } static test_result_t test_protection_single_000_rw0( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_single_common( checker_list, start, VM_PROT_NONE, VM_PROT_READ | VM_PROT_WRITE); } static test_result_t test_protection_single_r00_r00( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_single_common( checker_list, start, VM_PROT_READ, VM_PROT_READ); } static test_result_t test_protection_single_r00_rw0( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_single_common( checker_list, start, VM_PROT_READ, VM_PROT_READ | VM_PROT_WRITE); } static test_result_t test_protection_single_0w0_0w0( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_single_common( checker_list, start, VM_PROT_WRITE, VM_PROT_WRITE); } static test_result_t test_protection_single_0w0_rw0( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_single_common( checker_list, start, VM_PROT_WRITE, VM_PROT_READ | VM_PROT_WRITE); } static test_result_t test_protection_single_rw0_rw0( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_single_common( checker_list, start, VM_PROT_READ | VM_PROT_WRITE, VM_PROT_READ | VM_PROT_WRITE); } static test_result_t test_protection_pair_000_000( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_pair_common( checker_list, start, VM_PROT_NONE, VM_PROT_NONE); } static test_result_t test_protection_pair_000_r00( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_pair_common( checker_list, start, VM_PROT_NONE, VM_PROT_READ); } static test_result_t test_protection_pair_000_0w0( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_pair_common( checker_list, start, VM_PROT_NONE, VM_PROT_WRITE); } static test_result_t test_protection_pair_000_rw0( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_pair_common( checker_list, start, VM_PROT_NONE, VM_PROT_READ | VM_PROT_WRITE); } static test_result_t test_protection_pair_r00_000( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_pair_common( checker_list, start, VM_PROT_READ, VM_PROT_NONE); } static test_result_t test_protection_pair_r00_r00( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_pair_common( checker_list, start, VM_PROT_READ, VM_PROT_READ); } static test_result_t test_protection_pair_r00_0w0( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_pair_common( checker_list, start, VM_PROT_READ, VM_PROT_WRITE); } static test_result_t test_protection_pair_r00_rw0( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_pair_common( checker_list, start, VM_PROT_READ, VM_PROT_READ | VM_PROT_WRITE); } static test_result_t test_protection_pair_0w0_000( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_pair_common( checker_list, start, VM_PROT_WRITE, VM_PROT_NONE); } static test_result_t test_protection_pair_0w0_r00( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_pair_common( checker_list, start, VM_PROT_WRITE, VM_PROT_READ); } static test_result_t test_protection_pair_0w0_0w0( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_pair_common( checker_list, start, VM_PROT_WRITE, VM_PROT_WRITE); } static test_result_t test_protection_pair_0w0_rw0( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_pair_common( checker_list, start, VM_PROT_WRITE, VM_PROT_READ | VM_PROT_WRITE); } static test_result_t test_protection_pair_rw0_000( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_pair_common( checker_list, start, VM_PROT_READ | VM_PROT_WRITE, VM_PROT_NONE); } static test_result_t test_protection_pair_rw0_r00( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_pair_common( checker_list, start, VM_PROT_READ | VM_PROT_WRITE, VM_PROT_READ); } static test_result_t test_protection_pair_rw0_0w0( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_pair_common( checker_list, start, VM_PROT_READ | VM_PROT_WRITE, VM_PROT_WRITE); } static test_result_t test_protection_pair_rw0_rw0( checker_list_t *checker_list, mach_vm_address_t start, mach_vm_size_t size __unused) { return test_protection_pair_common( checker_list, start, VM_PROT_READ | VM_PROT_WRITE, VM_PROT_READ | VM_PROT_WRITE); } T_DECL(test_vm_configurator, "spot-check VM states generated by vm configurator") { vm_tests_t tests = { .single_entry_1 = test_single_entry_1, .single_entry_2 = test_single_entry_2, .single_entry_3 = test_single_entry_3, .single_entry_4 = test_single_entry_4, .multiple_entries_1 = test_multiple_entries_1, .multiple_entries_2 = test_multiple_entries_2, .multiple_entries_3 = test_multiple_entries_3, .multiple_entries_4 = test_multiple_entries_4, .multiple_entries_5 = test_multiple_entries_5, .multiple_entries_6 = test_multiple_entries_6, .some_holes_1 = test_some_holes_1, .some_holes_2 = test_some_holes_2, .some_holes_3 = test_some_holes_3, .some_holes_4 = test_some_holes_4, .some_holes_5 = test_some_holes_5, .some_holes_6 = test_some_holes_6, .some_holes_7 = test_some_holes_7, .some_holes_8 = test_some_holes_8, .some_holes_9 = test_some_holes_9, .some_holes_10 = test_some_holes_10, .some_holes_11 = test_some_holes_11, .some_holes_12 = test_some_holes_12, .all_holes_1 = test_all_holes_1, .all_holes_2 = test_all_holes_2, .all_holes_3 = test_all_holes_3, .all_holes_4 = test_all_holes_4, .null_entry = test_null_entry, .nonresident_entry = test_nonresident_entry, .resident_entry = test_resident_entry, .shared_entry = test_shared_entry, .shared_entry_discontiguous = test_shared_entry_discontiguous, .shared_entry_partial = test_shared_entry_partial, .shared_entry_pairs = test_shared_entry_pairs, .shared_entry_x1000 = test_shared_entry_x1000, .cow_entry = test_cow_entry, .cow_unreferenced = test_cow_unreferenced, .cow_nocow = test_cow_nocow, .nocow_cow = test_nocow_cow, .cow_unreadable = test_cow_unreadable, .cow_unwriteable = test_cow_unwriteable, .permanent_entry = test_permanent_entry, .permanent_before_permanent = test_permanent_before_permanent, .permanent_before_allocation = test_permanent_before_allocation, .permanent_before_allocation_2 = test_permanent_before_allocation_2, .permanent_before_hole = test_permanent_before_hole, .permanent_after_allocation = test_permanent_after_allocation, .permanent_after_hole = test_permanent_after_hole, .single_submap_single_entry = test_single_submap_single_entry, .single_submap_single_entry_first_pages = test_single_submap_single_entry_first_pages, .single_submap_single_entry_last_pages = test_single_submap_single_entry_last_pages, .single_submap_single_entry_middle_pages = test_single_submap_single_entry_middle_pages, .single_submap_oversize_entry_at_start = test_single_submap_oversize_entry_at_start, .single_submap_oversize_entry_at_end = test_single_submap_oversize_entry_at_end, .single_submap_oversize_entry_at_both = test_single_submap_oversize_entry_at_both, .submap_before_allocation = test_submap_before_allocation, .submap_after_allocation = test_submap_after_allocation, .submap_before_hole = test_submap_before_hole, .submap_after_hole = test_submap_after_hole, .submap_allocation_submap_one_entry = test_submap_allocation_submap_one_entry, .submap_allocation_submap_two_entries = test_submap_allocation_submap_two_entries, .submap_allocation_submap_three_entries = test_submap_allocation_submap_three_entries, .submap_before_allocation_ro = test_submap_before_allocation_ro, .submap_after_allocation_ro = test_submap_after_allocation_ro, .submap_before_hole_ro = test_submap_before_hole_ro, .submap_after_hole_ro = test_submap_after_hole_ro, .submap_allocation_submap_one_entry_ro = test_submap_allocation_submap_one_entry_ro, .submap_allocation_submap_two_entries_ro = test_submap_allocation_submap_two_entries_ro, .submap_allocation_submap_three_entries_ro = test_submap_allocation_submap_three_entries_ro, .protection_single_000_000 = test_protection_single_000_000, .protection_single_000_r00 = test_protection_single_000_r00, .protection_single_000_0w0 = test_protection_single_000_0w0, .protection_single_000_rw0 = test_protection_single_000_rw0, .protection_single_r00_r00 = test_protection_single_r00_r00, .protection_single_r00_rw0 = test_protection_single_r00_rw0, .protection_single_0w0_0w0 = test_protection_single_0w0_0w0, .protection_single_0w0_rw0 = test_protection_single_0w0_rw0, .protection_single_rw0_rw0 = test_protection_single_rw0_rw0, .protection_pairs_000_000 = test_protection_pair_000_000, .protection_pairs_000_r00 = test_protection_pair_000_r00, .protection_pairs_000_0w0 = test_protection_pair_000_0w0, .protection_pairs_000_rw0 = test_protection_pair_000_rw0, .protection_pairs_r00_000 = test_protection_pair_r00_000, .protection_pairs_r00_r00 = test_protection_pair_r00_r00, .protection_pairs_r00_0w0 = test_protection_pair_r00_0w0, .protection_pairs_r00_rw0 = test_protection_pair_r00_rw0, .protection_pairs_0w0_000 = test_protection_pair_0w0_000, .protection_pairs_0w0_r00 = test_protection_pair_0w0_r00, .protection_pairs_0w0_0w0 = test_protection_pair_0w0_0w0, .protection_pairs_0w0_rw0 = test_protection_pair_0w0_rw0, .protection_pairs_rw0_000 = test_protection_pair_rw0_000, .protection_pairs_rw0_r00 = test_protection_pair_rw0_r00, .protection_pairs_rw0_0w0 = test_protection_pair_rw0_0w0, .protection_pairs_rw0_rw0 = test_protection_pair_rw0_rw0, }; run_vm_tests("test_vm_configurator", __FILE__, &tests, argc, argv); }