1 /* 2 * Copyright (c) 2024 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29 /* 30 * exc_guard_helper.h 31 * 32 * Helper functions for userspace tests to test for EXC_GUARD exceptions. 33 * 34 * To use these functions in your test you must set additional build options. 35 * See target `exc_guard_helper_test` in tests/Makefile for an example. 36 */ 37 38 #pragma once 39 40 #include <stdbool.h> 41 #include <stdint.h> 42 #include <mach/task_info.h> 43 44 /* 45 * Set verbose_exc_helper = true to log exception information with T_LOG(). 46 * The default is true. 47 */ 48 extern bool verbose_exc_helper; 49 50 typedef struct { 51 /* The number of EXC_GUARD exceptions caught during the block. */ 52 unsigned catch_count; 53 54 /* 55 * The remaining fields are only set for the first EXC_GUARD caught. 56 * See kern/exc_guard.h for definitions of these fields. 57 */ 58 unsigned guard_type; /* e.g. GUARD_TYPE_VIRT_MEMORY */ 59 uint32_t guard_flavor; 60 uint32_t guard_target; 61 uint64_t guard_payload; 62 } exc_guard_helper_info_t; 63 64 /* 65 * Initialize exc_guard_helper's exception handling. 66 * 67 * Calling this is optional. The other functions will perform 68 * initialization if necessary. You may need to call this 69 * function if that automatic initialization allocates 70 * memory in address ranges that your test requires to 71 * be unallocated. 72 */ 73 extern void 74 exc_guard_helper_init(void); 75 76 /* 77 * Sets EXC_GUARD exceptions of the given type (e.g. GUARD_TYPE_VIRT_MEMORY) 78 * to be enabled and non-fatal in this process. 79 * Returns the previous guard exception behavior. Pass this value 80 * to task_set_exc_guard_behavior() to restore the previous behavior. 81 * 82 * Fails with T_FAIL if the behavior could not be set; for example: 83 * - guard exceptions cannot be configured in some processes 84 * - some guard exception types cannot be set to non-fatal 85 */ 86 extern task_exc_guard_behavior_t 87 enable_exc_guard_of_type(unsigned int guard_type); 88 89 /* 90 * Runs block() and returns true if it raised a non-fatal EXC_GUARD exception 91 * of the requested type (e.g. GUARD_TYPE_VIRT_MEMORY). 92 * 93 * While block() runs, any EXC_GUARD exceptions of the requested 94 * type are caught and recorded, then execution resumes. 95 * Information about any caught exception(s) is returned in *out_exc_info. 96 * If more than one EXC_GUARD exception of the requested type is raised then 97 * details about all but the first are discarded, other than `catch_count` 98 * the number of exceptions caught. 99 * 100 * Guard exceptions of this type must be enabled and non-fatal. 101 * enable_exc_guard_of_type() can set this for your process. 102 * 103 * Note that block_raised_exc_guard_of_type(GUARD_TYPE_VIRT_MEMORY) 104 * does not work on Rosetta. This function will T_FAIL if you try. 105 * See block_raised_exc_guard_of_type_ignoring_translated() below 106 * if you are willing to forgo the guard exception handler in 107 * translated execution environments like Rosetta. 108 * 109 * Example: 110 * enable_exc_guard_of_type(GUARD_TYPE_VIRT_MEMORY); 111 * [...] 112 * exc_guard_helper_info_t exc_info; 113 * if (block_raised_exc_guard_of_type(GUARD_TYPE_VIRT_MEMORY, &exc_info, ^{ 114 * mach_vm_deallocate(mach_task_self(), addr, size); 115 * })) { 116 * // EXC_GUARD raised during mach_vm_deallocate, details in exc_info 117 * } else { 118 * // mach_vm_deallocate did not raise EXC_GUARD 119 * } 120 */ 121 typedef void (^exc_guard_helper_block_t)(void); 122 extern bool 123 block_raised_exc_guard_of_type( 124 unsigned int guard_type, 125 exc_guard_helper_info_t * const out_exc_info, 126 exc_guard_helper_block_t block); 127 128 /* 129 * Like block_raised_exc_guard_of_type(), but quietly 130 * runs the block with no guard exception handler if 131 * the guard type is GUARD_TYPE_VIRT_MEMORY and we're 132 * in a translated execution environment like Rosetta. 133 */ 134 extern bool 135 block_raised_exc_guard_of_type_ignoring_translated( 136 unsigned int guard_type, 137 exc_guard_helper_info_t * const out_exc_info, 138 exc_guard_helper_block_t block); 139