xref: /xnu-12377.41.6/tests/exc_guard_helper.h (revision bbb1b6f9e71b8cdde6e5cd6f4841f207dee3d828)
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