xref: /xnu-12377.41.6/osfmk/tests/vm_parameter_validation.h (revision bbb1b6f9e71b8cdde6e5cd6f4841f207dee3d828)
1 #ifndef VM_PARAMETER_VALIDATION_H
2 #define VM_PARAMETER_VALIDATION_H
3 
4 
5 /*
6  * Common Naming Conventions:
7  * call_* functions are harnesses used to call a single function under test.
8  * They take all arguments needed to call the function and avoid calling functions with PANICing values.
9  * test_* functions are used to call the call_ functions. They iterate through possibilities of interesting parameters
10  * and provide those as arguments to the call_ functions.
11  *
12  * test_* functions are named in the following way:
13  * Arguments under test are put at the end of the name. e.g. (test_mach_vm_prot) tests a vm_prot_t
14  * test_mach_... functions test a function with the first argument being a MAP_T.
15  * test_unix_... functions test a unix-y function. This means it doesn't take a MAP_T.
16  * In kernel context, it means it operates on current_map instead of an arbitrary vm_map_t
17  * test_..._with_allocated_... means an allocation has already been created, and some parameters referring to that allocation are passed in.
18  *
19  * Common Abbreviations:
20  * ssz: Start + Start + Size
21  * ssoo: Start + Size + Offset + Object
22  * sso: Start + Start + Offset
23  */
24 
25 #include <sys/mman.h>
26 #if KERNEL
27 
28 #include <mach/vm_map.h>
29 #include <mach/mach_vm.h>
30 #include <mach/vm_reclaim.h>
31 #include <mach/vm_reclaim_private.h>
32 #include <mach/mach_types.h>
33 #include <mach/mach_host.h>
34 #include <mach/memory_object.h>
35 #include <mach/memory_entry.h>
36 #include <mach/mach_vm_server.h>
37 
38 #include <device/device_port.h>
39 #include <sys/mman.h>
40 #include <sys/errno.h>
41 #include <vm/memory_object.h>
42 #include <vm/vm_fault.h>
43 #include <vm/vm_map_internal.h>
44 #include <vm/vm_kern_internal.h>
45 #include <vm/vm_pageout.h>
46 #include <vm/vm_protos.h>
47 #include <vm/vm_memtag.h>
48 #include <vm/vm_memory_entry.h>
49 #include <vm/vm_memory_entry_xnu.h>
50 #include <vm/vm_object_internal.h>
51 #include <vm/vm_iokit.h>
52 #include <kern/ledger.h>
53 extern ledger_template_t        task_ledger_template;
54 
55 #define FLAGS_AND_TAG(f, t) ({                             \
56 	vm_map_kernel_flags_t vmk_flags;                   \
57 	vm_map_kernel_flags_set_vmflags(&vmk_flags, f, t); \
58 	vmk_flags;                                         \
59 })
60 
61 #else  // KERNEL
62 
63 #include <TargetConditionals.h>
64 
65 #endif // KERNEL
66 
67 
68 // ignore some warnings inside this file
69 #pragma clang diagnostic push
70 #pragma clang diagnostic ignored "-Wdeclaration-after-statement"
71 #pragma clang diagnostic ignored "-Wincompatible-function-pointer-types"
72 #pragma clang diagnostic ignored "-Wmissing-prototypes"
73 #pragma clang diagnostic ignored "-Wpedantic"
74 #pragma clang diagnostic ignored "-Wgcc-compat"
75 
76 /*
77  * Invalid values for various types. These are used by the outparameter tests.
78  * UNLIKELY_ means the value is not 100% guaranteed to be invalid for that type,
79  * and is just a very unlikely value for it. Tests should not rely on them to compare against UNLIKELY_
80  * values without explicit reason it cannot be possible.
81  *
82  * INVALID_* means the value is 100% guaranteed to be invalid. They can be relied on to be compared against.
83  */
84 
85 #define UNLIKELY_INITIAL_ADDRESS 0xabababab
86 /*
87  * It's important for us to never have a test with a size like
88  * UNLIKELY_INITIAL_SIZE, and for this to stay non page aligned.
89  * See comment in call_mach_memory_entry_map_size__start_size for more info
90  */
91 #define UNLIKELY_INITIAL_SIZE 0xabababab
92 #define UNLIKELY_INITIAL_PPNUM 0xabababab
93 #define UNLIKELY_INITIAL_MACH_PORT ((mach_port_t) 0xbabababa)
94 #define UNLIKELY_INITIAL_VID 0xbabababa
95 // This cannot possibly be a valid vnode pointer as they are pointers
96 #define INVALID_VNODE_PTR ((void *) -1)
97 // This cannot possibly be a valid vm_map_copy_t as they are pointers
98 #define INVALID_VM_MAP_COPY ((vm_map_copy_t) (void *) -1)
99 // This cannot be a purgable state (see vm_purgable.h) It's way above the last valid state
100 #define INVALID_PURGABLE_STATE 0xababab
101 static_assert(INVALID_PURGABLE_STATE > VM_PURGABLE_STATE_MAX, "This test requires a purgable state above the max");
102 // Disposition values are generated via the VM_PAGE_QUERY_ values being ored.
103 // This cannot be a valid one as it's above the greatest possible or
104 #define INVALID_DISPOSITION_VALUE 0xffffff0
105 #define INVALID_INHERIT 0xbaba
106 static_assert(INVALID_INHERIT > VM_INHERIT_LAST_VALID, "This test requires an inheritance above the max");
107 
108 #define INVALID_INITIAL_VID 0xbabababa
109 // output buffer size for kext/xnu sysctl tests
110 // note: 1 GB is too big for watchOS
111 static const int64_t SYSCTL_OUTPUT_BUFFER_SIZE = 512 * 1024 * 1024;  // 512 MB
112 
113 // caller name (kernel/kext/userspace), used to label the output
114 #if KERNEL
115 #       define CALLER_NAME "kernel"
116 #else
117 #       define CALLER_NAME "userspace"
118 #endif
119 
120 // os name, used to label the output
121 #if KERNEL
122 #       if XNU_TARGET_OS_OSX
123 #               define OS_NAME "macos"
124 #       elif XNU_TARGET_OS_IOS
125 #              define OS_NAME "ios"
126 #       elif XNU_TARGET_OS_TV
127 #               define OS_NAME "tvos"
128 #       elif XNU_TARGET_OS_WATCH
129 #               define OS_NAME "watchos"
130 #       elif XNU_TARGET_OS_BRIDGE
131 #               define OS_NAME "bridgeos"
132 #       else
133 #               define OS_NAME "unknown-os"
134 #       endif
135 #else
136 #       if TARGET_OS_OSX
137 #               define OS_NAME "macos"
138 #       elif TARGET_OS_MACCATALYST
139 #               define OS_NAME "catalyst"
140 #       elif TARGET_OS_IOS
141 #              define OS_NAME "ios"
142 #       elif TARGET_OS_TV
143 #               define OS_NAME "tvos"
144 #       elif TARGET_OS_WATCH
145 #               define OS_NAME "watchos"
146 #       elif TARGET_OS_BRIDGE
147 #               define OS_NAME "bridgeos"
148 #       else
149 #               define OS_NAME "unknown-os"
150 #       endif
151 #endif
152 
153 // architecture name, used to label the output
154 #if KERNEL
155 #       if __i386__
156 #               define ARCH_NAME "i386"
157 #       elif __x86_64__
158 #               define ARCH_NAME "x86_64"
159 #       elif __arm64__ && __LP64__
160 #               define ARCH_NAME "arm64"
161 #       elif __arm64__ && !__LP64__
162 #               define ARCH_NAME "arm64_32"
163 #       elif __arm__
164 #               define ARCH_NAME "arm"
165 #       else
166 #               define ARCH_NAME "unknown-arch"
167 #       endif
168 #else
169 #       if TARGET_CPU_X86
170 #               define ARCH_NAME "i386"
171 #       elif TARGET_CPU_X86_64
172 #               define ARCH_NAME "x86_64"
173 #       elif TARGET_CPU_ARM64 && __LP64__
174 #               define ARCH_NAME "arm64"
175 #       elif TARGET_CPU_ARM64 && !__LP64__
176 #               define ARCH_NAME "arm64_32"
177 #       elif TARGET_CPU_ARM
178 #               define ARCH_NAME "arm"
179 #       else
180 #               define ARCH_NAME "unknown-arch"
181 #       endif
182 #endif
183 
184 #if KERNEL
185 #       define MAP_T vm_map_t
186 #else
187 #       define MAP_T mach_port_t
188 #endif
189 
190 // Mach has new-style functions with 64-bit address and size
191 // and old-style functions with pointer-size address and size.
192 // On U64 platforms both names send the same MIG message
193 // and run the same kernel code so we need not test both.
194 // On U32 platforms they are different inside the kernel.
195 // fixme for kext/kernel, verify that vm32 entrypoints are not used and not exported
196 #if KERNEL || __LP64__
197 #       define TEST_OLD_STYLE_MACH 0
198 #else
199 #       define TEST_OLD_STYLE_MACH 1
200 #endif
201 
202 // always 64-bit: addr_t, mach_vm_address/size_t, memory_object_size/offset_t
203 // always 32-bit: mach_msg_type_number_t, natural_t
204 // pointer-size:  void*, vm_address_t, vm_size_t
205 typedef uint64_t addr_t;
206 
207 // We often use 4KB or 16KB instead of PAGE_SIZE
208 // (for example using 16KB instead of PAGE_SIZE to avoid Rosetta complications)
209 #define KB4 ((addr_t)4*1024)
210 #define KB16 ((addr_t)16*1024)
211 
212 // Allocation size commonly used in tests.
213 // This size is big enough that our trials of small
214 // address offsets and sizes will still fit inside it.
215 #define TEST_ALLOC_SIZE (4 * KB16)
216 
217 // Magic return codes used for in-band signalling.
218 // These must avoid kern_return_t and errno values.
219 #define BUSTED        -99  // trial is broken
220 #define IGNORED       -98  // trial not performed for acceptable reasons
221 #define ZEROSIZE      -97  // trial succeeded because size==0 (FAKE tests only)
222 #define PANIC         -96  // trial not performed because it would provoke a panic
223 #define GUARD         -95  // trial not performed because it would provoke EXC_GUARD
224 #define ACCEPTABLE    -94  // trial should be considered successful no matter what the golden result is
225 #define OUT_PARAM_BAD -93  // trial has incorrect setting of out parameter values
226 
227 static inline bool
is_fake_error(int err)228 is_fake_error(int err)
229 {
230 	return err == BUSTED || err == IGNORED || err == ZEROSIZE ||
231 	       err == PANIC || err == GUARD || err == OUT_PARAM_BAD;
232 }
233 
234 // Parameters passed between userspace and kernel
235 // for sysctl test vm_parameter_validation_kern
236 typedef struct {
237 	// Set this to sizeof(vm_parameter_validation_kern_args_t)
238 	uint64_t sizeof_args;
239 
240 	// Buffer for kernel test output. Allocated by userspace.
241 	uint64_t output_buffer_address;
242 	uint64_t output_buffer_size;
243 
244 	// File descriptor for kernel tests that map files. Allocated by userspace.
245 	uint64_t file_descriptor;
246 
247 	// Set if the kernel test output should be a golden file.
248 	// Read from GENERATE_GOLDEN_IMAGE.
249 	uint64_t generate_golden;
250 } vm_parameter_validation_kern_args_t;
251 
252 // Result values from sysctl test vm_parameter_validation_kern
253 #define KERN_TEST_SUCCESS  0
254 #define KERN_TEST_BAD_ARGS 1  // sizeof(args) didn't match args->sizeof_args
255 #define KERN_TEST_FAILED   2  // failed without running any tests; error text in output buffer
256 
257 #if KERNEL
258 
259 // "Global" data for test vm_parameter_validation_kern
260 // stored in the kernel thread test context.
261 typedef struct {
262 	thread_test_context_t ttc;
263 
264 	// Buffer for kernel test output. Allocated by userspace.
265 	user_addr_t output_buffer_start;
266 	user_addr_t output_buffer_cur;
267 	user_addr_t output_buffer_end;
268 
269 	// File descriptor for kernel tests that map files. Allocated by userspace.
270 	int file_descriptor;
271 
272 	// Set if the kernel test output should be a golden file.
273 	bool generate_golden;
274 
275 	// Cached lists of offsets. Populated by CACHE_OFFSETS().
276 	struct offset_list_t *addr_trial_offsets;
277 	struct offset_list_t *size_trial_offsets;
278 	struct offset_list_t *start_size_trial_offsets;
279 	struct offset_list_t *ssoo_absolute_offsets;
280 	struct offset_list_t *ssoo_absolute_and_relative_offsets;
281 } vm_parameter_validation_kern_thread_context_t;
282 
283 DECLARE_TEST_IDENTITY(test_identity_vm_parameter_validation_kern);
284 
285 // Get the test's global storage from thread-local data.
286 // Panics if not running on a development kernel.
287 // Panics if not running on the vm_parameter_validation_kern test's thread.
288 static vm_parameter_validation_kern_thread_context_t *
get_globals(void)289 get_globals(void)
290 {
291 	thread_test_context_t *ttc = thread_get_test_context();
292 	if (ttc == NULL ||
293 	    ttc->ttc_identity != test_identity_vm_parameter_validation_kern) {
294 		panic("no thread context or wrong thread context in test vm_parameter_validation_kern");
295 	}
296 
297 	return __container_of(ttc, vm_parameter_validation_kern_thread_context_t, ttc);
298 }
299 
300 #endif  /* KERNEL */
301 
302 // Return the count of a (non-decayed!) array.
303 #define countof(array) (sizeof(array) / sizeof((array)[0]))
304 
305 #if !KERNEL
306 static inline uint64_t
VM_MAP_PAGE_SIZE(MAP_T map __unused)307 VM_MAP_PAGE_SIZE(MAP_T map __unused)
308 {
309 	// fixme wrong for out-of-process maps
310 	// on platforms that support processes with two different page sizes
311 	return PAGE_SIZE;
312 }
313 
314 static inline uint64_t
VM_MAP_PAGE_MASK(MAP_T map __unused)315 VM_MAP_PAGE_MASK(MAP_T map __unused)
316 {
317 	// fixme wrong for out-of-process maps
318 	// on platforms that support processes with two different page sizes
319 	return PAGE_MASK;
320 }
321 #endif
322 
323 
324 #define IMPL(T)                                                         \
325 	/* Round up to the given page mask. */                          \
326 	__attribute__((overloadable, used))                             \
327 	static inline T                                                 \
328 	vm_sanitize_map_round_page_mask(T addr, uint64_t pagemask) {                      \
329 	        return (addr + (T)pagemask) & ~((T)pagemask);           \
330 	}                                                               \
331                                                                         \
332 	/* Round up to the given page size. */                          \
333 	__attribute__((overloadable, used))                             \
334 	static inline T                                                 \
335 	round_up_page(T addr, uint64_t pagesize) {                      \
336 	        return vm_sanitize_map_round_page_mask(addr, pagesize - 1);               \
337 	}                                                               \
338                                                                         \
339 	/* Round up to the given map's page size. */                    \
340 	__attribute__((overloadable, used))                             \
341 	static inline T                                                 \
342 	round_up_map(MAP_T map, T addr) {                               \
343 	        return vm_sanitize_map_round_page_mask(addr, VM_MAP_PAGE_MASK(map));      \
344 	}                                                               \
345                                                                         \
346 	/* Truncate to the given page mask. */                          \
347 	__attribute__((overloadable, used))                             \
348 	static inline T                                                 \
349 	vm_sanitize_map_trunc_page_mask(T addr, uint64_t pagemask)                      \
350 	{                                                               \
351 	        return addr & ~((T)pagemask);                           \
352 	}                                                               \
353                                                                         \
354 	/* Truncate to the given page size. */                          \
355 	__attribute__((overloadable, used))                             \
356 	static inline T                                                 \
357 	trunc_down_page(T addr, uint64_t pagesize)                      \
358 	{                                                               \
359 	        return vm_sanitize_map_trunc_page_mask(addr, pagesize - 1);             \
360 	}                                                               \
361                                                                         \
362 	/* Truncate to the given map's page size. */                    \
363 	__attribute__((overloadable, used))                             \
364 	static inline T                                                 \
365 	trunc_down_map(MAP_T map, T addr)                               \
366 	{                                                               \
367 	        return vm_sanitize_map_trunc_page_mask(addr, VM_MAP_PAGE_MASK(map));    \
368 	}                                                               \
369                                                                         \
370 	__attribute__((overloadable, unavailable("use round_up_page instead"))) \
371 	extern T                                                        \
372 	round_up(T addr, uint64_t pagesize);                            \
373 	__attribute__((overloadable, unavailable("use trunc_down_page instead"))) \
374 	extern T                                                        \
375 	trunc_down(T addr, uint64_t pagesize);
376 
377 IMPL(uint64_t)
IMPL(uint32_t)378 IMPL(uint32_t)
379 #undef IMPL
380 
381 
382 // duplicate the logic of VM's vm_map_range_overflows()
383 // false == good start+size combo, true == bad combo
384 #define IMPL(T)                                                         \
385 	__attribute__((overloadable, used))                             \
386 	static bool                                                     \
387 	vm_sanitize_range_overflows_allow_zero(T start, T size, T pgmask)           \
388 	{                                                               \
389 	        if (size == 0) {                                        \
390 	                return false;                                   \
391 	        }                                                       \
392                                                                         \
393 	        T sum;                                                  \
394 	        if (__builtin_add_overflow(start, size, &sum)) {        \
395 	                return true;                                    \
396 	        }                                                       \
397                                                                         \
398 	        T aligned_start = vm_sanitize_map_trunc_page_mask(start, pgmask);       \
399 	        T aligned_end = vm_sanitize_map_round_page_mask(start + size, pgmask);    \
400 	        if (aligned_end <= aligned_start) {                     \
401 	                return true;                                    \
402 	        }                                                       \
403                                                                         \
404 	        return false;                                           \
405 	}                                                               \
406                                                                         \
407 	/* like vm_sanitize_range_overflows_allow_zero(), but without the */        \
408 	/* unconditional approval of size==0 */                         \
409 	__attribute__((overloadable, used))                             \
410 	static bool                                                     \
411 	vm_sanitize_range_overflows_strict_zero(T start, T size, T pgmask)                      \
412 	{                                                               \
413 	        T sum;                                                  \
414 	        if (__builtin_add_overflow(start, size, &sum)) {        \
415 	                return true;                                    \
416 	        }                                                       \
417                                                                         \
418 	        T aligned_start = vm_sanitize_map_trunc_page_mask(start, pgmask);       \
419 	        T aligned_end = vm_sanitize_map_round_page_mask(start + size, pgmask);    \
420 	        if (aligned_end <= aligned_start) {                     \
421 	                return true;                                    \
422 	        }                                                       \
423                                                                         \
424 	        return false;                                           \
425 	}                                                               \
426 
427 IMPL(uint64_t)
428 IMPL(uint32_t)
429 #undef IMPL
430 
431 
432 // return true if the process is running under Rosetta translation
433 // https://developer.apple.com/documentation/apple-silicon/about-the-rosetta-translation-environment#Determine-Whether-Your-App-Is-Running-as-a-Translated-Binary
434 static bool
435 isRosetta()
436 {
437 #if KERNEL
438 	return false;
439 #else
440 	int out_value = 0;
441 	size_t io_size = sizeof(out_value);
442 	if (sysctlbyname("sysctl.proc_translated", &out_value, &io_size, NULL, 0) == 0) {
443 		assert(io_size >= sizeof(out_value));
444 		return out_value;
445 	}
446 	return false;
447 #endif
448 }
449 
450 // Needed to distinguish between rosetta kernel runs and generating trials names from kern golden files.
451 #if KERNEL
452 #define kern_trialname_generation FALSE
453 #else
454 static bool kern_trialname_generation = FALSE;
455 #endif
456 static addr_t trial_page_size = 0;
457 
458 static inline addr_t
adjust_page_size()459 adjust_page_size()
460 {
461 	addr_t test_page_size = PAGE_SIZE;
462 #if !KERNEL && __x86_64__
463 	// Handle kernel page size variation while recreating trials names for golden files in userspace.
464 	if (kern_trialname_generation && isRosetta()) {
465 		test_page_size = trial_page_size;
466 	}
467 #endif //  !KERNEL && __x86_64__
468 	return test_page_size;
469 }
470 
471 
472 /////////////////////////////////////////////////////
473 // String functions that work in both kernel and userspace.
474 
475 // Test output function.
476 // This prints either to stdout (userspace tests) or to a userspace buffer (kernel sysctl tests)
477 // Golden tests generation in userspace also writes to a buffer (GOLDEN_OUTPUT_BUF)
478 #if KERNEL
479 extern void testprintf(const char *, ...) __printflike(1, 2);
480 #define goldenprintf testprintf
481 #else
482 #define testprintf printf
483 extern void goldenprintf(const char *, ...) __printflike(1, 2);
484 #endif
485 
486 // kstrdup() is like strdup() but in the kernel it uses kalloc_data()
487 static inline char *
kstrdup(const char * str)488 kstrdup(const char *str)
489 {
490 #if KERNEL
491 	size_t size = strlen(str) + 1;
492 	char *copy = kalloc_data(size, Z_WAITOK | Z_ZERO);
493 	memcpy(copy, str, size);
494 	return copy;
495 #else
496 	return strdup(str);
497 #endif
498 }
499 
500 // kfree_str() is like free() but in the kernel it uses kfree_data_addr()
501 static inline void
kfree_str(char * str)502 kfree_str(char *str)
503 {
504 #if KERNEL
505 	kfree_data_addr(str);
506 #else
507 	free(str);
508 #endif
509 }
510 
511 // kasprintf() is like asprintf() but in the kernel it uses kalloc_data()
512 
513 #if !KERNEL
514 #       define kasprintf asprintf
515 #else
516 extern int vsnprintf(char *, size_t, const char *, va_list) __printflike(3, 0);
517 static inline int
kasprintf(char ** __restrict out_str,const char * __restrict format,...)518 kasprintf(char ** __restrict out_str, const char * __restrict format, ...) __printflike(2, 3)
519 {
520 	va_list args1, args2;
521 
522 	// compute length
523 	char c;
524 	va_start(args1, format);
525 	va_copy(args2, args1);
526 	int len1 = vsnprintf(&c, sizeof(c), format, args1);
527 	va_end(args1);
528 	if (len1 < 0) {
529 		*out_str = NULL;
530 		return len1;
531 	}
532 
533 	// allocate and print
534 	char *str = kalloc_data(len1 + 1, Z_NOFAIL);
535 	int len2 = vsnprintf(str, len1 + 1, format, args2);
536 	va_end(args2);
537 	if (len2 < 0) {
538 		kfree_data_addr(str);
539 		*out_str = NULL;
540 		return len1;
541 	}
542 	assert(len1 == len2);
543 
544 	*out_str = str;
545 	return len1;
546 }
547 // KERNEL
548 #endif
549 
550 
551 /////////////////////////////////////////////////////
552 // Record trials and return values from tested functions (BSD int or Mach kern_return_t)
553 
554 // Maintain list of known trials "smart" generator functions (trial formulae) as
555 // these are included in the golden result list (keeping the enum forces people to
556 // maintain the list up-to-date when adding new functions).
557 #define TRIALSFORMULA_ENUM(VARIANT) \
558 	VARIANT(eUNKNOWN_TRIALS) \
559 	VARIANT(eSMART_VM_MAP_KERNEL_FLAGS_TRIALS) \
560 	VARIANT(eSMART_VM_INHERIT_TRIALS) \
561 	VARIANT(eSMART_MMAP_KERNEL_FLAGS_TRIALS) \
562 	VARIANT(eSMART_MMAP_FLAGS_TRIALS) \
563 	VARIANT(eSMART_GENERIC_FLAG_TRIALS) \
564 	VARIANT(eSMART_VM_TAG_TRIALS) \
565 	VARIANT(eSMART_VM_PROT_TRIALS) \
566 	VARIANT(eSMART_VM_PROT_PAIR_TRIALS) \
567 	VARIANT(eSMART_LEDGER_TAG_TRIALS) \
568 	VARIANT(eSMART_LEDGER_FLAG_TRIALS) \
569 	VARIANT(eSMART_ADDR_TRIALS) \
570 	VARIANT(eSMART_SIZE_TRIALS) \
571 	VARIANT(eSMART_START_SIZE_TRIALS) \
572 	VARIANT(eSMART_START_SIZE_OFFSET_OBJECT_TRIALS) \
573 	VARIANT(eSMART_START_SIZE_OFFSET_TRIALS) \
574 	VARIANT(eSMART_SIZE_SIZE_TRIALS) \
575 	VARIANT(eSMART_SRC_DST_SIZE_TRIALS) \
576 	VARIANT(eSMART_FILEOFF_DST_SIZE_TRIALS) \
577 	VARIANT(eSMART_VM_BEHAVIOR_TRIALS) \
578 	VARIANT(eSMART_VM_ADVISE_TRIALS) \
579 	VARIANT(eSMART_VM_SYNC_TRIALS) \
580 	VARIANT(eSMART_VM_MSYNC_TRIALS) \
581 	VARIANT(eSMART_VM_MACHINE_ATTRIBUTE_TRIALS) \
582 	VARIANT(eSMART_VM_PURGEABLE_AND_STATE_TRIALS) \
583 	VARIANT(eSMART_START_SIZE_START_SIZE_TRIALS) \
584 	VARIANT(eSMART_SHARED_REGION_MAP_AND_SLIDE_2_TRIALS) \
585 	VARIANT(eSMART_RECLAMATION_BUFFER_INIT_TRIALS)
586 
587 #define TRIALSFORMULA_ENUM_VARIANT(NAME) NAME,
588 typedef enum {
589 	TRIALSFORMULA_ENUM(TRIALSFORMULA_ENUM_VARIANT)
590 } trialsformula_t;
591 
592 #define TRIALSARGUMENTS_NONE 0
593 #define TRIALSARGUMENTS_SIZE 2
594 
595 // formula enum id to string
596 #define TRIALSFORMULA_ENUM_STRING(NAME) case NAME: return #NAME;
597 const char *
trialsformula_name(trialsformula_t formula)598 trialsformula_name(trialsformula_t formula)
599 {
600 	switch (formula) {
601 		TRIALSFORMULA_ENUM(TRIALSFORMULA_ENUM_STRING)
602 	default:
603 		testprintf("Unknown formula_t %d\n", formula);
604 		assert(false);
605 	}
606 }
607 
608 #define TRIALSFORMULA_ENUM_FROM_STRING(NAME)    \
609 	if (strncmp(string, #NAME, strlen(#NAME)) == 0) return NAME;
610 
611 // formula name to enum id
612 trialsformula_t
trialsformula_from_string(const char * string)613 trialsformula_from_string(const char *string)
614 {
615 	TRIALSFORMULA_ENUM(TRIALSFORMULA_ENUM_FROM_STRING)
616 	// else
617 	testprintf("Unknown formula %s\n", string);
618 	assert(false);
619 }
620 
621 // ret: return value of this trial
622 // name: name of this trial, including the input values passed in
623 typedef struct {
624 	int ret;
625 	char *name;
626 } result_t;
627 
628 typedef struct {
629 	const char *testname;
630 	char *testconfig;
631 	trialsformula_t trialsformula;
632 	uint64_t trialsargs[TRIALSARGUMENTS_SIZE];
633 	unsigned capacity;
634 	unsigned count;
635 	unsigned tested_count;
636 	bool kernel_buffer_full;  /* incomplete, parsed from a truncated buffer */
637 	result_t list[];
638 } results_t;
639 
640 static __attribute__((overloadable))
641 results_t *
alloc_results(const char * testname,char * testconfig,trialsformula_t trialsformula,uint64_t trialsargs[static TRIALSARGUMENTS_SIZE],unsigned capacity)642 alloc_results(const char *testname, char *testconfig,
643     trialsformula_t trialsformula, uint64_t trialsargs[static TRIALSARGUMENTS_SIZE],
644     unsigned capacity)
645 {
646 	results_t *results;
647 #if KERNEL
648 	results = kalloc_type(results_t, result_t, capacity, Z_WAITOK | Z_ZERO);
649 #else
650 	results = calloc(sizeof(results_t) + capacity * sizeof(result_t), 1);
651 #endif
652 	assert(results != NULL);
653 	results->testname = testname;
654 	results->testconfig = testconfig;
655 	results->trialsformula = trialsformula;
656 	for (unsigned i = 0; i < TRIALSARGUMENTS_SIZE; i++) {
657 		results->trialsargs[i] = trialsargs[i];
658 	}
659 	results->capacity = capacity;
660 	results->count = 0;
661 	results->tested_count = 0;
662 	results->kernel_buffer_full = false;
663 	return results;
664 }
665 
666 static char *
alloc_default_testconfig(void)667 alloc_default_testconfig(void)
668 {
669 	char *result;
670 	kasprintf(&result, "%s %s %s%s",
671 	    OS_NAME, ARCH_NAME,
672 	    kern_trialname_generation ? "kernel" : CALLER_NAME,
673 	    !kern_trialname_generation && isRosetta() ? " rosetta" : "");
674 	return result;
675 }
676 
677 static __attribute__((overloadable))
678 results_t *
alloc_results(const char * testname,trialsformula_t trialsformula,uint64_t * trialsargs,size_t trialsargs_count,unsigned capacity)679 alloc_results(const char *testname,
680     trialsformula_t trialsformula, uint64_t *trialsargs, size_t trialsargs_count,
681     unsigned capacity)
682 {
683 	assert(trialsargs_count == TRIALSARGUMENTS_SIZE);
684 	return alloc_results(testname, alloc_default_testconfig(), trialsformula, trialsargs, capacity);
685 }
686 
687 static __attribute__((overloadable))
688 results_t *
alloc_results(const char * testname,trialsformula_t trialsformula,uint64_t trialsarg0,unsigned capacity)689 alloc_results(const char *testname, trialsformula_t trialsformula, uint64_t trialsarg0, unsigned capacity)
690 {
691 	uint64_t trialsargs[TRIALSARGUMENTS_SIZE] = {trialsarg0, TRIALSARGUMENTS_NONE};
692 	return alloc_results(testname, trialsformula, trialsargs, TRIALSARGUMENTS_SIZE, capacity);
693 }
694 
695 static __attribute__((overloadable))
696 results_t *
alloc_results(const char * testname,trialsformula_t trialsformula,unsigned capacity)697 alloc_results(const char *testname, trialsformula_t trialsformula, unsigned capacity)
698 {
699 	uint64_t trialsargs[TRIALSARGUMENTS_SIZE] = {TRIALSARGUMENTS_NONE, TRIALSARGUMENTS_NONE};
700 	return alloc_results(testname, trialsformula, trialsargs, TRIALSARGUMENTS_SIZE, capacity);
701 }
702 
703 static void __unused
dealloc_results(results_t * results)704 dealloc_results(results_t *results)
705 {
706 	for (unsigned int i = 0; i < results->count; i++) {
707 		if (results->list[i].name) {
708 			kfree_str(results->list[i].name);
709 		}
710 	}
711 	if (results->testconfig) {
712 		kfree_str(results->testconfig);
713 	}
714 #if KERNEL
715 	kfree_type(results_t, result_t, results->capacity, results);
716 #else
717 	free(results);
718 #endif
719 }
720 
721 static void __attribute__((overloadable, unused))
append_result(results_t * results,int ret,const char * name)722 append_result(results_t *results, int ret, const char *name)
723 {
724 	// halt if the results list is already full
725 	// fixme reallocate instead if we can't always choose the size in advance
726 	assert(results->count < results->capacity);
727 
728 	// name may be freed before we make use of it
729 	char * name_cpy = kstrdup(name);
730 	assert(name_cpy);
731 	results->list[results->count++] =
732 	    (result_t){.ret = ret, .name = name_cpy};
733 }
734 
735 
736 #define TESTNAME_DELIMITER        "TESTNAME "
737 #define RESULTCOUNT_DELIMITER     "RESULT COUNT "
738 #define TESTRESULT_DELIMITER      " "
739 #define TESTCONFIG_DELIMITER      "  TESTCONFIG "
740 #define TRIALSFORMULA_DELIMITER   "TRIALSFORMULA "
741 #define TRIALSARGUMENTS_DELIMITER "TRIALSARGUMENTS"
742 #define KERN_TESTRESULT_DELIMITER "  RESULT "
743 #define KERN_FAILURE_DELIMITER    "FAIL: "
744 #define KERN_RESULT_DELIMITER     "\n"
745 
746 // print results, unformatted
747 // This output is read by populate_kernel_results()
748 // and by tools/format_vm_parameter_validation.py
749 static results_t *
__dump_results(results_t * results)750 __dump_results(results_t *results)
751 {
752 	testprintf(TESTNAME_DELIMITER "%s\n", results->testname);
753 	testprintf(RESULTCOUNT_DELIMITER "%d\n", results->count);
754 	testprintf(TESTCONFIG_DELIMITER "%s\n", results->testconfig);
755 
756 	for (unsigned i = 0; i < results->count; i++) {
757 		testprintf(KERN_TESTRESULT_DELIMITER "%d, %s\n", results->list[i].ret, results->list[i].name);
758 	}
759 
760 	results->tested_count += 1;
761 	return results;
762 }
763 
764 // This output is read by populate_golden_results()
765 static results_t *
dump_golden_results(results_t * results)766 dump_golden_results(results_t *results)
767 {
768 	trial_page_size = PAGE_SIZE;
769 	goldenprintf(TESTNAME_DELIMITER "%s\n", results->testname);
770 	goldenprintf(TRIALSFORMULA_DELIMITER "%s %s %llu,%llu,%llu\n",
771 	    trialsformula_name(results->trialsformula), TRIALSARGUMENTS_DELIMITER,
772 	    results->trialsargs[0], results->trialsargs[1], trial_page_size);
773 	goldenprintf(RESULTCOUNT_DELIMITER "%d\n", results->count);
774 
775 	for (unsigned i = 0; i < results->count; i++) {
776 		goldenprintf(TESTRESULT_DELIMITER "%d: %d\n", i, results->list[i].ret);
777 #if !KERNEL
778 		if (results->list[i].ret == OUT_PARAM_BAD) {
779 			extern int out_param_bad_count;
780 			out_param_bad_count += 1;
781 			T_FAIL("Out parameter violation in test %s - %s\n", results->testname, results->list[i].name);
782 		}
783 #endif
784 	}
785 
786 	return results;
787 }
788 
789 
790 static inline mach_vm_address_t
truncate_vm_map_addr_with_flags(MAP_T map,mach_vm_address_t addr,int flags)791 truncate_vm_map_addr_with_flags(MAP_T map, mach_vm_address_t addr, int flags)
792 {
793 	mach_vm_address_t truncated_addr = addr;
794 	if (flags & VM_FLAGS_RETURN_4K_DATA_ADDR) {
795 		// VM_FLAGS_RETURN_4K_DATA_ADDR means return a 4k aligned address rather than the
796 		// base of the page. Truncate to 4k.
797 		truncated_addr = trunc_down_page(addr, KB4);
798 	} else if (flags & VM_FLAGS_RETURN_DATA_ADDR) {
799 		// On VM_FLAGS_RETURN_DATA_ADDR, we expect to get back the unaligned address.
800 		// Don't truncate.
801 	} else {
802 		// Otherwise we truncate to the map page size
803 		truncated_addr = trunc_down_map(map, addr);
804 	}
805 	return truncated_addr;
806 }
807 
808 
809 static inline mach_vm_address_t
get_expected_remap_misalignment(MAP_T map,mach_vm_address_t addr,int flags)810 get_expected_remap_misalignment(MAP_T map, mach_vm_address_t addr, int flags)
811 {
812 	mach_vm_address_t misalignment;
813 	if (flags & VM_FLAGS_RETURN_4K_DATA_ADDR) {
814 		// VM_FLAGS_RETURN_4K_DATA_ADDR means return a 4k aligned address rather than the
815 		// base of the page. The misalignment is relative to the first 4k page
816 		misalignment = addr - trunc_down_page(addr, KB4);
817 	} else if (flags & VM_FLAGS_RETURN_DATA_ADDR) {
818 		// On VM_FLAGS_RETURN_DATA_ADDR, we expect to get back the unaligned address.
819 		// The misalignment is therefore the low bits
820 		misalignment = addr - trunc_down_map(map, addr);
821 	} else {
822 		// Otherwise we expect it to be aligned
823 		misalignment = 0;
824 	}
825 	return misalignment;
826 }
827 
828 // absolute and relative offsets are used to specify a trial's values
829 
830 typedef struct {
831 	bool is_absolute;
832 	addr_t offset;
833 } absolute_or_relative_offset_t;
834 
835 typedef struct offset_list_t {
836 	unsigned count;
837 	unsigned capacity;
838 	absolute_or_relative_offset_t list[];
839 } offset_list_t;
840 
841 static offset_list_t *
allocate_offsets(unsigned capacity)842 allocate_offsets(unsigned capacity)
843 {
844 	offset_list_t *offsets;
845 #if KERNEL
846 	offsets = kalloc_type(offset_list_t, absolute_or_relative_offset_t, capacity, Z_WAITOK | Z_ZERO);
847 #else
848 	offsets = calloc(sizeof(offset_list_t) + capacity * sizeof(absolute_or_relative_offset_t), 1);
849 #endif
850 	offsets->count = 0;
851 	offsets->capacity = capacity;
852 	return offsets;
853 }
854 
855 static void
append_offset(offset_list_t * offsets,bool is_absolute,addr_t offset)856 append_offset(offset_list_t *offsets, bool is_absolute, addr_t offset)
857 {
858 	assert(offsets->count < offsets->capacity);
859 	offsets->list[offsets->count].is_absolute = is_absolute;
860 	offsets->list[offsets->count].offset = offset;
861 	offsets->count++;
862 }
863 
864 #if KERNEL
865 
866 /* kernel globals are shared across processes, store cached offsets in thread-local storage */
867 #define CACHE_OFFSETS(name, ctor) \
868 	offset_list_t *name = get_globals()->name;              \
869 	do {                                                    \
870 	        if (name == NULL) {                             \
871 	                name = ctor();                          \
872 	                get_globals()->name = name;             \
873 	        }                                               \
874 	} while (0)
875 
876 #else   /* not KERNEL */
877 
878 /* userspace test is single-threaded, store cached offsets in a static variable */
879 #define CACHE_OFFSETS(name, ctor)               \
880 	static offset_list_t *name;             \
881 	do {                                    \
882 	        if (name == NULL) {             \
883 	                name = ctor();          \
884 	        }                               \
885 	} while (0)
886 
887 #endif  /* not KERNEL */
888 
889 
890 /////////////////////////////////////////////////////
891 // Generation of trials and their parameter values
892 // A "trial" is a single execution of a function to be tested
893 
894 #if KERNEL
895 #define ALLOC_TRIALS(NAME, new_capacity)                                \
896 	(NAME ## _trials_t *)kalloc_type(NAME ## _trials_t, NAME ## _trial_t, \
897 	                                 new_capacity, Z_WAITOK | Z_ZERO)
898 #define FREE_TRIALS(NAME, trials)                                       \
899 	kfree_type(NAME ## _trials_t, NAME ## _trial_t, trials->capacity, trials)
900 #else
901 #define ALLOC_TRIALS(NAME, new_capacity)                                \
902 	(NAME ## _trials_t *)calloc(sizeof(NAME ## _trials_t) + (new_capacity) * sizeof(NAME ## _trial_t), 1)
903 #define FREE_TRIALS(NAME, trials)               \
904 	free(trials)
905 #endif
906 
907 #define TRIALS_IMPL(NAME)                                               \
908 	static NAME ## _trials_t *                                      \
909 	__attribute__((used))                                       \
910 	allocate_ ## NAME ## _trials(unsigned capacity)                 \
911 	{                                                               \
912 	        NAME ## _trials_t *trials = ALLOC_TRIALS(NAME, capacity); \
913 	        assert(trials);                                         \
914 	        trials->count = 0;                                      \
915 	        trials->capacity = capacity;                            \
916 	        return trials;                                          \
917 	}                                                               \
918                                                                         \
919 	static void __attribute__((overloadable, used))                 \
920 	free_trials(NAME ## _trials_t *trials)                          \
921 	{                                                               \
922 	        FREE_TRIALS(NAME, trials);                              \
923 	}                                                               \
924                                                                         \
925 	static void __attribute__((overloadable, used))                 \
926 	append_trial(NAME ## _trials_t *trials, NAME ## _trial_t new_trial) \
927 	{                                                               \
928 	        assert(trials->count < trials->capacity);               \
929 	        trials->list[trials->count++] = new_trial;              \
930 	}                                                               \
931                                                                         \
932 	static void __attribute__((overloadable, used))                 \
933 	append_trials(NAME ## _trials_t *trials, NAME ## _trial_t *new_trials, unsigned new_count) \
934 	{                                                               \
935 	        for (unsigned i = 0; i < new_count; i++) {              \
936 	                append_trial(trials, new_trials[i]);            \
937 	        }                                                       \
938 	}
939 
940 // allocate vm_inherit_t trials, and deallocate it at end of scope
941 #define SMART_VM_INHERIT_TRIALS()                                               \
942 	__attribute__((cleanup(cleanup_vm_inherit_trials)))             \
943 	= allocate_vm_inherit_trials(countof(vm_inherit_trials_values));        \
944 	append_trials(trials, vm_inherit_trials_values, countof(vm_inherit_trials_values))
945 
946 // generate vm_inherit_t trials
947 
948 typedef struct {
949 	vm_inherit_t value;
950 	const char * name;
951 } vm_inherit_trial_t;
952 
953 typedef struct {
954 	unsigned count;
955 	unsigned capacity;
956 	vm_inherit_trial_t list[];
957 } vm_inherit_trials_t;
958 
959 
960 #define VM_INHERIT_TRIAL(new_value) \
961 	(vm_inherit_trial_t) {.value = (vm_inherit_t)(new_value), .name = "vm_inherit " #new_value}
962 
963 static_assert(VM_INHERIT_LAST_VALID == VM_INHERIT_NONE,
964     "Update this test with new vm_inherit_t values");
965 static vm_inherit_trial_t vm_inherit_trials_values[] = {
966 	VM_INHERIT_TRIAL(VM_INHERIT_SHARE),
967 	VM_INHERIT_TRIAL(VM_INHERIT_COPY),
968 	VM_INHERIT_TRIAL(VM_INHERIT_NONE),
969 	// end valid ones
970 	// note: VM_INHERIT_DONATE_COPY is invalid and unimplemented
971 	// VM_INHERIT_LAST_VALID correctly excludes VM_INHERIT_DONATE_COPY
972 	VM_INHERIT_TRIAL(VM_INHERIT_LAST_VALID + 1),
973 	VM_INHERIT_TRIAL(VM_INHERIT_LAST_VALID + 2),
974 	VM_INHERIT_TRIAL(0xffffffff),
975 };
976 
TRIALS_IMPL(vm_inherit)977 TRIALS_IMPL(vm_inherit)
978 
979 static void
980 cleanup_vm_inherit_trials(vm_inherit_trials_t **trials)
981 {
982 	free_trials(*trials);
983 }
984 
985 // allocate vm_behavior_t trials, and deallocate it at end of scope
986 #define SMART_VM_BEHAVIOR_TRIALS()                                               \
987 	__attribute__((cleanup(cleanup_vm_behavior_trials)))             \
988 	= allocate_vm_behavior_trials(countof(vm_behavior_trials_values));        \
989 	append_trials(trials, vm_behavior_trials_values, countof(vm_behavior_trials_values))
990 
991 // generate vm_behavior_t trials
992 
993 typedef struct {
994 	vm_behavior_t value;
995 	const char * name;
996 } vm_behavior_trial_t;
997 
998 typedef struct {
999 	unsigned count;
1000 	unsigned capacity;
1001 	vm_behavior_trial_t list[];
1002 } vm_behavior_trials_t;
1003 
1004 
1005 #define VM_BEHAVIOR_TRIAL(new_value) \
1006 	(vm_behavior_trial_t) {.value = (vm_behavior_t)(new_value), .name = "vm_behavior " #new_value}
1007 
1008 static vm_behavior_trial_t vm_behavior_trials_values[] = {
1009 	VM_BEHAVIOR_TRIAL(VM_BEHAVIOR_DEFAULT),
1010 	VM_BEHAVIOR_TRIAL(VM_BEHAVIOR_RANDOM),
1011 	VM_BEHAVIOR_TRIAL(VM_BEHAVIOR_SEQUENTIAL),
1012 	VM_BEHAVIOR_TRIAL(VM_BEHAVIOR_RSEQNTL),
1013 	VM_BEHAVIOR_TRIAL(VM_BEHAVIOR_WILLNEED),
1014 	VM_BEHAVIOR_TRIAL(VM_BEHAVIOR_DONTNEED),
1015 	VM_BEHAVIOR_TRIAL(VM_BEHAVIOR_FREE),
1016 	VM_BEHAVIOR_TRIAL(VM_BEHAVIOR_ZERO_WIRED_PAGES),
1017 	VM_BEHAVIOR_TRIAL(VM_BEHAVIOR_REUSABLE),
1018 	VM_BEHAVIOR_TRIAL(VM_BEHAVIOR_REUSE),
1019 	VM_BEHAVIOR_TRIAL(VM_BEHAVIOR_CAN_REUSE),
1020 	VM_BEHAVIOR_TRIAL(VM_BEHAVIOR_PAGEOUT),
1021 	VM_BEHAVIOR_TRIAL(VM_BEHAVIOR_ZERO),
1022 	// end valid ones
1023 	VM_BEHAVIOR_TRIAL(VM_BEHAVIOR_LAST_VALID + 1),
1024 	VM_BEHAVIOR_TRIAL(VM_BEHAVIOR_LAST_VALID + 2),
1025 	VM_BEHAVIOR_TRIAL(0x12345),
1026 	VM_BEHAVIOR_TRIAL(0xffffffff),
1027 };
1028 
TRIALS_IMPL(vm_behavior)1029 TRIALS_IMPL(vm_behavior)
1030 
1031 static void
1032 cleanup_vm_behavior_trials(vm_behavior_trials_t **trials)
1033 {
1034 	free_trials(*trials);
1035 }
1036 
1037 // allocate vm_sync_t trials, and deallocate it at end of scope
1038 #define SMART_VM_SYNC_TRIALS()                                               \
1039 	__attribute__((cleanup(cleanup_vm_sync_trials)))             \
1040 	= allocate_vm_sync_trials(countof(vm_sync_trials_values));        \
1041 	append_trials(trials, vm_sync_trials_values, countof(vm_sync_trials_values))
1042 
1043 // generate vm_sync_t trials
1044 
1045 typedef struct {
1046 	vm_sync_t value;
1047 	const char * name;
1048 } vm_sync_trial_t;
1049 
1050 typedef struct {
1051 	unsigned count;
1052 	unsigned capacity;
1053 	vm_sync_trial_t list[];
1054 } vm_sync_trials_t;
1055 
1056 
1057 #define VM_SYNC_TRIAL(new_value) \
1058 	(vm_sync_trial_t) {.value = (vm_sync_t)(new_value), .name = "vm_sync_t " #new_value}
1059 
1060 static vm_sync_trial_t vm_sync_trials_values[] = {
1061 	VM_SYNC_TRIAL(0),
1062 	// start valid values
1063 	VM_SYNC_TRIAL(VM_SYNC_ASYNCHRONOUS),
1064 	VM_SYNC_TRIAL(VM_SYNC_SYNCHRONOUS),
1065 	VM_SYNC_TRIAL(VM_SYNC_INVALIDATE),
1066 	VM_SYNC_TRIAL(VM_SYNC_KILLPAGES),
1067 	VM_SYNC_TRIAL(VM_SYNC_DEACTIVATE),
1068 	VM_SYNC_TRIAL(VM_SYNC_CONTIGUOUS),
1069 	VM_SYNC_TRIAL(VM_SYNC_REUSABLEPAGES),
1070 	// end valid values
1071 	VM_SYNC_TRIAL(1u << 7),
1072 	VM_SYNC_TRIAL(1u << 8),
1073 	VM_SYNC_TRIAL(1u << 9),
1074 	VM_SYNC_TRIAL(1u << 10),
1075 	VM_SYNC_TRIAL(1u << 11),
1076 	VM_SYNC_TRIAL(1u << 12),
1077 	VM_SYNC_TRIAL(1u << 13),
1078 	VM_SYNC_TRIAL(1u << 14),
1079 	VM_SYNC_TRIAL(1u << 15),
1080 	VM_SYNC_TRIAL(1u << 16),
1081 	VM_SYNC_TRIAL(1u << 17),
1082 	VM_SYNC_TRIAL(1u << 18),
1083 	VM_SYNC_TRIAL(1u << 19),
1084 	VM_SYNC_TRIAL(1u << 20),
1085 	VM_SYNC_TRIAL(1u << 21),
1086 	VM_SYNC_TRIAL(1u << 22),
1087 	VM_SYNC_TRIAL(1u << 23),
1088 	VM_SYNC_TRIAL(1u << 24),
1089 	VM_SYNC_TRIAL(1u << 25),
1090 	VM_SYNC_TRIAL(1u << 26),
1091 	VM_SYNC_TRIAL(1u << 27),
1092 	VM_SYNC_TRIAL(1u << 28),
1093 	VM_SYNC_TRIAL(1u << 29),
1094 	VM_SYNC_TRIAL(1u << 30),
1095 	VM_SYNC_TRIAL(1u << 31),
1096 	VM_SYNC_TRIAL(VM_SYNC_ASYNCHRONOUS | VM_SYNC_SYNCHRONOUS),
1097 	VM_SYNC_TRIAL(VM_SYNC_ASYNCHRONOUS | (1u << 7)),
1098 	VM_SYNC_TRIAL(0xffffffff),
1099 };
1100 
TRIALS_IMPL(vm_sync)1101 TRIALS_IMPL(vm_sync)
1102 
1103 static void
1104 cleanup_vm_sync_trials(vm_sync_trials_t **trials)
1105 {
1106 	free_trials(*trials);
1107 }
1108 
1109 // allocate vm_msync_t trials, and deallocate it at end of scope
1110 #define SMART_VM_MSYNC_TRIALS()                                               \
1111 	__attribute__((cleanup(cleanup_vm_msync_trials)))             \
1112 	= allocate_vm_msync_trials(countof(vm_msync_trials_values));        \
1113 	append_trials(trials, vm_msync_trials_values, countof(vm_msync_trials_values))
1114 
1115 // generate vm_msync_t trials
1116 
1117 typedef struct {
1118 	int value;
1119 	const char * name;
1120 } vm_msync_trial_t;
1121 
1122 typedef struct {
1123 	unsigned count;
1124 	unsigned capacity;
1125 	vm_msync_trial_t list[];
1126 } vm_msync_trials_t;
1127 
1128 
1129 #define VM_MSYNC_TRIAL(new_value) \
1130 	(vm_msync_trial_t) {.value = (int)(new_value), .name = "vm_msync_t " #new_value}
1131 
1132 static vm_msync_trial_t vm_msync_trials_values[] = {
1133 	VM_MSYNC_TRIAL(0),
1134 	// start valid values
1135 	VM_MSYNC_TRIAL(MS_ASYNC),
1136 	VM_MSYNC_TRIAL(MS_INVALIDATE),
1137 	VM_MSYNC_TRIAL(MS_KILLPAGES),
1138 	VM_MSYNC_TRIAL(MS_DEACTIVATE),
1139 	VM_MSYNC_TRIAL(MS_SYNC),
1140 	VM_MSYNC_TRIAL(MS_ASYNC | MS_INVALIDATE),
1141 	// end valid values
1142 	VM_MSYNC_TRIAL(1u << 5),
1143 	VM_MSYNC_TRIAL(1u << 6),
1144 	VM_MSYNC_TRIAL(1u << 7),
1145 	VM_MSYNC_TRIAL(1u << 8),
1146 	VM_MSYNC_TRIAL(1u << 9),
1147 	VM_MSYNC_TRIAL(1u << 10),
1148 	VM_MSYNC_TRIAL(1u << 11),
1149 	VM_MSYNC_TRIAL(1u << 12),
1150 	VM_MSYNC_TRIAL(1u << 13),
1151 	VM_MSYNC_TRIAL(1u << 14),
1152 	VM_MSYNC_TRIAL(1u << 15),
1153 	VM_MSYNC_TRIAL(1u << 16),
1154 	VM_MSYNC_TRIAL(1u << 17),
1155 	VM_MSYNC_TRIAL(1u << 18),
1156 	VM_MSYNC_TRIAL(1u << 19),
1157 	VM_MSYNC_TRIAL(1u << 20),
1158 	VM_MSYNC_TRIAL(1u << 21),
1159 	VM_MSYNC_TRIAL(1u << 22),
1160 	VM_MSYNC_TRIAL(1u << 23),
1161 	VM_MSYNC_TRIAL(1u << 24),
1162 	VM_MSYNC_TRIAL(1u << 25),
1163 	VM_MSYNC_TRIAL(1u << 26),
1164 	VM_MSYNC_TRIAL(1u << 27),
1165 	VM_MSYNC_TRIAL(1u << 28),
1166 	VM_MSYNC_TRIAL(1u << 29),
1167 	VM_MSYNC_TRIAL(1u << 30),
1168 	VM_MSYNC_TRIAL(1u << 31),
1169 	VM_MSYNC_TRIAL(MS_ASYNC | MS_SYNC),
1170 	VM_MSYNC_TRIAL(0xffffffff),
1171 };
1172 
TRIALS_IMPL(vm_msync)1173 TRIALS_IMPL(vm_msync)
1174 
1175 static void __attribute__((used))
1176 cleanup_vm_msync_trials(vm_msync_trials_t **trials)
1177 {
1178 	free_trials(*trials);
1179 }
1180 
1181 
1182 // allocate advise_t trials, and deallocate it at end of scope
1183 #define SMART_VM_ADVISE_TRIALS()                                           \
1184 	__attribute__((cleanup(cleanup_advise_trials)))                 \
1185 	= allocate_vm_advise_trials(countof(vm_advise_trials_values));        \
1186 	append_trials(trials, vm_advise_trials_values, countof(vm_advise_trials_values))
1187 
1188 // generate advise_t trials
1189 
1190 typedef struct {
1191 	int value;
1192 	const char * name;
1193 } vm_advise_trial_t;
1194 
1195 typedef struct {
1196 	unsigned count;
1197 	unsigned capacity;
1198 	vm_advise_trial_t list[];
1199 } vm_advise_trials_t;
1200 
1201 
1202 #define ADVISE_TRIAL(new_value) \
1203 	(vm_advise_trial_t) {.value = (int)(new_value), .name = "advise " #new_value}
1204 
1205 static vm_advise_trial_t vm_advise_trials_values[] = {
1206 	ADVISE_TRIAL(MADV_NORMAL),
1207 	ADVISE_TRIAL(MADV_RANDOM),
1208 	ADVISE_TRIAL(MADV_SEQUENTIAL),
1209 	ADVISE_TRIAL(MADV_WILLNEED),
1210 	ADVISE_TRIAL(MADV_DONTNEED),
1211 	ADVISE_TRIAL(MADV_FREE),
1212 	ADVISE_TRIAL(MADV_ZERO_WIRED_PAGES),
1213 	ADVISE_TRIAL(MADV_FREE_REUSABLE),
1214 	ADVISE_TRIAL(MADV_FREE_REUSE),
1215 	ADVISE_TRIAL(MADV_CAN_REUSE),
1216 	ADVISE_TRIAL(MADV_PAGEOUT),
1217 	ADVISE_TRIAL(MADV_ZERO),
1218 	// end valid ones
1219 	ADVISE_TRIAL(MADV_ZERO + 1),
1220 	ADVISE_TRIAL(MADV_ZERO + 2),
1221 	ADVISE_TRIAL(0xffffffff),
1222 };
1223 
TRIALS_IMPL(vm_advise)1224 TRIALS_IMPL(vm_advise)
1225 
1226 static void __attribute__((used))
1227 cleanup_advise_trials(vm_advise_trials_t **trials)
1228 {
1229 	free_trials(*trials);
1230 }
1231 
1232 // allocate machine_attribute_t trials, and deallocate it at end of scope
1233 #define SMART_VM_MACHINE_ATTRIBUTE_TRIALS()                                           \
1234 	__attribute__((cleanup(cleanup_vm_machine_attribute_trials)))                 \
1235 	= allocate_vm_machine_attribute_trials(countof(vm_machine_attribute_trials_values));        \
1236 	append_trials(trials, vm_machine_attribute_trials_values, countof(vm_machine_attribute_trials_values))
1237 
1238 // generate advise_t trials
1239 
1240 typedef struct {
1241 	vm_machine_attribute_t value;
1242 	const char * name;
1243 } vm_machine_attribute_trial_t;
1244 
1245 typedef struct {
1246 	unsigned count;
1247 	unsigned capacity;
1248 	vm_machine_attribute_trial_t list[];
1249 } vm_machine_attribute_trials_t;
1250 
1251 
1252 #define VM_MACHINE_ATTRIBUTE_TRIAL(new_value) \
1253 	(vm_machine_attribute_trial_t) {.value = (vm_machine_attribute_t)(new_value), .name = "vm_machine_attribute_t " #new_value}
1254 
1255 static vm_machine_attribute_trial_t vm_machine_attribute_trials_values[] = {
1256 	VM_MACHINE_ATTRIBUTE_TRIAL(0),
1257 	// start valid ones
1258 	VM_MACHINE_ATTRIBUTE_TRIAL(MATTR_CACHE),
1259 	VM_MACHINE_ATTRIBUTE_TRIAL(MATTR_MIGRATE),
1260 	VM_MACHINE_ATTRIBUTE_TRIAL(MATTR_REPLICATE),
1261 	// end valid ones
1262 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 3),
1263 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 4),
1264 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 5),
1265 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 6),
1266 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 7),
1267 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 8),
1268 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 9),
1269 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 10),
1270 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 11),
1271 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 12),
1272 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 13),
1273 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 14),
1274 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 15),
1275 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 16),
1276 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 17),
1277 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 18),
1278 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 19),
1279 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 20),
1280 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 21),
1281 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 22),
1282 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 23),
1283 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 24),
1284 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 25),
1285 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 26),
1286 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 27),
1287 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 28),
1288 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 29),
1289 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 30),
1290 	VM_MACHINE_ATTRIBUTE_TRIAL(1u << 31),
1291 };
1292 
TRIALS_IMPL(vm_machine_attribute)1293 TRIALS_IMPL(vm_machine_attribute)
1294 
1295 static void
1296 cleanup_vm_machine_attribute_trials(vm_machine_attribute_trials_t **trials)
1297 {
1298 	free_trials(*trials);
1299 }
1300 
1301 // allocate vm_map_kernel_flags trials, and deallocate it at end of scope
1302 #define SMART_VM_MAP_KERNEL_FLAGS_TRIALS()                              \
1303 	__attribute__((cleanup(cleanup_vm_map_kernel_flags_trials)))    \
1304 	= generate_vm_map_kernel_flags_trials()
1305 
1306 
1307 // generate vm_map_kernel_flags_t trials
1308 
1309 typedef struct {
1310 	int flags;
1311 	char * name;
1312 } vm_map_kernel_flags_trial_t;
1313 
1314 typedef struct {
1315 	unsigned count;
1316 	unsigned capacity;
1317 	vm_map_kernel_flags_trial_t list[];
1318 } vm_map_kernel_flags_trials_t;
1319 
1320 #define VM_MAP_KERNEL_FLAGS_TRIAL(new_flags) \
1321 	(vm_map_kernel_flags_trial_t) {.flags = (int)(new_flags), .name ="vm_map_kernel_flags " #new_flags}
1322 
TRIALS_IMPL(vm_map_kernel_flags)1323 TRIALS_IMPL(vm_map_kernel_flags)
1324 
1325 static vm_map_kernel_flags_trials_t *
1326 generate_prefixed_vm_map_kernel_flags_trials(int prefix_flags, const char *prefix_name)
1327 {
1328 	vm_map_kernel_flags_trials_t *trials;
1329 	trials = allocate_vm_map_kernel_flags_trials(32);
1330 
1331 	char *str;
1332 #define APPEND(flag)                                                    \
1333 	({                                                              \
1334 	        kasprintf(&str, "vm_map_kernel_flags %s%s%s", \
1335 	            prefix_name, prefix_flags == 0 ? "" : " | ", #flag); \
1336 	        append_trial(trials, (vm_map_kernel_flags_trial_t){ prefix_flags | (int)flag, str }); \
1337 	})
1338 
1339 	// First trial is just the prefix flags set, if any.
1340 	// (either ANYWHERE or FIXED | OVERWRITE)
1341 	if (prefix_flags != 0) {
1342 		kasprintf(&str, "vm_map_kernel_flags %s", prefix_name);
1343 		append_trial(trials, (vm_map_kernel_flags_trial_t){ prefix_flags, str });
1344 	}
1345 
1346 	// Try each other flag with the prefix flags.
1347 	// Skip FIXED and ANYWHERE and OVERWRITE because they cause
1348 	// memory management changes that the caller may not be prepared for.
1349 	// skip 0x00000000 VM_FLAGS_FIXED
1350 	// skip 0x00000001 VM_FLAGS_ANYWHERE
1351 	APPEND(VM_FLAGS_PURGABLE);
1352 	APPEND(VM_FLAGS_4GB_CHUNK);
1353 	APPEND(VM_FLAGS_RANDOM_ADDR);
1354 	APPEND(VM_FLAGS_NO_CACHE);
1355 	APPEND(VM_FLAGS_RESILIENT_CODESIGN);
1356 	APPEND(VM_FLAGS_RESILIENT_MEDIA);
1357 	APPEND(VM_FLAGS_PERMANENT);
1358 	// skip 0x00001000 VM_FLAGS_TPRO; it only works on some hardware.
1359 	APPEND(0x00002000);
1360 	// skip 0x00004000 VM_FLAGS_OVERWRITE
1361 	APPEND(0x00008000);
1362 	APPEND(VM_FLAGS_SUPERPAGE_MASK); // 0x10000, 0x20000, 0x40000
1363 	APPEND(0x00080000);
1364 	APPEND(VM_FLAGS_RETURN_DATA_ADDR);
1365 	APPEND(VM_FLAGS_RETURN_4K_DATA_ADDR);
1366 	APPEND(VM_FLAGS_ALIAS_MASK);
1367 
1368 	return trials;
1369 }
1370 
1371 static vm_map_kernel_flags_trials_t *
generate_vm_map_kernel_flags_trials()1372 generate_vm_map_kernel_flags_trials()
1373 {
1374 	vm_map_kernel_flags_trials_t *fixed =
1375 	    generate_prefixed_vm_map_kernel_flags_trials(
1376 		VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, "VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE");
1377 	vm_map_kernel_flags_trials_t *anywhere =
1378 	    generate_prefixed_vm_map_kernel_flags_trials(
1379 		VM_FLAGS_ANYWHERE, "VM_FLAGS_ANYWHERE");
1380 	vm_map_kernel_flags_trials_t *trials =
1381 	    allocate_vm_map_kernel_flags_trials(fixed->count + anywhere->count);
1382 	append_trials(trials, fixed->list, fixed->count);
1383 	append_trials(trials, anywhere->list, anywhere->count);
1384 
1385 	// free not cleanup, trials has stolen their strings
1386 	free_trials(fixed);
1387 	free_trials(anywhere);
1388 
1389 	return trials;
1390 }
1391 
1392 static void
cleanup_vm_map_kernel_flags_trials(vm_map_kernel_flags_trials_t ** trials)1393 cleanup_vm_map_kernel_flags_trials(vm_map_kernel_flags_trials_t **trials)
1394 {
1395 	for (size_t i = 0; i < (*trials)->count; i++) {
1396 		kfree_str((*trials)->list[i].name);
1397 	}
1398 	free_trials(*trials);
1399 }
1400 
1401 
1402 // generate mmap flags trials
1403 
1404 typedef struct {
1405 	int flags;
1406 	const char *name;
1407 } mmap_flags_trial_t;
1408 
1409 typedef struct {
1410 	unsigned count;
1411 	unsigned capacity;
1412 	mmap_flags_trial_t list[];
1413 } mmap_flags_trials_t;
1414 
1415 #define MMAP_FLAGS_TRIAL(new_flags)                                             \
1416 	(mmap_flags_trial_t){ .flags = (int)(new_flags), .name = "mmap flags "#new_flags }
1417 
1418 static mmap_flags_trial_t mmap_flags_trials_values[] = {
1419 	MMAP_FLAGS_TRIAL(MAP_FILE),
1420 	MMAP_FLAGS_TRIAL(MAP_ANON),
1421 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_SHARED),
1422 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE),
1423 	MMAP_FLAGS_TRIAL(MAP_ANON | MAP_SHARED),
1424 	MMAP_FLAGS_TRIAL(MAP_ANON | MAP_PRIVATE),
1425 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_SHARED | MAP_PRIVATE),
1426 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | MAP_FIXED),
1427 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | MAP_RENAME),
1428 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | MAP_NORESERVE),
1429 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | MAP_RESERVED0080),
1430 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | MAP_NOEXTEND),
1431 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | MAP_HASSEMAPHORE),
1432 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | MAP_NOCACHE),
1433 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | MAP_JIT),
1434 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | MAP_RESILIENT_CODESIGN),
1435 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | MAP_RESILIENT_MEDIA),
1436 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | MAP_TRANSLATED_ALLOW_EXECUTE),
1437 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | MAP_UNIX03),
1438 	// skip MAP_TPRO; it only works on some hardware
1439 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 3),
1440 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 4),
1441 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 5),
1442 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 6),
1443 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 7),
1444 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 8),
1445 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 9),
1446 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 10),
1447 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 11),
1448 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 12),
1449 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 13),
1450 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 14),
1451 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 15),
1452 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 16),
1453 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 17),
1454 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 18),
1455 	// skip MAP_TPRO (1<<19); it only works on some hardware
1456 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 20),
1457 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 21),
1458 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 22),
1459 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 23),
1460 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 24),
1461 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 25),
1462 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 26),
1463 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 27),
1464 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 28),
1465 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 29),
1466 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 30),
1467 	MMAP_FLAGS_TRIAL(MAP_FILE | MAP_PRIVATE | 1u << 31),
1468 };
1469 
TRIALS_IMPL(mmap_flags)1470 TRIALS_IMPL(mmap_flags)
1471 
1472 static void
1473 cleanup_mmap_flags_trials(mmap_flags_trials_t **trials)
1474 {
1475 	free_trials(*trials);
1476 }
1477 
1478 // allocate mmap_flag trials, and deallocate it at end of scope
1479 #define SMART_MMAP_FLAGS_TRIALS()                                               \
1480 	__attribute__((cleanup(cleanup_mmap_flags_trials)))             \
1481 	= allocate_mmap_flags_trials(countof(mmap_flags_trials_values));        \
1482 	append_trials(trials, mmap_flags_trials_values, countof(mmap_flags_trials_values))
1483 
1484 // generate generic flag trials
1485 
1486 typedef struct {
1487 	int flag;
1488 	const char *name;
1489 } generic_flag_trial_t;
1490 
1491 typedef struct {
1492 	unsigned count;
1493 	unsigned capacity;
1494 	generic_flag_trial_t list[];
1495 } generic_flag_trials_t;
1496 
1497 #define GENERIC_FLAG_TRIAL(new_flag)                                            \
1498 	(generic_flag_trial_t){ .flag = (int)(new_flag), .name = "generic flag "#new_flag }
1499 
1500 static generic_flag_trial_t generic_flag_trials_values[] = {
1501 	GENERIC_FLAG_TRIAL(0),
1502 	GENERIC_FLAG_TRIAL(1),
1503 	GENERIC_FLAG_TRIAL(2),
1504 	GENERIC_FLAG_TRIAL(3),
1505 	GENERIC_FLAG_TRIAL(4),
1506 	GENERIC_FLAG_TRIAL(5),
1507 	GENERIC_FLAG_TRIAL(6),
1508 	GENERIC_FLAG_TRIAL(7),
1509 	GENERIC_FLAG_TRIAL(1u << 3),
1510 	GENERIC_FLAG_TRIAL(1u << 4),
1511 	GENERIC_FLAG_TRIAL(1u << 5),
1512 	GENERIC_FLAG_TRIAL(1u << 6),
1513 	GENERIC_FLAG_TRIAL(1u << 7),
1514 	GENERIC_FLAG_TRIAL(1u << 8),
1515 	GENERIC_FLAG_TRIAL(1u << 9),
1516 	GENERIC_FLAG_TRIAL(1u << 10),
1517 	GENERIC_FLAG_TRIAL(1u << 11),
1518 	GENERIC_FLAG_TRIAL(1u << 12),
1519 	GENERIC_FLAG_TRIAL(1u << 13),
1520 	GENERIC_FLAG_TRIAL(1u << 14),
1521 	GENERIC_FLAG_TRIAL(1u << 15),
1522 	GENERIC_FLAG_TRIAL(1u << 16),
1523 	GENERIC_FLAG_TRIAL(1u << 17),
1524 	GENERIC_FLAG_TRIAL(1u << 18),
1525 	GENERIC_FLAG_TRIAL(1u << 19),
1526 	GENERIC_FLAG_TRIAL(1u << 20),
1527 	GENERIC_FLAG_TRIAL(1u << 21),
1528 	GENERIC_FLAG_TRIAL(1u << 22),
1529 	GENERIC_FLAG_TRIAL(1u << 23),
1530 	GENERIC_FLAG_TRIAL(1u << 24),
1531 	GENERIC_FLAG_TRIAL(1u << 25),
1532 	GENERIC_FLAG_TRIAL(1u << 26),
1533 	GENERIC_FLAG_TRIAL(1u << 27),
1534 	GENERIC_FLAG_TRIAL(1u << 28),
1535 	GENERIC_FLAG_TRIAL(1u << 29),
1536 	GENERIC_FLAG_TRIAL(1u << 30),
1537 	GENERIC_FLAG_TRIAL(1u << 31),
1538 };
1539 
TRIALS_IMPL(generic_flag)1540 TRIALS_IMPL(generic_flag)
1541 
1542 static void
1543 cleanup_generic_flag_trials(generic_flag_trials_t **trials)
1544 {
1545 	free_trials(*trials);
1546 }
1547 
1548 // allocate mmap_flag trials, and deallocate it at end of scope
1549 #define SMART_GENERIC_FLAG_TRIALS()                                             \
1550 	__attribute__((cleanup(cleanup_generic_flag_trials)))           \
1551 	= allocate_generic_flag_trials(countof(generic_flag_trials_values));    \
1552 	append_trials(trials, generic_flag_trials_values, countof(generic_flag_trials_values))
1553 
1554 
1555 // generate vm_prot_t trials
1556 
1557 #ifndef KERNEL
1558 typedef int vm_tag_t;
1559 #endif /* KERNEL */
1560 
1561 typedef struct {
1562 	vm_tag_t tag;
1563 	const char *name;
1564 } vm_tag_trial_t;
1565 
1566 typedef struct {
1567 	unsigned count;
1568 	unsigned capacity;
1569 	vm_tag_trial_t list[];
1570 } vm_tag_trials_t;
1571 
1572 #if KERNEL
1573 #define KERNEL_VM_TAG_TRIAL(new_tag)     \
1574 	(vm_tag_trial_t){ .tag = (vm_tag_t)(new_tag), .name = "vm_tag "#new_tag }
1575 
1576 #define VM_TAG_TRIAL KERNEL_VM_TAG_TRIAL
1577 #else
1578 #define USER_VM_TAG_TRIAL(new_tag)      \
1579 	(vm_tag_trial_t){ .tag = (vm_tag_t)0, .name = "vm_tag "#new_tag }
1580 
1581 #define VM_TAG_TRIAL USER_VM_TAG_TRIAL
1582 #endif
1583 
1584 static vm_tag_trial_t vm_tag_trials_values[] = {
1585 	VM_TAG_TRIAL(VM_KERN_MEMORY_NONE),
1586 	VM_TAG_TRIAL(VM_KERN_MEMORY_OSFMK),
1587 	VM_TAG_TRIAL(VM_KERN_MEMORY_BSD),
1588 	VM_TAG_TRIAL(VM_KERN_MEMORY_IOKIT),
1589 	VM_TAG_TRIAL(VM_KERN_MEMORY_LIBKERN),
1590 	VM_TAG_TRIAL(VM_KERN_MEMORY_OSKEXT),
1591 	VM_TAG_TRIAL(VM_KERN_MEMORY_KEXT),
1592 	VM_TAG_TRIAL(VM_KERN_MEMORY_IPC),
1593 	VM_TAG_TRIAL(VM_KERN_MEMORY_STACK),
1594 	VM_TAG_TRIAL(VM_KERN_MEMORY_CPU),
1595 	VM_TAG_TRIAL(VM_KERN_MEMORY_PMAP),
1596 	VM_TAG_TRIAL(VM_KERN_MEMORY_PTE),
1597 	VM_TAG_TRIAL(VM_KERN_MEMORY_ZONE),
1598 	VM_TAG_TRIAL(VM_KERN_MEMORY_KALLOC),
1599 	VM_TAG_TRIAL(VM_KERN_MEMORY_COMPRESSOR),
1600 	VM_TAG_TRIAL(VM_KERN_MEMORY_COMPRESSED_DATA),
1601 	VM_TAG_TRIAL(VM_KERN_MEMORY_PHANTOM_CACHE),
1602 	VM_TAG_TRIAL(VM_KERN_MEMORY_WAITQ),
1603 	VM_TAG_TRIAL(VM_KERN_MEMORY_DIAG),
1604 	VM_TAG_TRIAL(VM_KERN_MEMORY_LOG),
1605 	VM_TAG_TRIAL(VM_KERN_MEMORY_FILE),
1606 	VM_TAG_TRIAL(VM_KERN_MEMORY_MBUF),
1607 	VM_TAG_TRIAL(VM_KERN_MEMORY_UBC),
1608 	VM_TAG_TRIAL(VM_KERN_MEMORY_SECURITY),
1609 	VM_TAG_TRIAL(VM_KERN_MEMORY_MLOCK),
1610 	VM_TAG_TRIAL(VM_KERN_MEMORY_REASON),
1611 	VM_TAG_TRIAL(VM_KERN_MEMORY_SKYWALK),
1612 	VM_TAG_TRIAL(VM_KERN_MEMORY_LTABLE),
1613 	VM_TAG_TRIAL(VM_KERN_MEMORY_HV),
1614 	VM_TAG_TRIAL(VM_KERN_MEMORY_KALLOC_DATA),
1615 	VM_TAG_TRIAL(VM_KERN_MEMORY_RETIRED),
1616 	VM_TAG_TRIAL(VM_KERN_MEMORY_KALLOC_TYPE),
1617 	VM_TAG_TRIAL(VM_KERN_MEMORY_TRIAGE),
1618 	VM_TAG_TRIAL(VM_KERN_MEMORY_RECOUNT),
1619 };
1620 
TRIALS_IMPL(vm_tag)1621 TRIALS_IMPL(vm_tag)
1622 
1623 static void
1624 cleanup_vm_tag_trials(vm_tag_trials_t **trials)
1625 {
1626 	free_trials(*trials);
1627 }
1628 
1629 #define SMART_VM_TAG_TRIALS()                                           \
1630 	__attribute__((cleanup(cleanup_vm_tag_trials)))         \
1631 	= allocate_vm_tag_trials(countof(vm_tag_trials_values));        \
1632 	append_trials(trials, vm_tag_trials_values, countof(vm_tag_trials_values))
1633 
1634 //END vm_tag_t
1635 
1636 // generate vm_prot_t trials
1637 
1638 typedef struct {
1639 	vm_prot_t prot;
1640 	const char *name;
1641 } vm_prot_trial_t;
1642 
1643 typedef struct {
1644 	unsigned count;
1645 	unsigned capacity;
1646 	vm_prot_trial_t list[];
1647 } vm_prot_trials_t;
1648 
1649 #define VM_PROT_TRIAL(new_prot)                                         \
1650 	(vm_prot_trial_t){ .prot = (vm_prot_t)(new_prot), .name = "vm_prot "#new_prot }
1651 
1652 static vm_prot_trial_t vm_prot_trials_values[] = {
1653 	// none
1654 	VM_PROT_TRIAL(VM_PROT_NONE),
1655 	// ordinary r-- / rw- / r-x
1656 	VM_PROT_TRIAL(VM_PROT_READ),
1657 	VM_PROT_TRIAL(VM_PROT_READ | VM_PROT_WRITE),
1658 	VM_PROT_TRIAL(VM_PROT_READ | VM_PROT_EXECUTE),
1659 	// rwx (w+x often disallowed)
1660 	VM_PROT_TRIAL(VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE),
1661 	// VM_PROT_READ | VM_PROT_x for each other VM_PROT_x bit
1662 	// plus write and execute for some interesting cases
1663 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 3),
1664 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 4),
1665 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 5),
1666 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 6),
1667 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 7),
1668 	VM_PROT_TRIAL(VM_PROT_READ | VM_PROT_WRITE | 1u << 7),
1669 	VM_PROT_TRIAL(VM_PROT_READ | VM_PROT_EXECUTE | 1u << 7),
1670 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 8),
1671 	VM_PROT_TRIAL(VM_PROT_READ | VM_PROT_WRITE | 1u << 8),
1672 	VM_PROT_TRIAL(VM_PROT_READ | VM_PROT_EXECUTE | 1u << 8),
1673 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 9),
1674 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 10),
1675 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 11),
1676 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 12),
1677 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 13),
1678 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 14),
1679 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 15),
1680 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 16),
1681 	VM_PROT_TRIAL(VM_PROT_READ | VM_PROT_WRITE | 1u << 16),
1682 	VM_PROT_TRIAL(VM_PROT_READ | VM_PROT_EXECUTE | 1u << 16),
1683 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 17),
1684 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 18),
1685 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 19),
1686 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 20),
1687 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 21),
1688 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 22),
1689 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 23),
1690 	VM_PROT_TRIAL(VM_PROT_READ | VM_PROT_WRITE | 1u << 23),
1691 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 24),
1692 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 25),
1693 	VM_PROT_TRIAL(VM_PROT_READ | VM_PROT_WRITE | 1u << 25),
1694 	VM_PROT_TRIAL(VM_PROT_READ | VM_PROT_EXECUTE | 1u << 25),
1695 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 26),
1696 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 27),
1697 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 28),
1698 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 29),
1699 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 30),
1700 	VM_PROT_TRIAL(VM_PROT_READ | 1u << 31),
1701 	VM_PROT_TRIAL(VM_PROT_READ | VM_PROT_WRITE | 1u << 31),
1702 	VM_PROT_TRIAL(VM_PROT_READ | VM_PROT_EXECUTE | 1u << 31),
1703 
1704 	// error case coverage in specific subfunctions
1705 	VM_PROT_TRIAL(VM_PROT_READ | MAP_MEM_ONLY | MAP_MEM_USE_DATA_ADDR),
1706 	VM_PROT_TRIAL(VM_PROT_READ | MAP_MEM_ONLY | MAP_MEM_4K_DATA_ADDR),
1707 	VM_PROT_TRIAL(VM_PROT_READ | MAP_MEM_NAMED_CREATE | MAP_MEM_USE_DATA_ADDR),
1708 	VM_PROT_TRIAL(VM_PROT_READ | MAP_MEM_NAMED_CREATE | MAP_MEM_4K_DATA_ADDR),
1709 	VM_PROT_TRIAL(VM_PROT_READ | MAP_MEM_NAMED_CREATE | MAP_MEM_PURGABLE),
1710 	VM_PROT_TRIAL(VM_PROT_NONE | MAP_MEM_VM_SHARE | VM_PROT_IS_MASK),
1711 
1712 	// interesting non-error cases for additional test coverage
1713 	VM_PROT_TRIAL(VM_PROT_READ | VM_PROT_WRITE | MAP_MEM_NAMED_CREATE | MAP_MEM_PURGABLE),
1714 	VM_PROT_TRIAL(VM_PROT_READ | VM_PROT_WRITE | MAP_MEM_NAMED_CREATE |
1715     MAP_MEM_PURGABLE | MAP_MEM_PURGABLE_KERNEL_ONLY),
1716 };
1717 
TRIALS_IMPL(vm_prot)1718 TRIALS_IMPL(vm_prot)
1719 
1720 static void
1721 cleanup_vm_prot_trials(vm_prot_trials_t **trials)
1722 {
1723 	free_trials(*trials);
1724 }
1725 
1726 // allocate vm_prot trials, and deallocate it at end of scope
1727 #define SMART_VM_PROT_TRIALS()                                          \
1728 	__attribute__((cleanup(cleanup_vm_prot_trials)))                \
1729 	= allocate_vm_prot_trials(countof(vm_prot_trials_values));      \
1730 	append_trials(trials, vm_prot_trials_values, countof(vm_prot_trials_values))
1731 
1732 // Trials for pairs of vm_prot_t
1733 
1734 typedef struct {
1735 	vm_prot_t cur;
1736 	vm_prot_t max;
1737 	char * name;
1738 } vm_prot_pair_trial_t;
1739 
1740 typedef struct {
1741 	unsigned count;
1742 	unsigned capacity;
1743 	vm_prot_pair_trial_t list[];
1744 } vm_prot_pair_trials_t;
1745 
TRIALS_IMPL(vm_prot_pair)1746 TRIALS_IMPL(vm_prot_pair)
1747 
1748 #define VM_PROT_PAIR_TRIAL(new_cur, new_max, new_name) \
1749 (vm_prot_pair_trial_t){ .cur = (vm_prot_t)(new_cur), \
1750 	        .max = (vm_prot_t)(new_max), \
1751 	        .name = new_name,}
1752 
1753 vm_prot_pair_trials_t *
1754 generate_vm_prot_pair_trials()
1755 {
1756 	const unsigned D = countof(vm_prot_trials_values);
1757 	unsigned num_trials = D * D;
1758 
1759 	vm_prot_pair_trials_t * trials = allocate_vm_prot_pair_trials(num_trials);
1760 	for (size_t i = 0; i < D; i++) {
1761 		for (size_t j = 0; j < D; j++) {
1762 			vm_prot_t cur = vm_prot_trials_values[i].prot;
1763 			vm_prot_t max = vm_prot_trials_values[j].prot;
1764 			char *str;
1765 			kasprintf(&str, "cur: 0x%x, max: 0x%x", cur, max);
1766 			append_trial(trials, VM_PROT_PAIR_TRIAL(cur, max, str));
1767 		}
1768 	}
1769 	return trials;
1770 }
1771 
1772 #define SMART_VM_PROT_PAIR_TRIALS()                                             \
1773 	__attribute__((cleanup(cleanup_vm_prot_pair_trials)))           \
1774 	= generate_vm_prot_pair_trials();
1775 
1776 static void
cleanup_vm_prot_pair_trials(vm_prot_pair_trials_t ** trials)1777 cleanup_vm_prot_pair_trials(vm_prot_pair_trials_t **trials)
1778 {
1779 	for (size_t i = 0; i < (*trials)->count; i++) {
1780 		kfree_str((*trials)->list[i].name);
1781 	}
1782 	free_trials(*trials);
1783 }
1784 
1785 
1786 // vm_purgeable_t trial contents.
1787 typedef struct {
1788 	vm_purgable_t value;
1789 	char * name;
1790 } vm_purgeable_trial_t;
1791 
1792 #define VM_PURGEABLE_TRIAL(new_value) \
1793 	(vm_purgeable_trial_t) {.value = (vm_purgable_t)(new_value), .name = "vm_purgeable_t " #new_value}
1794 
1795 static vm_purgeable_trial_t vm_purgeable_trials_values[] = {
1796 	VM_PURGEABLE_TRIAL(VM_PURGABLE_SET_STATE),
1797 	VM_PURGEABLE_TRIAL(VM_PURGABLE_GET_STATE),
1798 	VM_PURGEABLE_TRIAL(VM_PURGABLE_PURGE_ALL),
1799 	VM_PURGEABLE_TRIAL(VM_PURGABLE_SET_STATE_FROM_KERNEL),
1800 	// end valid values
1801 	VM_PURGEABLE_TRIAL(VM_PURGABLE_SET_STATE_FROM_KERNEL + 1),
1802 	VM_PURGEABLE_TRIAL(VM_PURGABLE_SET_STATE_FROM_KERNEL + 2),
1803 	VM_PURGEABLE_TRIAL(0x12345),
1804 	VM_PURGEABLE_TRIAL(0xffffffff),
1805 };
1806 
1807 typedef struct {
1808 	int value;
1809 	char * name;
1810 } vm_purgeable_state_trial_t;
1811 
1812 #define VM_PURGEABLE_STATE_TRIAL(new_value) \
1813 	(vm_purgeable_state_trial_t) {.value = (int)(new_value), .name = "state " #new_value}
1814 
1815 static vm_purgeable_state_trial_t vm_purgeable_state_trials_values[] = {
1816 	VM_PURGEABLE_STATE_TRIAL(VM_PURGABLE_NO_AGING),
1817 	VM_PURGEABLE_STATE_TRIAL(VM_PURGABLE_DEBUG_EMPTY),
1818 	VM_PURGEABLE_STATE_TRIAL(VM_VOLATILE_GROUP_0),
1819 	VM_PURGEABLE_STATE_TRIAL(VM_VOLATILE_GROUP_7),
1820 	VM_PURGEABLE_STATE_TRIAL(VM_PURGABLE_BEHAVIOR_FIFO),
1821 	VM_PURGEABLE_STATE_TRIAL(VM_PURGABLE_ORDERING_NORMAL),
1822 	VM_PURGEABLE_STATE_TRIAL(VM_PURGABLE_EMPTY),
1823 	VM_PURGEABLE_STATE_TRIAL(VM_PURGABLE_DENY),
1824 	VM_PURGEABLE_STATE_TRIAL(VM_PURGABLE_NONVOLATILE),
1825 	VM_PURGEABLE_STATE_TRIAL(VM_PURGABLE_VOLATILE),
1826 	VM_PURGEABLE_STATE_TRIAL(0x12345),
1827 	VM_PURGEABLE_STATE_TRIAL(0xffffffff),
1828 };
1829 
1830 // Trials for vm_purgeable_t and state
1831 typedef struct {
1832 	vm_purgable_t control;
1833 	int state;
1834 	char * name;
1835 } vm_purgeable_and_state_trial_t;
1836 
1837 typedef struct {
1838 	unsigned count;
1839 	unsigned capacity;
1840 	vm_purgeable_and_state_trial_t list[];
1841 } vm_purgeable_and_state_trials_t;
1842 
TRIALS_IMPL(vm_purgeable_and_state)1843 TRIALS_IMPL(vm_purgeable_and_state)
1844 
1845 #define VM_PURGEABLE_AND_STATE_TRIAL(new_control, new_state, new_name) \
1846 (vm_purgeable_and_state_trial_t){ .control = (vm_purgable_t)(new_control), \
1847 	        .state = (int)(new_state), \
1848 	        .name = new_name,}
1849 
1850 vm_purgeable_and_state_trials_t *
1851 generate_vm_purgeable_t_and_state_trials()
1852 {
1853 	const unsigned purgeable_trial_count = countof(vm_purgeable_trials_values);
1854 	const unsigned state_trial_count = countof(vm_purgeable_state_trials_values);
1855 	unsigned num_trials = purgeable_trial_count * state_trial_count;
1856 
1857 	vm_purgeable_and_state_trials_t * trials = allocate_vm_purgeable_and_state_trials(num_trials);
1858 	for (size_t i = 0; i < purgeable_trial_count; i++) {
1859 		for (size_t j = 0; j < state_trial_count; j++) {
1860 			vm_purgeable_trial_t control_trial = vm_purgeable_trials_values[i];
1861 			vm_purgeable_state_trial_t state_trial = vm_purgeable_state_trials_values[j];
1862 			char *str;
1863 			kasprintf(&str, "%s, %s", control_trial.name, state_trial.name);
1864 			append_trial(trials, VM_PURGEABLE_AND_STATE_TRIAL(control_trial.value, state_trial.value, str));
1865 		}
1866 	}
1867 	return trials;
1868 }
1869 
1870 #define SMART_VM_PURGEABLE_AND_STATE_TRIALS()                           \
1871 	__attribute__((cleanup(cleanup_vm_purgeable_t_and_state_trials))) \
1872 	= generate_vm_purgeable_t_and_state_trials();
1873 
1874 static void
cleanup_vm_purgeable_t_and_state_trials(vm_purgeable_and_state_trials_t ** trials)1875 cleanup_vm_purgeable_t_and_state_trials(vm_purgeable_and_state_trials_t **trials)
1876 {
1877 	for (size_t i = 0; i < (*trials)->count; i++) {
1878 		kfree_str((*trials)->list[i].name);
1879 	}
1880 	free_trials(*trials);
1881 }
1882 
1883 // generate ledger tag trials
1884 
1885 typedef struct {
1886 	int tag;
1887 	const char *name;
1888 } ledger_tag_trial_t;
1889 
1890 typedef struct {
1891 	unsigned count;
1892 	unsigned capacity;
1893 	ledger_tag_trial_t list[];
1894 } ledger_tag_trials_t;
1895 
1896 #define LEDGER_TAG_TRIAL(new_tag)                            \
1897 	(ledger_tag_trial_t){ .tag = (int)(new_tag), .name = "ledger tag "#new_tag }
1898 
1899 static ledger_tag_trial_t ledger_tag_trials_values[] = {
1900 	LEDGER_TAG_TRIAL(VM_LEDGER_TAG_NONE),
1901 	LEDGER_TAG_TRIAL(VM_LEDGER_TAG_DEFAULT),
1902 	LEDGER_TAG_TRIAL(VM_LEDGER_TAG_NETWORK),
1903 	LEDGER_TAG_TRIAL(VM_LEDGER_TAG_MEDIA),
1904 	LEDGER_TAG_TRIAL(VM_LEDGER_TAG_GRAPHICS),
1905 	LEDGER_TAG_TRIAL(VM_LEDGER_TAG_NEURAL),
1906 	LEDGER_TAG_TRIAL(VM_LEDGER_TAG_MAX),
1907 	LEDGER_TAG_TRIAL(1u << 16),
1908 	LEDGER_TAG_TRIAL(1u << 17),
1909 	LEDGER_TAG_TRIAL(1u << 18),
1910 	LEDGER_TAG_TRIAL(1u << 19),
1911 	LEDGER_TAG_TRIAL(1u << 20),
1912 	LEDGER_TAG_TRIAL(1u << 21),
1913 	LEDGER_TAG_TRIAL(1u << 22),
1914 	LEDGER_TAG_TRIAL(1u << 23),
1915 	LEDGER_TAG_TRIAL(1u << 24),
1916 	LEDGER_TAG_TRIAL(1u << 25),
1917 	LEDGER_TAG_TRIAL(1u << 26),
1918 	LEDGER_TAG_TRIAL(1u << 27),
1919 	LEDGER_TAG_TRIAL(1u << 28),
1920 	LEDGER_TAG_TRIAL(1u << 29),
1921 	LEDGER_TAG_TRIAL(1u << 30),
1922 	LEDGER_TAG_TRIAL(1u << 31),
1923 	LEDGER_TAG_TRIAL(VM_LEDGER_TAG_UNCHANGED),
1924 };
1925 
TRIALS_IMPL(ledger_tag)1926 TRIALS_IMPL(ledger_tag)
1927 
1928 static void
1929 cleanup_ledger_tag_trials(ledger_tag_trials_t **trials)
1930 {
1931 	free_trials(*trials);
1932 }
1933 
1934 // allocate ledger tag trials, and deallocate it at end of scope
1935 #define SMART_LEDGER_TAG_TRIALS()                                               \
1936 	__attribute__((cleanup(cleanup_ledger_tag_trials)))             \
1937 	= allocate_ledger_tag_trials(countof(ledger_tag_trials_values));        \
1938 	append_trials(trials, ledger_tag_trials_values, countof(ledger_tag_trials_values))
1939 
1940 
1941 // generate ledger flag trials
1942 
1943 typedef struct {
1944 	int flag;
1945 	const char *name;
1946 } ledger_flag_trial_t;
1947 
1948 typedef struct {
1949 	unsigned count;
1950 	unsigned capacity;
1951 	ledger_flag_trial_t list[];
1952 } ledger_flag_trials_t;
1953 
1954 #define LEDGER_FLAG_TRIAL(new_flag)                            \
1955 	(ledger_flag_trial_t){ .flag = (int)(new_flag), .name = "ledger flag "#new_flag }
1956 
1957 static ledger_flag_trial_t ledger_flag_trials_values[] = {
1958 	LEDGER_FLAG_TRIAL(0),
1959 	LEDGER_FLAG_TRIAL(VM_LEDGER_FLAG_NO_FOOTPRINT),
1960 	LEDGER_FLAG_TRIAL(VM_LEDGER_FLAG_NO_FOOTPRINT_FOR_DEBUG),
1961 	LEDGER_FLAG_TRIAL(VM_LEDGER_FLAGS_USER),
1962 	LEDGER_FLAG_TRIAL(VM_LEDGER_FLAG_FROM_KERNEL),
1963 	LEDGER_FLAG_TRIAL(VM_LEDGER_FLAGS_ALL),
1964 	LEDGER_FLAG_TRIAL(1u << 3),
1965 	LEDGER_FLAG_TRIAL(1u << 4),
1966 	LEDGER_FLAG_TRIAL(1u << 5),
1967 	LEDGER_FLAG_TRIAL(1u << 6),
1968 	LEDGER_FLAG_TRIAL(1u << 7),
1969 	LEDGER_FLAG_TRIAL(1u << 8),
1970 	LEDGER_FLAG_TRIAL(1u << 9),
1971 	LEDGER_FLAG_TRIAL(1u << 10),
1972 	LEDGER_FLAG_TRIAL(1u << 11),
1973 	LEDGER_FLAG_TRIAL(1u << 12),
1974 	LEDGER_FLAG_TRIAL(1u << 13),
1975 	LEDGER_FLAG_TRIAL(1u << 14),
1976 	LEDGER_FLAG_TRIAL(1u << 15),
1977 	LEDGER_FLAG_TRIAL(1u << 16),
1978 	LEDGER_FLAG_TRIAL(1u << 17),
1979 	LEDGER_FLAG_TRIAL(1u << 18),
1980 	LEDGER_FLAG_TRIAL(1u << 19),
1981 	LEDGER_FLAG_TRIAL(1u << 20),
1982 	LEDGER_FLAG_TRIAL(1u << 21),
1983 	LEDGER_FLAG_TRIAL(1u << 22),
1984 	LEDGER_FLAG_TRIAL(1u << 23),
1985 	LEDGER_FLAG_TRIAL(1u << 24),
1986 	LEDGER_FLAG_TRIAL(1u << 25),
1987 	LEDGER_FLAG_TRIAL(1u << 26),
1988 	LEDGER_FLAG_TRIAL(1u << 27),
1989 	LEDGER_FLAG_TRIAL(1u << 28),
1990 	LEDGER_FLAG_TRIAL(1u << 29),
1991 	LEDGER_FLAG_TRIAL(1u << 30),
1992 	LEDGER_FLAG_TRIAL(1u << 31),
1993 };
1994 
TRIALS_IMPL(ledger_flag)1995 TRIALS_IMPL(ledger_flag)
1996 
1997 static void
1998 cleanup_ledger_flag_trials(ledger_flag_trials_t **trials)
1999 {
2000 	free_trials(*trials);
2001 }
2002 
2003 // allocate ledger flag trials, and deallocate it at end of scope
2004 #define SMART_LEDGER_FLAG_TRIALS()                                              \
2005 	__attribute__((cleanup(cleanup_ledger_flag_trials)))            \
2006 	= allocate_ledger_flag_trials(countof(ledger_flag_trials_values));      \
2007 	append_trials(trials, ledger_flag_trials_values, countof(ledger_flag_trials_values))
2008 
2009 // generate address-parameter trials
2010 // where the address has no associated size
2011 // and the callee's arithmetic includes `round_page(addr)`
2012 
2013 typedef struct {
2014 	addr_t addr;
2015 	bool addr_is_absolute;
2016 	char *name;
2017 } addr_trial_t;
2018 
2019 typedef struct {
2020 	unsigned count;
2021 	unsigned capacity;
2022 	addr_trial_t list[];
2023 } addr_trials_t;
2024 
2025 #define ADDR_TRIAL(new_addr, new_absolute, new_name)                    \
2026 	(addr_trial_t){ .addr = (addr_t)(new_addr), .addr_is_absolute = new_absolute, .name = new_name }
2027 
2028 static addr_trial_t __attribute__((overloadable, used))
slide_trial(addr_trial_t trial,mach_vm_address_t slide)2029 slide_trial(addr_trial_t trial, mach_vm_address_t slide)
2030 {
2031 	addr_trial_t result = trial;
2032 	if (!trial.addr_is_absolute) {
2033 		result.addr += slide;
2034 	}
2035 	return result;
2036 }
2037 
2038 static const offset_list_t *
get_addr_trial_offsets(void)2039 get_addr_trial_offsets(void)
2040 {
2041 	addr_t test_page_size = adjust_page_size();
2042 	CACHE_OFFSETS(addr_trial_offsets, ^{
2043 		offset_list_t *offsets = allocate_offsets(20);
2044 		append_offset(offsets, true, 0);
2045 		append_offset(offsets, true, 1);
2046 		append_offset(offsets, true, 2);
2047 		append_offset(offsets, true, test_page_size - 2);
2048 		append_offset(offsets, true, test_page_size - 1);
2049 		append_offset(offsets, true, test_page_size);
2050 		append_offset(offsets, true, test_page_size + 1);
2051 		append_offset(offsets, true, test_page_size + 2);
2052 		append_offset(offsets, true, -(mach_vm_address_t)test_page_size - 2);
2053 		append_offset(offsets, true, -(mach_vm_address_t)test_page_size - 1);
2054 		append_offset(offsets, true, -(mach_vm_address_t)test_page_size);
2055 		append_offset(offsets, true, -(mach_vm_address_t)test_page_size + 1);
2056 		append_offset(offsets, true, -(mach_vm_address_t)test_page_size + 2);
2057 		append_offset(offsets, true, -(mach_vm_address_t)2);
2058 		append_offset(offsets, true, -(mach_vm_address_t)1);
2059 
2060 		append_offset(offsets, false, 0);
2061 		append_offset(offsets, false, 1);
2062 		append_offset(offsets, false, 2);
2063 		append_offset(offsets, false, test_page_size - 2);
2064 		append_offset(offsets, false, test_page_size - 1);
2065 		return offsets;
2066 	});
2067 	return addr_trial_offsets;
2068 }
2069 
TRIALS_IMPL(addr)2070 TRIALS_IMPL(addr)
2071 
2072 addr_trials_t *
2073 generate_addr_trials(addr_t base)
2074 {
2075 	const offset_list_t *offsets = get_addr_trial_offsets();
2076 	const unsigned ADDRS = offsets->count;
2077 	addr_trials_t *trials = allocate_addr_trials(ADDRS);
2078 
2079 	for (unsigned a = 0; a < ADDRS; a++) {
2080 		mach_vm_address_t addr_offset = offsets->list[a].offset;
2081 		mach_vm_address_t addr = addr_offset;
2082 		bool addr_is_absolute = offsets->list[a].is_absolute;
2083 		if (!addr_is_absolute) {
2084 			addr += base;
2085 		}
2086 
2087 		char *str;
2088 		kasprintf(&str, "addr: %s0x%llx",
2089 		    addr_is_absolute ? "" : "base+", addr_offset);
2090 		append_trial(trials, ADDR_TRIAL(addr, addr_is_absolute, str));
2091 	}
2092 	return trials;
2093 }
2094 
2095 static void
cleanup_addr_trials(addr_trials_t ** trials)2096 cleanup_addr_trials(addr_trials_t **trials)
2097 {
2098 	for (size_t i = 0; i < (*trials)->count; i++) {
2099 		kfree_str((*trials)->list[i].name);
2100 	}
2101 	free_trials(*trials);
2102 }
2103 
2104 // allocate address trials around a base address
2105 // and deallocate it at end of scope
2106 #define SMART_ADDR_TRIALS(base)                                         \
2107 	__attribute__((cleanup(cleanup_addr_trials)))                   \
2108 	    = generate_addr_trials(base)
2109 
2110 
2111 /////////////////////////////////////////////////////
2112 // generate size-parameter trials
2113 // where the size is not associated with any base address
2114 // and the callee's arithmetic includes `round_page(size)`
2115 
2116 typedef struct {
2117 	addr_t size;
2118 	char *name;
2119 } size_trial_t;
2120 
2121 typedef struct {
2122 	unsigned count;
2123 	unsigned capacity;
2124 	size_trial_t list[];
2125 } size_trials_t;
2126 
2127 #define SIZE_TRIAL(new_size, new_name)                                          \
2128 	(size_trial_t){ .size = (addr_t)(new_size), .name = new_name }
2129 
2130 static const offset_list_t *
get_size_trial_offsets(void)2131 get_size_trial_offsets(void)
2132 {
2133 	addr_t test_page_size = adjust_page_size();
2134 	CACHE_OFFSETS(size_trial_offsets, ^{
2135 		offset_list_t *offsets = allocate_offsets(15);
2136 		append_offset(offsets, true, 0);
2137 		append_offset(offsets, true, 1);
2138 		append_offset(offsets, true, 2);
2139 		append_offset(offsets, true, test_page_size - 2);
2140 		append_offset(offsets, true, test_page_size - 1);
2141 		append_offset(offsets, true, test_page_size);
2142 		append_offset(offsets, true, test_page_size + 1);
2143 		append_offset(offsets, true, test_page_size + 2);
2144 		append_offset(offsets, true, -(mach_vm_address_t)test_page_size - 2);
2145 		append_offset(offsets, true, -(mach_vm_address_t)test_page_size - 1);
2146 		append_offset(offsets, true, -(mach_vm_address_t)test_page_size);
2147 		append_offset(offsets, true, -(mach_vm_address_t)test_page_size + 1);
2148 		append_offset(offsets, true, -(mach_vm_address_t)test_page_size + 2);
2149 		append_offset(offsets, true, -(mach_vm_address_t)2);
2150 		append_offset(offsets, true, -(mach_vm_address_t)1);
2151 		return offsets;
2152 	});
2153 	return size_trial_offsets;
2154 }
2155 
TRIALS_IMPL(size)2156 TRIALS_IMPL(size)
2157 
2158 size_trials_t *
2159 generate_size_trials(void)
2160 {
2161 	const offset_list_t *size_offsets = get_size_trial_offsets();
2162 	const unsigned SIZES = size_offsets->count;
2163 	size_trials_t *trials = allocate_size_trials(SIZES);
2164 
2165 	for (unsigned s = 0; s < SIZES; s++) {
2166 		mach_vm_size_t size = size_offsets->list[s].offset;
2167 
2168 		char *str;
2169 		kasprintf(&str, "size: 0x%llx", size);
2170 		append_trial(trials, SIZE_TRIAL(size, str));
2171 	}
2172 	return trials;
2173 }
2174 
2175 static void
cleanup_size_trials(size_trials_t ** trials)2176 cleanup_size_trials(size_trials_t **trials)
2177 {
2178 	for (size_t i = 0; i < (*trials)->count; i++) {
2179 		kfree_str((*trials)->list[i].name);
2180 	}
2181 	free_trials(*trials);
2182 }
2183 
2184 // allocate size trials, and deallocate it at end of scope
2185 #define SMART_SIZE_TRIALS()                                             \
2186 	__attribute__((cleanup(cleanup_size_trials)))                   \
2187 	= generate_size_trials()
2188 
2189 /////////////////////////////////////////////////////
2190 // generate start/size trials
2191 // using absolute addresses or addresses around a given address
2192 // where `size` is the size of the thing at `start`
2193 // and the callee's arithmetic performs `start+size`
2194 
2195 typedef struct {
2196 	addr_t start;
2197 	addr_t size;
2198 	char *name;
2199 	bool start_is_absolute;  // start computation does not include any allocation's base address
2200 	bool size_is_absolute;   // size computation does not include start
2201 } start_size_trial_t;
2202 
2203 typedef struct {
2204 	unsigned count;
2205 	unsigned capacity;
2206 	start_size_trial_t list[];
2207 } start_size_trials_t;
2208 
2209 
2210 #define START_SIZE_TRIAL(new_start, start_absolute, new_size, size_absolute, new_name) \
2211 	(start_size_trial_t){ .start = (addr_t)(new_start), .size = (addr_t)(new_size), \
2212 	                .name = new_name,                                       \
2213 	                .start_is_absolute = start_absolute, .size_is_absolute = size_absolute }
2214 
2215 static const offset_list_t *
get_start_size_trial_start_offsets(void)2216 get_start_size_trial_start_offsets(void)
2217 {
2218 	return get_addr_trial_offsets();
2219 }
2220 
2221 static const offset_list_t *
get_start_size_trial_size_offsets(void)2222 get_start_size_trial_size_offsets(void)
2223 {
2224 	CACHE_OFFSETS(start_size_trial_offsets, ^{
2225 		// use each size offset twice: once absolute and once relative
2226 		const offset_list_t *old_offsets = get_size_trial_offsets();
2227 		offset_list_t *offsets = allocate_offsets(2 * old_offsets->count);
2228 		for (unsigned i = 0; i < old_offsets->count; i++) {
2229 		        append_offset(offsets, true, old_offsets->list[i].offset);
2230 		}
2231 		for (unsigned i = 0; i < old_offsets->count; i++) {
2232 		        append_offset(offsets, false, old_offsets->list[i].offset);
2233 		}
2234 		return offsets;
2235 	});
2236 	return start_size_trial_offsets;
2237 }
2238 
TRIALS_IMPL(start_size)2239 TRIALS_IMPL(start_size)
2240 
2241 // Return a new start/size trial which is offset by `slide` bytes
2242 // Only "relative" start and size values get slid.
2243 // "absolute" values don't change.
2244 static start_size_trial_t __attribute__((overloadable, used))
2245 slide_trial(start_size_trial_t trial, mach_vm_address_t slide)
2246 {
2247 	start_size_trial_t result = trial;
2248 	if (!result.start_is_absolute) {
2249 		result.start += slide;
2250 		if (!result.size_is_absolute) {
2251 			result.size -= slide;
2252 		}
2253 	}
2254 	return result;
2255 }
2256 
2257 start_size_trials_t *
generate_start_size_trials(addr_t base)2258 generate_start_size_trials(addr_t base)
2259 {
2260 	const offset_list_t *start_offsets = get_start_size_trial_start_offsets();
2261 	const offset_list_t *size_offsets = get_start_size_trial_size_offsets();
2262 
2263 	const unsigned ADDRS = start_offsets->count;
2264 	const unsigned SIZES = size_offsets->count;
2265 
2266 	start_size_trials_t *trials = allocate_start_size_trials(ADDRS * SIZES);
2267 
2268 	for (unsigned a = 0; a < ADDRS; a++) {
2269 		for (unsigned s = 0; s < SIZES; s++) {
2270 			mach_vm_address_t start_offset = start_offsets->list[a].offset;
2271 			mach_vm_address_t start = start_offset;
2272 			bool start_is_absolute = start_offsets->list[a].is_absolute;
2273 			if (!start_is_absolute) {
2274 				start += base;
2275 			}
2276 
2277 			mach_vm_size_t size_offset = size_offsets->list[s].offset;
2278 			mach_vm_size_t size = size_offset;
2279 			bool size_is_absolute = size_offsets->list[s].is_absolute;
2280 			if (!size_is_absolute) {
2281 				size = -start + size;
2282 			}
2283 
2284 			char *str;
2285 			kasprintf(&str, "start: %s0x%llx, size: %s0x%llx",
2286 			    start_is_absolute ? "" : "base+", start_offset,
2287 			    size_is_absolute ? "" :"-start+", size_offset);
2288 			append_trial(trials, START_SIZE_TRIAL(start, start_is_absolute, size, size_is_absolute, str));
2289 		}
2290 	}
2291 	return trials;
2292 }
2293 
2294 static void
cleanup_start_size_trials(start_size_trials_t ** trials)2295 cleanup_start_size_trials(start_size_trials_t **trials)
2296 {
2297 	for (size_t i = 0; i < (*trials)->count; i++) {
2298 		kfree_str((*trials)->list[i].name);
2299 	}
2300 	free_trials(*trials);
2301 }
2302 
2303 // allocate start/size trials around a base address
2304 // and deallocate it at end of scope
2305 #define SMART_START_SIZE_TRIALS(base)                                   \
2306 	__attribute__((cleanup(cleanup_start_size_trials)))             \
2307 	= generate_start_size_trials(base)
2308 
2309 // Trials for start/size/offset/object tuples
2310 
2311 typedef struct {
2312 	mach_vm_address_t start;
2313 	mach_vm_size_t size;
2314 	vm_object_offset_t offset;
2315 	mach_vm_size_t obj_size;
2316 	bool start_is_absolute;
2317 	bool size_is_absolute;
2318 	char * name;
2319 } start_size_offset_object_trial_t;
2320 
2321 typedef struct {
2322 	unsigned count;
2323 	unsigned capacity;
2324 	start_size_offset_object_trial_t list[];
2325 } start_size_offset_object_trials_t;
2326 
TRIALS_IMPL(start_size_offset_object)2327 TRIALS_IMPL(start_size_offset_object)
2328 
2329 #define START_SIZE_OFFSET_OBJECT_TRIAL(new_start, new_size, new_offset, new_obj_size, new_start_is_absolute, new_size_is_absolute, new_name) \
2330 (start_size_offset_object_trial_t){ .start = (mach_vm_address_t)(new_start), \
2331 	        .size = (mach_vm_size_t)(new_size), \
2332 	        .offset = (vm_object_offset_t)(new_offset), \
2333 	        .obj_size = (mach_vm_size_t)(new_obj_size), \
2334 	        .start_is_absolute = (bool)(new_start_is_absolute), \
2335 	        .size_is_absolute = (bool)(new_size_is_absolute), \
2336 	        .name = new_name,}
2337 
2338 bool
2339 obj_size_is_ok(mach_vm_size_t obj_size)
2340 {
2341 	addr_t test_page_size = adjust_page_size();
2342 	if (round_up_page(obj_size, test_page_size) == 0) {
2343 		return false;
2344 	}
2345 	/* in rosetta, PAGE_SIZE is 4K but rounding to 16K also panics */ \
2346 	if (!kern_trialname_generation && isRosetta() && round_up_page(obj_size, KB16) == 0) {
2347 		return false;
2348 	}
2349 	return true;
2350 }
2351 
2352 static start_size_offset_object_trial_t __attribute__((overloadable, used))
slide_trial(start_size_offset_object_trial_t trial,mach_vm_address_t slide)2353 slide_trial(start_size_offset_object_trial_t trial, mach_vm_address_t slide)
2354 {
2355 	start_size_offset_object_trial_t result = trial;
2356 
2357 	if (!trial.start_is_absolute) {
2358 		result.start += slide;
2359 		if (!trial.size_is_absolute) {
2360 			result.size -= slide;
2361 		}
2362 	}
2363 	return result;
2364 }
2365 
2366 static offset_list_t *
get_ssoo_absolute_offsets()2367 get_ssoo_absolute_offsets()
2368 {
2369 	addr_t test_page_size = adjust_page_size();
2370 	CACHE_OFFSETS(ssoo_absolute_offsets, ^{
2371 		offset_list_t *offsets = allocate_offsets(20);
2372 		append_offset(offsets, true, 0);
2373 		append_offset(offsets, true, 1);
2374 		append_offset(offsets, true, 2);
2375 		append_offset(offsets, true, test_page_size - 2);
2376 		append_offset(offsets, true, test_page_size - 1);
2377 		append_offset(offsets, true, test_page_size);
2378 		append_offset(offsets, true, test_page_size + 1);
2379 		append_offset(offsets, true, test_page_size + 2);
2380 		append_offset(offsets, true, -(mach_vm_address_t)test_page_size - 2);
2381 		append_offset(offsets, true, -(mach_vm_address_t)test_page_size - 1);
2382 		append_offset(offsets, true, -(mach_vm_address_t)test_page_size);
2383 		append_offset(offsets, true, -(mach_vm_address_t)test_page_size + 1);
2384 		append_offset(offsets, true, -(mach_vm_address_t)test_page_size + 2);
2385 		append_offset(offsets, true, -(mach_vm_address_t)2);
2386 		append_offset(offsets, true, -(mach_vm_address_t)1);
2387 		return offsets;
2388 	});
2389 	return ssoo_absolute_offsets;
2390 }
2391 
2392 static offset_list_t *
get_ssoo_absolute_and_relative_offsets()2393 get_ssoo_absolute_and_relative_offsets()
2394 {
2395 	addr_t test_page_size = adjust_page_size();
2396 	CACHE_OFFSETS(ssoo_absolute_and_relative_offsets, ^{
2397 		const offset_list_t *old_offsets = get_ssoo_absolute_offsets();
2398 		offset_list_t *offsets = allocate_offsets(old_offsets->count + 5);
2399 		// absolute offsets
2400 		for (unsigned i = 0; i < old_offsets->count; i++) {
2401 		        append_offset(offsets, true, old_offsets->list[i].offset);
2402 		}
2403 		// relative offsets
2404 		append_offset(offsets, false, 0);
2405 		append_offset(offsets, false, 1);
2406 		append_offset(offsets, false, 2);
2407 		append_offset(offsets, false, test_page_size - 2);
2408 		append_offset(offsets, false, test_page_size - 1);
2409 		return offsets;
2410 	});
2411 	return ssoo_absolute_and_relative_offsets;
2412 }
2413 
2414 start_size_offset_object_trials_t *
generate_start_size_offset_object_trials()2415 generate_start_size_offset_object_trials()
2416 {
2417 	const offset_list_t *start_offsets = get_ssoo_absolute_and_relative_offsets();
2418 	const offset_list_t *size_offsets  = get_ssoo_absolute_and_relative_offsets();
2419 	const offset_list_t *offset_values = get_ssoo_absolute_offsets();
2420 	const offset_list_t *object_sizes  = get_ssoo_absolute_offsets();
2421 
2422 	unsigned num_trials = 0;
2423 	for (size_t d = 0; d < object_sizes->count; d++) {
2424 		mach_vm_size_t obj_size = object_sizes->list[d].offset;
2425 		if (!obj_size_is_ok(obj_size)) { // make_a_mem_object would fail
2426 			continue;
2427 		}
2428 		num_trials++;
2429 	}
2430 	num_trials *= start_offsets->count * size_offsets->count * offset_values->count;
2431 
2432 	start_size_offset_object_trials_t * trials = allocate_start_size_offset_object_trials(num_trials);
2433 	for (size_t a = 0; a < start_offsets->count; a++) {
2434 		for (size_t b = 0; b < size_offsets->count; b++) {
2435 			for (size_t c = 0; c < offset_values->count; c++) {
2436 				for (size_t d = 0; d < object_sizes->count; d++) {
2437 					bool start_is_absolute = start_offsets->list[a].is_absolute;
2438 					bool size_is_absolute = size_offsets->list[b].is_absolute;
2439 					mach_vm_address_t start = start_offsets->list[a].offset;
2440 					mach_vm_size_t size = size_offsets->list[b].offset;
2441 					vm_object_offset_t offset = offset_values->list[c].offset;
2442 					mach_vm_size_t obj_size = object_sizes->list[d].offset;
2443 					if (!obj_size_is_ok(obj_size)) { // make_a_mem_object would fail
2444 						continue;
2445 					}
2446 					char *str;
2447 					kasprintf(&str, "start: %s0x%llx, size: %s0x%llx, offset: 0x%llx, obj_size: 0x%llx",
2448 					    start_is_absolute ? "" : "base+", start,
2449 					    size_is_absolute ? "" :"-start+", size,
2450 					    offset,
2451 					    obj_size);
2452 					append_trial(trials, START_SIZE_OFFSET_OBJECT_TRIAL(start, size, offset, obj_size, start_is_absolute, size_is_absolute, str));
2453 				}
2454 			}
2455 		}
2456 	}
2457 	return trials;
2458 }
2459 
2460 #define SMART_START_SIZE_OFFSET_OBJECT_TRIALS()                                         \
2461 	__attribute__((cleanup(cleanup_start_size_offset_object_trials)))               \
2462 	= generate_start_size_offset_object_trials();
2463 
2464 static void
cleanup_start_size_offset_object_trials(start_size_offset_object_trials_t ** trials)2465 cleanup_start_size_offset_object_trials(start_size_offset_object_trials_t **trials)
2466 {
2467 	for (size_t i = 0; i < (*trials)->count; i++) {
2468 		kfree_str((*trials)->list[i].name);
2469 	}
2470 	free_trials(*trials);
2471 }
2472 
2473 
2474 // Trials for start/size/start/size tuples
2475 
2476 typedef struct {
2477 	mach_vm_address_t start;
2478 	mach_vm_size_t size;
2479 	mach_vm_address_t second_start;
2480 	mach_vm_size_t second_size;
2481 	bool start_is_absolute;
2482 	bool size_is_absolute;
2483 	bool second_start_is_absolute;
2484 	bool second_size_is_absolute;
2485 	char * name;
2486 } start_size_start_size_trial_t;
2487 
2488 typedef struct {
2489 	unsigned count;
2490 	unsigned capacity;
2491 	start_size_start_size_trial_t list[];
2492 } start_size_start_size_trials_t;
2493 
TRIALS_IMPL(start_size_start_size)2494 TRIALS_IMPL(start_size_start_size)
2495 
2496 #define START_SIZE_START_SIZE_TRIAL(new_start, new_size, new_second_start, new_second_size, new_start_is_absolute, \
2497 	    new_size_is_absolute, new_second_start_is_absolute, new_second_size_is_absolute, new_name) \
2498 (start_size_start_size_trial_t){ .start = (mach_vm_address_t)(new_start), \
2499 	        .size = (mach_vm_size_t)(new_size), \
2500 	        .second_start = (mach_vm_address_t)(new_second_start), \
2501 	        .second_size = (mach_vm_size_t)(new_second_size), \
2502 	        .start_is_absolute = (bool)(new_start_is_absolute), \
2503 	        .size_is_absolute = (bool)(new_size_is_absolute), \
2504 	        .second_start_is_absolute = (bool)(new_second_start_is_absolute), \
2505 	        .second_size_is_absolute = (bool)(new_second_size_is_absolute),\
2506 	        .name = new_name,}
2507 
2508 static start_size_start_size_trial_t __attribute__((overloadable, used))
2509 slide_trial(start_size_start_size_trial_t trial, mach_vm_address_t slide, mach_vm_address_t second_slide)
2510 {
2511 	start_size_start_size_trial_t result = trial;
2512 
2513 	if (!trial.start_is_absolute) {
2514 		result.start += slide;
2515 		if (!trial.size_is_absolute) {
2516 			result.size -= slide;
2517 		}
2518 	}
2519 	if (!trial.second_start_is_absolute) {
2520 		result.second_start += second_slide;
2521 		if (!trial.second_size_is_absolute) {
2522 			result.second_size -= second_slide;
2523 		}
2524 	}
2525 	return result;
2526 }
2527 
2528 start_size_start_size_trials_t *
generate_start_size_start_size_trials()2529 generate_start_size_start_size_trials()
2530 {
2531 	/*
2532 	 * Reuse the starts/sizes from start/size/offset/object
2533 	 */
2534 	const offset_list_t *start_offsets        = get_ssoo_absolute_and_relative_offsets();
2535 	const offset_list_t *size_offsets         = get_ssoo_absolute_and_relative_offsets();
2536 	const offset_list_t *second_start_offsets = get_ssoo_absolute_and_relative_offsets();
2537 	const offset_list_t *second_size_offsets  = get_ssoo_absolute_and_relative_offsets();
2538 
2539 	unsigned num_trials = start_offsets->count * size_offsets->count
2540 	    * second_start_offsets->count * second_start_offsets->count;
2541 
2542 	start_size_start_size_trials_t * trials = allocate_start_size_start_size_trials(num_trials);
2543 	for (size_t a = 0; a < start_offsets->count; a++) {
2544 		for (size_t b = 0; b < size_offsets->count; b++) {
2545 			for (size_t c = 0; c < second_start_offsets->count; c++) {
2546 				for (size_t d = 0; d < second_size_offsets->count; d++) {
2547 					bool start_is_absolute = start_offsets->list[a].is_absolute;
2548 					bool size_is_absolute = size_offsets->list[b].is_absolute;
2549 					bool second_start_is_absolute = second_start_offsets->list[c].is_absolute;
2550 					bool second_size_is_absolute = second_size_offsets->list[d].is_absolute;
2551 					mach_vm_address_t start = start_offsets->list[a].offset;
2552 					mach_vm_size_t size = size_offsets->list[b].offset;
2553 					mach_vm_address_t second_start = second_start_offsets->list[c].offset;
2554 					mach_vm_size_t second_size = second_size_offsets->list[d].offset;
2555 
2556 					char *str;
2557 					kasprintf(&str, "start: %s0x%llx, size: %s0x%llx, second_start: %s0x%llx, second_size: %s0x%llx",
2558 					    start_is_absolute ? "" : "base+", start,
2559 					    size_is_absolute ? "" :"-start+", size,
2560 					    second_start_is_absolute ? "" : "base+", second_start,
2561 					    second_size_is_absolute ? "" : "-start+", second_size);
2562 					append_trial(trials, START_SIZE_START_SIZE_TRIAL(start, size, second_start, second_size,
2563 					    start_is_absolute, size_is_absolute,
2564 					    second_start_is_absolute, second_size_is_absolute, str));
2565 				}
2566 			}
2567 		}
2568 	}
2569 	return trials;
2570 }
2571 
2572 #define SMART_START_SIZE_START_SIZE_TRIALS()                                            \
2573 	__attribute__((cleanup(cleanup_start_size_start_size_trials)))                  \
2574 	= generate_start_size_start_size_trials();
2575 
2576 static void __attribute__((used))
cleanup_start_size_start_size_trials(start_size_start_size_trials_t ** trials)2577 cleanup_start_size_start_size_trials(start_size_start_size_trials_t **trials)
2578 {
2579 	for (size_t i = 0; i < (*trials)->count; i++) {
2580 		kfree_str((*trials)->list[i].name);
2581 	}
2582 	free_trials(*trials);
2583 }
2584 
2585 
2586 // start/size/offset: test start+size and a second independent address
2587 // consider src/dst/size instead if the size may be added to both addresses
2588 
2589 typedef struct {
2590 	mach_vm_address_t start;
2591 	mach_vm_size_t size;
2592 	vm_object_offset_t offset;
2593 	bool start_is_absolute;
2594 	bool size_is_absolute;
2595 	char * name;
2596 } start_size_offset_trial_t;
2597 
2598 typedef struct {
2599 	unsigned count;
2600 	unsigned capacity;
2601 	start_size_offset_trial_t list[];
2602 } start_size_offset_trials_t;
2603 
TRIALS_IMPL(start_size_offset)2604 TRIALS_IMPL(start_size_offset)
2605 
2606 #define START_SIZE_OFFSET_TRIAL(new_start, new_size, new_offset, new_start_is_absolute, new_size_is_absolute, new_name) \
2607 (start_size_offset_trial_t){ .start = (mach_vm_address_t)(new_start), \
2608 	        .size = (mach_vm_size_t)(new_size), \
2609 	        .offset = (vm_object_offset_t)(new_offset), \
2610 	        .start_is_absolute = (bool)(new_start_is_absolute), \
2611 	        .size_is_absolute = (bool)(new_size_is_absolute), \
2612 	        .name = new_name,}
2613 
2614 
2615 static start_size_offset_trial_t __attribute__((overloadable, used))
2616 slide_trial(start_size_offset_trial_t trial, mach_vm_address_t slide)
2617 {
2618 	start_size_offset_trial_t result = trial;
2619 
2620 	if (!trial.start_is_absolute) {
2621 		result.start += slide;
2622 		if (!trial.size_is_absolute) {
2623 			result.size -= slide;
2624 		}
2625 	}
2626 	return result;
2627 }
2628 
2629 start_size_offset_trials_t *
generate_start_size_offset_trials()2630 generate_start_size_offset_trials()
2631 {
2632 	const offset_list_t *start_offsets = get_ssoo_absolute_and_relative_offsets();
2633 	const offset_list_t *offset_values = get_ssoo_absolute_offsets();
2634 	const offset_list_t *size_offsets  = get_ssoo_absolute_and_relative_offsets();
2635 
2636 	// output is actually ordered start - offset - size
2637 	// because it pretty-prints better than start - size - offset
2638 	unsigned num_trials = start_offsets->count * offset_values->count * size_offsets->count;
2639 	start_size_offset_trials_t * trials = allocate_start_size_offset_trials(num_trials);
2640 	for (size_t a = 0; a < start_offsets->count; a++) {
2641 		for (size_t b = 0; b < offset_values->count; b++) {
2642 			for (size_t c = 0; c < size_offsets->count; c++) {
2643 				bool start_is_absolute = start_offsets->list[a].is_absolute;
2644 				bool size_is_absolute = size_offsets->list[c].is_absolute;
2645 				mach_vm_address_t start = start_offsets->list[a].offset;
2646 				vm_object_offset_t offset = offset_values->list[b].offset;
2647 				mach_vm_size_t size = size_offsets->list[c].offset;
2648 
2649 				char *str;
2650 				kasprintf(&str, "start: %s0x%llx, offset: 0x%llx, size: %s0x%llx",
2651 				    start_is_absolute ? "" : "base+", start,
2652 				    offset,
2653 				    size_is_absolute ? "" :"-start+", size);
2654 				append_trial(trials, START_SIZE_OFFSET_TRIAL(start, size, offset, start_is_absolute, size_is_absolute, str));
2655 			}
2656 		}
2657 	}
2658 	return trials;
2659 }
2660 
2661 #define SMART_START_SIZE_OFFSET_TRIALS()                                        \
2662 	__attribute__((cleanup(cleanup_start_size_offset_trials)))              \
2663 	= generate_start_size_offset_trials();
2664 
2665 static void
cleanup_start_size_offset_trials(start_size_offset_trials_t ** trials)2666 cleanup_start_size_offset_trials(start_size_offset_trials_t **trials)
2667 {
2668 	for (size_t i = 0; i < (*trials)->count; i++) {
2669 		kfree_str((*trials)->list[i].name);
2670 	}
2671 	free_trials(*trials);
2672 }
2673 
2674 // src/dst/size: test a source address, a dest address,
2675 // and a common size that may be added to both addresses
2676 
2677 typedef struct {
2678 	addr_t src;
2679 	addr_t dst;
2680 	addr_t size;
2681 	char *name;
2682 	bool src_is_absolute;  // src computation does not include any allocation's base address
2683 	bool dst_is_absolute;  // dst computation does not include any allocation's base address
2684 	bool size_is_src_relative;   // size computation includes src
2685 	bool size_is_dst_relative;   // size computation includes dst
2686 } src_dst_size_trial_t;
2687 
2688 typedef struct {
2689 	unsigned count;
2690 	unsigned capacity;
2691 	src_dst_size_trial_t list[];
2692 } src_dst_size_trials_t;
2693 
TRIALS_IMPL(src_dst_size)2694 TRIALS_IMPL(src_dst_size)
2695 
2696 #define SRC_DST_SIZE_TRIAL(new_src, new_dst, new_size, new_name, src_absolute, dst_absolute, size_src_rel, size_dst_rel) \
2697 	(src_dst_size_trial_t){                                         \
2698 	        .src = (addr_t)(new_src),                               \
2699 	        .dst = (addr_t)(new_dst),                               \
2700 	        .size = (addr_t)(new_size),                             \
2701 	        .name = new_name,                                       \
2702 	        .src_is_absolute = src_absolute,                        \
2703 	        .dst_is_absolute = dst_absolute,                        \
2704 	        .size_is_src_relative = size_src_rel,                   \
2705 	        .size_is_dst_relative = size_dst_rel,                   \
2706 	}
2707 
2708 src_dst_size_trials_t * __attribute__((overloadable))
2709 generate_src_dst_size_trials(const char *srcname, const char *dstname)
2710 {
2711 	const offset_list_t *addr_offsets = get_addr_trial_offsets();
2712 	const offset_list_t *size_offsets = get_size_trial_offsets();
2713 	unsigned src_count = addr_offsets->count;
2714 	unsigned dst_count = src_count;
2715 	unsigned size_count = 3 * size_offsets->count;
2716 	unsigned num_trials = src_count * dst_count * size_count;
2717 	src_dst_size_trials_t * trials = allocate_src_dst_size_trials(num_trials);
2718 
2719 	// each size is used three times:
2720 	// once src-relative, once dst-relative, and once absolute
2721 	unsigned size_part = size_count / 3;
2722 
2723 	for (size_t i = 0; i < src_count; i++) {
2724 		bool rebase_src = !addr_offsets->list[i].is_absolute;
2725 		addr_t src_offset = addr_offsets->list[i].offset;
2726 
2727 		for (size_t j = 0; j < dst_count; j++) {
2728 			bool rebase_dst = !addr_offsets->list[j].is_absolute;
2729 			addr_t dst_offset = addr_offsets->list[j].offset;
2730 
2731 			for (size_t k = 0; k < size_count; k++) {
2732 				bool rebase_size_from_src = false;
2733 				bool rebase_size_from_dst = false;
2734 				addr_t size_offset;
2735 				if (k < size_part) {
2736 					size_offset = size_offsets->list[k].offset;
2737 				} else if (k < 2 * size_part) {
2738 					size_offset = size_offsets->list[k - size_part].offset;
2739 					rebase_size_from_src = true;
2740 					rebase_size_from_dst = false;
2741 				} else {
2742 					size_offset = size_offsets->list[k - 2 * size_part].offset;
2743 					rebase_size_from_src = false;
2744 					rebase_size_from_dst = true;
2745 				}
2746 
2747 				addr_t size;
2748 				char *desc;
2749 				if (rebase_size_from_src) {
2750 					size = -src_offset + size_offset;
2751 					kasprintf(&desc, "%s: %s%lli, %s: %s%lli, size: -%s%+lli",
2752 					    srcname, rebase_src ? "base+" : "", (int64_t)src_offset,
2753 					    dstname, rebase_dst ? "base+" : "", (int64_t)dst_offset,
2754 					    srcname, (int64_t)size_offset);
2755 				} else if (rebase_size_from_dst) {
2756 					size = -dst_offset + size_offset;
2757 					kasprintf(&desc, "%s: %s%lli, %s: %s%lli, size: -%s%+lli",
2758 					    srcname, rebase_src ? "base+" : "", (int64_t)src_offset,
2759 					    dstname, rebase_dst ? "base+" : "", (int64_t)dst_offset,
2760 					    dstname, (int64_t)size_offset);
2761 				} else {
2762 					size = size_offset;
2763 					kasprintf(&desc, "%s: %s%lli, %s: %s%lli, size: %lli",
2764 					    srcname, rebase_src ? "base+" : "", (int64_t)src_offset,
2765 					    dstname, rebase_dst ? "base+" : "", (int64_t)dst_offset,
2766 					    (int64_t)size_offset);
2767 				}
2768 				assert(desc);
2769 				append_trial(trials, SRC_DST_SIZE_TRIAL(src_offset, dst_offset, size, desc,
2770 				    !rebase_src, !rebase_dst, rebase_size_from_src, rebase_size_from_dst));
2771 			}
2772 		}
2773 	}
2774 	return trials;
2775 }
2776 
2777 src_dst_size_trials_t * __attribute__((overloadable))
generate_src_dst_size_trials(void)2778 generate_src_dst_size_trials(void)
2779 {
2780 	return generate_src_dst_size_trials("src", "dst");
2781 }
2782 #define SMART_SRC_DST_SIZE_TRIALS()                                     \
2783 	__attribute__((cleanup(cleanup_src_dst_size_trials)))           \
2784 	= generate_src_dst_size_trials();
2785 
2786 #define SMART_FILEOFF_DST_SIZE_TRIALS()                                 \
2787 	__attribute__((cleanup(cleanup_src_dst_size_trials)))           \
2788 	= generate_src_dst_size_trials("fileoff", "dst");
2789 
2790 static void
cleanup_src_dst_size_trials(src_dst_size_trials_t ** trials)2791 cleanup_src_dst_size_trials(src_dst_size_trials_t **trials)
2792 {
2793 	for (size_t i = 0; i < (*trials)->count; i++) {
2794 		kfree_str((*trials)->list[i].name);
2795 	}
2796 	free_trials(*trials);
2797 }
2798 
2799 static src_dst_size_trial_t __attribute__((overloadable, used))
slide_trial_src(src_dst_size_trial_t trial,mach_vm_address_t slide)2800 slide_trial_src(src_dst_size_trial_t trial, mach_vm_address_t slide)
2801 {
2802 	src_dst_size_trial_t result = trial;
2803 
2804 	if (!trial.src_is_absolute) {
2805 		result.src += slide;
2806 		if (trial.size_is_src_relative) {
2807 			result.size -= slide;
2808 		}
2809 	}
2810 	return result;
2811 }
2812 
2813 static src_dst_size_trial_t __attribute__((overloadable, used))
slide_trial_dst(src_dst_size_trial_t trial,mach_vm_address_t slide)2814 slide_trial_dst(src_dst_size_trial_t trial, mach_vm_address_t slide)
2815 {
2816 	src_dst_size_trial_t result = trial;
2817 
2818 	if (!trial.dst_is_absolute) {
2819 		result.dst += slide;
2820 		if (trial.size_is_dst_relative) {
2821 			result.size -= slide;
2822 		}
2823 	}
2824 	return result;
2825 }
2826 
2827 #if !KERNEL
2828 // shared_file_np / shared_file_mapping_slide_np tests
2829 
2830 // copied from bsd/vm/vm_unix.c
2831 #define _SR_FILE_MAPPINGS_MAX_FILES     256
2832 #define SFM_MAX (_SR_FILE_MAPPINGS_MAX_FILES * 8)
2833 
2834 // From Rosetta dyld
2835 #define kNumSharedCacheMappings 4
2836 #define kMaxSubcaches 16
2837 
2838 typedef struct {
2839 	uint32_t files_count;
2840 	struct shared_file_np *files;
2841 	char *name;
2842 } shared_file_np_trial_t;
2843 
2844 typedef struct {
2845 	unsigned count;
2846 	unsigned capacity;
2847 	shared_file_np_trial_t list[];
2848 } shared_file_np_trials_t;
2849 
TRIALS_IMPL(shared_file_np)2850 TRIALS_IMPL(shared_file_np)
2851 
2852 #define SHARED_FILE_NP_TRIAL(new_files_count, new_files, new_name) \
2853 (shared_file_np_trial_t){ .files_count = (uint32_t)(new_files_count), \
2854 	    .files = (struct shared_file_np *)(new_files), \
2855 	    .name = "files_count="#new_files_count new_name }
2856 
2857 struct shared_file_np *
2858 alloc_shared_file_np(uint32_t files_count)
2859 {
2860 	struct shared_file_np *files;
2861 #if KERNEL
2862 	files = kalloc_type(struct shared_file_np, files_count, Z_WAITOK | Z_ZERO);
2863 #else
2864 	files = calloc(files_count, sizeof(struct shared_file_np));
2865 #endif
2866 	return files;
2867 }
2868 
2869 void
free_shared_file_np(shared_file_np_trial_t * trial)2870 free_shared_file_np(shared_file_np_trial_t *trial)
2871 {
2872 #if KERNEL
2873 	// some trials have files_count > 0 but null files.
2874 	if (trial->files) {
2875 		kfree_type(struct shared_file_np, trial->files_count, trial->files);
2876 	}
2877 #else
2878 	free(trial->files);
2879 #endif
2880 }
2881 
2882 static int get_fd();
2883 
2884 shared_file_np_trials_t *
get_shared_file_np_trials(uint64_t dyld_fd)2885 get_shared_file_np_trials(uint64_t dyld_fd)
2886 {
2887 	struct shared_file_np * files = NULL;
2888 	shared_file_np_trials_t *trials = allocate_shared_file_np_trials(11);
2889 	append_trial(trials, SHARED_FILE_NP_TRIAL(0, NULL, " (NULL files)"));
2890 	append_trial(trials, SHARED_FILE_NP_TRIAL(1, NULL, " (NULL files)"));
2891 	append_trial(trials, SHARED_FILE_NP_TRIAL(_SR_FILE_MAPPINGS_MAX_FILES - 1, NULL, " (NULL files)"));
2892 	append_trial(trials, SHARED_FILE_NP_TRIAL(_SR_FILE_MAPPINGS_MAX_FILES, NULL, " (NULL files)"));
2893 	append_trial(trials, SHARED_FILE_NP_TRIAL(_SR_FILE_MAPPINGS_MAX_FILES + 1, NULL, " (NULL files)"));
2894 	files = alloc_shared_file_np(1);
2895 	append_trial(trials, SHARED_FILE_NP_TRIAL(1, files, ""));
2896 	files = alloc_shared_file_np(_SR_FILE_MAPPINGS_MAX_FILES - 1);
2897 	append_trial(trials, SHARED_FILE_NP_TRIAL(_SR_FILE_MAPPINGS_MAX_FILES - 1, files, ""));
2898 	files = alloc_shared_file_np(_SR_FILE_MAPPINGS_MAX_FILES);
2899 	append_trial(trials, SHARED_FILE_NP_TRIAL(_SR_FILE_MAPPINGS_MAX_FILES, files, ""));
2900 	files = alloc_shared_file_np(_SR_FILE_MAPPINGS_MAX_FILES + 1);
2901 	append_trial(trials, SHARED_FILE_NP_TRIAL(_SR_FILE_MAPPINGS_MAX_FILES + 1, files, ""));
2902 	files = alloc_shared_file_np(1);
2903 	files->sf_fd = get_fd();
2904 	files->sf_slide = 4096;
2905 	files->sf_mappings_count = 1;
2906 	append_trial(trials, SHARED_FILE_NP_TRIAL(1, files, " non-zero shared_file_np"));
2907 	files = alloc_shared_file_np(2);
2908 	files[0].sf_fd = (int)dyld_fd;
2909 	files[0].sf_mappings_count = 1;
2910 	files[1].sf_fd = files[0].sf_fd;
2911 	files[1].sf_mappings_count = 4;
2912 	append_trial(trials, SHARED_FILE_NP_TRIAL(2, files, " checks shared_file_np"));
2913 	return trials;
2914 }
2915 
2916 static void
cleanup_shared_file_np_trials(shared_file_np_trials_t ** trials)2917 cleanup_shared_file_np_trials(shared_file_np_trials_t **trials)
2918 {
2919 	for (size_t i = 0; i < (*trials)->count; i++) {
2920 		free_shared_file_np(&(*trials)->list[i]);
2921 	}
2922 	free_trials(*trials);
2923 }
2924 
2925 typedef struct {
2926 	uint32_t mappings_count;
2927 	struct shared_file_mapping_slide_np *mappings;
2928 	char *name;
2929 } shared_file_mapping_slide_np_trial_t;
2930 
2931 typedef struct {
2932 	unsigned count;
2933 	unsigned capacity;
2934 	shared_file_mapping_slide_np_trial_t list[];
2935 } shared_file_mapping_slide_np_trials_t;
2936 
TRIALS_IMPL(shared_file_mapping_slide_np)2937 TRIALS_IMPL(shared_file_mapping_slide_np)
2938 
2939 #define SHARED_FILE_MAPPING_SLIDE_NP_TRIAL(new_mappings_count, new_mappings, new_name) \
2940 (shared_file_mapping_slide_np_trial_t){ .mappings_count = (uint32_t)(new_mappings_count), \
2941 	    .mappings = (struct shared_file_mapping_slide_np *)(new_mappings), \
2942 	    .name = "mappings_count="#new_mappings_count new_name }
2943 
2944 struct shared_file_mapping_slide_np *
2945 alloc_shared_file_mapping_slide_np(uint32_t mappings_count)
2946 {
2947 	struct shared_file_mapping_slide_np *mappings;
2948 #if KERNEL
2949 	mappings = kalloc_type(struct shared_file_mapping_slide_np, mappings_count, Z_WAITOK | Z_ZERO);
2950 #else
2951 	mappings = calloc(mappings_count, sizeof(struct shared_file_mapping_slide_np));
2952 #endif
2953 	return mappings;
2954 }
2955 
2956 void
free_shared_file_mapping_slide_np(shared_file_mapping_slide_np_trial_t * trial)2957 free_shared_file_mapping_slide_np(shared_file_mapping_slide_np_trial_t *trial)
2958 {
2959 #if KERNEL
2960 	// some trials have files_count > 0 but null files.
2961 	if (trial->mappings) {
2962 		kfree_type(struct shared_file_mapping_slide_np, trial->mappings_count, trial->mappings);
2963 	}
2964 #else
2965 	free(trial->mappings);
2966 #endif
2967 }
2968 
2969 typedef enum { MP_NORMAL = 0, MP_ADDR_SIZE = 1, MP_OFFSET_SIZE, MP_PROTS } mapping_slide_np_test_style_t;
2970 
2971 static inline struct shared_file_mapping_slide_np *
alloc_and_fill_shared_file_mappings(uint32_t num_mappings,mapping_slide_np_test_style_t style)2972 alloc_and_fill_shared_file_mappings(uint32_t num_mappings, mapping_slide_np_test_style_t style)
2973 {
2974 	assert(num_mappings > 0);
2975 	struct shared_file_mapping_slide_np *mappings = alloc_shared_file_mapping_slide_np(num_mappings);
2976 
2977 	// Checks happen in a for-loop so is desirable to differentiate the first mapping.
2978 	switch (style) {
2979 	case MP_NORMAL:
2980 		mappings[0].sms_slide_size = KB4;
2981 		mappings[0].sms_slide_start = KB4;
2982 		mappings[0].sms_max_prot = VM_PROT_DEFAULT;
2983 		mappings[0].sms_init_prot = VM_PROT_DEFAULT;
2984 		break;
2985 	case MP_ADDR_SIZE:
2986 		mappings[0].sms_address = 1;
2987 		mappings[0].sms_size = UINT64_MAX;
2988 		mappings[0].sms_file_offset = 0;
2989 		mappings[0].sms_slide_size = KB4;
2990 		mappings[0].sms_slide_start = KB4;
2991 		mappings[0].sms_max_prot = VM_PROT_DEFAULT;
2992 		mappings[0].sms_init_prot = VM_PROT_DEFAULT;
2993 		break;
2994 	case MP_OFFSET_SIZE:
2995 		mappings[0].sms_size = 0;
2996 		mappings[0].sms_file_offset = UINT64_MAX;
2997 		mappings[0].sms_slide_size = KB4;
2998 		mappings[0].sms_slide_start = KB4;
2999 		mappings[0].sms_max_prot = VM_PROT_DEFAULT;
3000 		mappings[0].sms_init_prot = VM_PROT_DEFAULT;
3001 		break;
3002 	case MP_PROTS:
3003 		mappings[0].sms_slide_size = KB4;
3004 		mappings[0].sms_slide_start = KB4;
3005 		mappings[0].sms_max_prot = VM_PROT_DEFAULT;
3006 		mappings[0].sms_init_prot = INT_MAX;
3007 		break;
3008 	default:
3009 		assert(0);
3010 		break;
3011 	}
3012 
3013 	for (size_t idx = 1; idx < num_mappings; idx++) {
3014 		size_t i = idx % 4;
3015 		switch (i) {
3016 		case 0:
3017 			mappings[idx].sms_slide_size = KB4;
3018 			mappings[idx].sms_slide_start = KB4;
3019 			mappings[idx].sms_max_prot = VM_PROT_DEFAULT;
3020 			mappings[idx].sms_init_prot = VM_PROT_DEFAULT;
3021 			break;
3022 		case 1:
3023 			mappings[idx].sms_slide_size = KB4;
3024 			mappings[idx].sms_slide_start = UINT64_MAX;
3025 			mappings[idx].sms_max_prot = VM_PROT_DEFAULT;
3026 			mappings[idx].sms_init_prot = VM_PROT_DEFAULT;
3027 			break;
3028 		case 2:
3029 			mappings[idx].sms_slide_size = 0;
3030 			mappings[idx].sms_slide_start = UINT64_MAX;
3031 			mappings[idx].sms_max_prot = VM_PROT_DEFAULT;
3032 			mappings[idx].sms_init_prot = INT_MAX;
3033 			break;
3034 		case 3:
3035 			mappings[idx].sms_slide_size = KB4;
3036 			mappings[idx].sms_slide_start = 0;
3037 			mappings[idx].sms_max_prot = INT_MAX;
3038 			mappings[idx].sms_init_prot = VM_PROT_DEFAULT;
3039 			break;
3040 		default:
3041 			assert(0);
3042 			break;
3043 		}
3044 	}
3045 	return mappings;
3046 }
3047 
3048 shared_file_mapping_slide_np_trials_t*
get_shared_file_mapping_slide_np_trials(void)3049 get_shared_file_mapping_slide_np_trials(void)
3050 {
3051 	struct shared_file_mapping_slide_np *mappings = NULL;
3052 	shared_file_mapping_slide_np_trials_t *trials = allocate_shared_file_mapping_slide_np_trials(14);
3053 	append_trial(trials, SHARED_FILE_MAPPING_SLIDE_NP_TRIAL(0, NULL, " (NULL mappings)"));
3054 	append_trial(trials, SHARED_FILE_MAPPING_SLIDE_NP_TRIAL(1, NULL, " (NULL mappings)"));
3055 	append_trial(trials, SHARED_FILE_MAPPING_SLIDE_NP_TRIAL(SFM_MAX - 1, NULL, " (NULL mappings)"));
3056 	append_trial(trials, SHARED_FILE_MAPPING_SLIDE_NP_TRIAL(SFM_MAX, NULL, " (NULL mappings)"));
3057 	append_trial(trials, SHARED_FILE_MAPPING_SLIDE_NP_TRIAL(SFM_MAX + 1, NULL, " (NULL mappings)"));
3058 	mappings = alloc_and_fill_shared_file_mappings(1, MP_NORMAL);
3059 	append_trial(trials, SHARED_FILE_MAPPING_SLIDE_NP_TRIAL(1, mappings, " (normal)"));
3060 	mappings = alloc_and_fill_shared_file_mappings(1, MP_ADDR_SIZE);
3061 	append_trial(trials, SHARED_FILE_MAPPING_SLIDE_NP_TRIAL(1, mappings, " (sms_address+sms_size check)"));
3062 	mappings = alloc_and_fill_shared_file_mappings(1, MP_OFFSET_SIZE);
3063 	append_trial(trials, SHARED_FILE_MAPPING_SLIDE_NP_TRIAL(1, mappings, " (sms_file_offset+sms_size check)"));
3064 	mappings = alloc_and_fill_shared_file_mappings(1, MP_PROTS);
3065 	append_trial(trials, SHARED_FILE_MAPPING_SLIDE_NP_TRIAL(1, mappings, " (sms_init_prot check)"));
3066 	mappings = alloc_and_fill_shared_file_mappings(SFM_MAX - 1, MP_NORMAL);
3067 	append_trial(trials, SHARED_FILE_MAPPING_SLIDE_NP_TRIAL(SFM_MAX - 1, mappings, ""));
3068 	mappings = alloc_and_fill_shared_file_mappings(SFM_MAX, MP_NORMAL);
3069 	append_trial(trials, SHARED_FILE_MAPPING_SLIDE_NP_TRIAL(SFM_MAX, mappings, ""));
3070 	mappings = alloc_and_fill_shared_file_mappings(SFM_MAX + 1, MP_NORMAL);
3071 	append_trial(trials, SHARED_FILE_MAPPING_SLIDE_NP_TRIAL(SFM_MAX + 1, mappings, ""));
3072 	mappings = alloc_and_fill_shared_file_mappings(kNumSharedCacheMappings, MP_NORMAL);
3073 	append_trial(trials, SHARED_FILE_MAPPING_SLIDE_NP_TRIAL(kNumSharedCacheMappings, mappings, ""));
3074 	mappings = alloc_and_fill_shared_file_mappings(2 * kNumSharedCacheMappings, MP_NORMAL);
3075 	append_trial(trials, SHARED_FILE_MAPPING_SLIDE_NP_TRIAL(2 * kNumSharedCacheMappings, mappings, ""));
3076 
3077 	return trials;
3078 }
3079 
3080 static void
cleanup_shared_file_mapping_slide_np_trials(shared_file_mapping_slide_np_trials_t ** trials)3081 cleanup_shared_file_mapping_slide_np_trials(shared_file_mapping_slide_np_trials_t **trials)
3082 {
3083 	for (size_t i = 0; i < (*trials)->count; i++) {
3084 		free_shared_file_mapping_slide_np(&(*trials)->list[i]);
3085 	}
3086 	free_trials(*trials);
3087 }
3088 
3089 typedef struct {
3090 	uint32_t files_count;
3091 	struct shared_file_np *files;
3092 	uint32_t mappings_count;
3093 	struct shared_file_mapping_slide_np *mappings;
3094 	char *name;
3095 } shared_region_map_and_slide_2_trial_t;
3096 
3097 typedef struct {
3098 	unsigned count;
3099 	unsigned capacity;
3100 	shared_file_np_trials_t *shared_files_trials;
3101 	shared_file_mapping_slide_np_trials_t *shared_mappings_trials;
3102 	shared_region_map_and_slide_2_trial_t list[];
3103 } shared_region_map_and_slide_2_trials_t;
3104 
TRIALS_IMPL(shared_region_map_and_slide_2)3105 TRIALS_IMPL(shared_region_map_and_slide_2)
3106 
3107 #define SHARED_REGION_MAP_AND_SLIDE_2_TRIAL(new_files_count, new_files, new_mappings_count, new_mappings, new_name) \
3108 (shared_region_map_and_slide_2_trial_t){ .files_count = (uint32_t)(new_files_count), \
3109 	    .files = (struct shared_file_np *)(new_files), \
3110 	    .mappings_count = (uint32_t)(new_mappings_count), \
3111 	    .mappings = (struct shared_file_mapping_slide_np *)(new_mappings), \
3112 	    .name = new_name }
3113 
3114 shared_region_map_and_slide_2_trials_t *
3115 generate_shared_region_map_and_slide_2_trials(uint64_t dyld_fd)
3116 {
3117 	shared_file_np_trials_t *shared_files = get_shared_file_np_trials(dyld_fd);
3118 	shared_file_mapping_slide_np_trials_t *shared_mappings = get_shared_file_mapping_slide_np_trials();
3119 	unsigned num_trials = shared_files->count * shared_mappings->count;
3120 	shared_region_map_and_slide_2_trials_t *trials = allocate_shared_region_map_and_slide_2_trials(num_trials);
3121 	trials->shared_files_trials = shared_files;
3122 	trials->shared_mappings_trials = shared_mappings;
3123 	for (size_t i = 0; i < shared_files->count; i++) {
3124 		for (size_t j = 0; j < shared_mappings->count; j++) {
3125 			char *buf;
3126 			shared_file_np_trial_t shared_file = shared_files->list[i];
3127 			shared_file_mapping_slide_np_trial_t shared_mapping = shared_mappings->list[j];
3128 			kasprintf(&buf, "%s, %s", shared_file.name, shared_mapping.name);
3129 			append_trial(trials, SHARED_REGION_MAP_AND_SLIDE_2_TRIAL(shared_file.files_count, shared_file.files, shared_mapping.mappings_count, shared_mapping.mappings, buf));
3130 		}
3131 	}
3132 	return trials;
3133 }
3134 
3135 #define SMART_SHARED_REGION_MAP_AND_SLIDE_2_TRIALS(dyld_fd)    \
3136 	__attribute__((cleanup(cleanup_shared_region_map_and_slide_2_trials))) \
3137 	= generate_shared_region_map_and_slide_2_trials(dyld_fd);
3138 
3139 static void __attribute__((used))
cleanup_shared_region_map_and_slide_2_trials(shared_region_map_and_slide_2_trials_t ** trials)3140 cleanup_shared_region_map_and_slide_2_trials(shared_region_map_and_slide_2_trials_t **trials)
3141 {
3142 	for (size_t i = 0; i < (*trials)->count; i++) {
3143 		kfree_str((*trials)->list[i].name);
3144 	}
3145 	cleanup_shared_file_np_trials(&(*trials)->shared_files_trials);
3146 	cleanup_shared_file_mapping_slide_np_trials(&(*trials)->shared_mappings_trials);
3147 	free_trials(*trials);
3148 }
3149 #endif // !KERNEL
3150 
3151 /////////////////////////////////////////////////////
3152 // utility code
3153 
3154 // Return true if flags has VM_FLAGS_FIXED
3155 // This is non-trivial because VM_FLAGS_FIXED is zero;
3156 // the real value is the absence of VM_FLAGS_ANYWHERE.
3157 static inline bool
is_fixed(int flags)3158 is_fixed(int flags)
3159 {
3160 	static_assert(VM_FLAGS_FIXED == 0, "this test requies VM_FLAGS_FIXED be zero");
3161 	static_assert(VM_FLAGS_ANYWHERE != 0, "this test requires VM_FLAGS_ANYWHERE be nonzero");
3162 	return !(flags & VM_FLAGS_ANYWHERE);
3163 }
3164 
3165 // Return true if flags has VM_FLAGS_FIXED and VM_FLAGS_OVERWRITE set.
3166 static inline bool
is_fixed_overwrite(int flags)3167 is_fixed_overwrite(int flags)
3168 {
3169 	return is_fixed(flags) && (flags & VM_FLAGS_OVERWRITE);
3170 }
3171 
3172 
3173 // Return true if flags has VM_FLAGS_ANYWHERE and VM_FLAGS_RANDOM_ADDR set.
3174 static inline bool
is_random_anywhere(int flags)3175 is_random_anywhere(int flags)
3176 {
3177 	static_assert(VM_FLAGS_ANYWHERE != 0, "this test requires VM_FLAGS_ANYWHERE be nonzero");
3178 	return (flags & VM_FLAGS_RANDOM_ADDR) && (flags & VM_FLAGS_ANYWHERE);
3179 }
3180 
3181 // Deallocate [start, start+size).
3182 // Don't deallocate if the allocator failed (allocator_kr)
3183 // Don't deallocate if flags include FIXED | OVERWRITE (in which case
3184 //   the memory is a pre-existing allocation and should be left alone)
3185 static void
deallocate_if_not_fixed_overwrite(kern_return_t allocator_kr,MAP_T map,mach_vm_address_t start,mach_vm_size_t size,int flags)3186 deallocate_if_not_fixed_overwrite(kern_return_t allocator_kr, MAP_T map,
3187     mach_vm_address_t start, mach_vm_size_t size, int flags)
3188 {
3189 	if (is_fixed_overwrite(flags)) {
3190 		// fixed-overwrite with pre-existing allocation, don't deallocate
3191 	} else if (allocator_kr != 0) {
3192 		// allocator failed, don't deallocate
3193 	} else {
3194 		(void)mach_vm_deallocate(map, start, size);
3195 	}
3196 }
3197 
3198 // PPL is inefficient at deallocations of very large address ranges.
3199 // Skip those trials to avoid test timeouts.
3200 // We assume that tests on other devices will cover any testing gaps.
3201 static inline bool
dealloc_would_time_out(mach_vm_address_t addr __unused,mach_vm_size_t size __unused,vm_map_t map __unused)3202 dealloc_would_time_out(
3203 	mach_vm_address_t addr __unused,
3204 	mach_vm_size_t size __unused,
3205 	vm_map_t map __unused)
3206 {
3207 #if CONFIG_SPTM
3208 	/* not PPL - okay */
3209 	return false;
3210 #elif !(__ARM_42BIT_PA_SPACE__ || ARM_LARGE_MEMORY)
3211 	/* PPL but small pmap address space - okay */
3212 	return false;
3213 #else
3214 	/*
3215 	 * PPL with large pmap address space - bad
3216 	 * Pre-empt trials of very large allocations.
3217 	 */
3218 	return size > 0x8000000000;
3219 #endif
3220 }
3221 
3222 #if !KERNEL
3223 
3224 // SMART_MAP is mach_task_self() in userspace and a new empty map in kernel
3225 #define SMART_MAP = mach_task_self()
3226 
3227 // CURRENT_MAP is mach_task_self() in userspace and current_map() in kernel
3228 #define CURRENT_MAP = mach_task_self()
3229 
3230 #else
3231 
3232 static inline vm_map_t
create_map(mach_vm_address_t map_start,mach_vm_address_t map_end)3233 create_map(mach_vm_address_t map_start, mach_vm_address_t map_end)
3234 {
3235 	ledger_t ledger = ledger_instantiate(task_ledger_template, LEDGER_CREATE_ACTIVE_ENTRIES);
3236 	pmap_t pmap = pmap_create_options(ledger, 0, PMAP_CREATE_64BIT);
3237 	assert(pmap);
3238 	ledger_dereference(ledger);  // now retained by pmap
3239 	vm_map_t map = vm_map_create_options(pmap, map_start, map_end, VM_MAP_CREATE_PAGEABLE);
3240 	assert(map);
3241 
3242 	/*
3243 	 * Normally, we would vm_map_setup a task's map, but since we're breaking the assumed
3244 	 * 1:1 correspondence between map and task here, we must manually set up the map's
3245 	 * back pointer, without repeating any one-time task setup (e.g. registering reclaim
3246 	 * buffers)
3247 	 */
3248 	map->owning_task = current_task();
3249 
3250 	return map;
3251 }
3252 
3253 static inline void
cleanup_map(vm_map_t * map)3254 cleanup_map(vm_map_t *map)
3255 {
3256 	assert(*map);
3257 	kern_return_t kr = vm_map_terminate(*map);
3258 	assert(kr == 0);
3259 	vm_map_deallocate(*map);  // also destroys pmap
3260 }
3261 
3262 // kernel: create a new vm_map and deallocate it at end of scope
3263 // fixme choose a user-like and a kernel-like address range
3264 #define SMART_MAP                                                       \
3265 	__attribute__((cleanup(cleanup_map))) = create_map(0, 0xffffffffffffffff)
3266 
3267 // This map has a map_offset that matches what a user would get. This allows
3268 // vm_map_user_ranges to work properly when tested from the kernel
3269 #define SMART_RANGE_MAP                                                       \
3270 	__attribute__((cleanup(cleanup_map))) = create_map(0, vm_compute_max_offset(true))
3271 
3272 #define CURRENT_MAP = current_map()
3273 
3274 #endif
3275 
3276 // Allocate with an address hint.
3277 static kern_return_t
allocate_after(MAP_T map,mach_vm_address_t * address,mach_vm_size_t size,mach_vm_size_t align_mask,int additional_map_flags)3278 allocate_after(
3279 	MAP_T               map,
3280 	mach_vm_address_t  *address,
3281 	mach_vm_size_t      size,
3282 	mach_vm_size_t      align_mask,
3283 	int                 additional_map_flags)
3284 {
3285 	return mach_vm_map(map, address, size, align_mask,
3286 	           VM_FLAGS_ANYWHERE | additional_map_flags, 0, 0, 0,
3287 	           VM_PROT_DEFAULT, VM_PROT_ALL, VM_INHERIT_DEFAULT);
3288 }
3289 
3290 static inline mach_vm_address_t
default_allocation_address_hint(void)3291 default_allocation_address_hint(void)
3292 {
3293 	/*
3294 	 * Try to allocate after address 2 GB. It is important in
3295 	 * in-kernel tests of empty maps to avoid addresses near 0 and ~0.
3296 	 */
3297 	return 2ull * 1024 * 1024 * 1024;
3298 }
3299 
3300 // allocate a purgeable VM region with size and permissions
3301 // and deallocate it at end of scope
3302 #define SMART_ALLOCATE_PURGEABLE_VM(map, size, perm)                              \
3303     __attribute__((cleanup(cleanup_allocation))) = create_allocation(map, size, 0, perm, false, VM_FLAGS_PURGABLE)
3304 
3305 // allocate a VM region with size and permissions
3306 // and deallocate it at end of scope
3307 #define SMART_ALLOCATE_VM(map, size, perm)                              \
3308     __attribute__((cleanup(cleanup_allocation))) = create_allocation(map, size, 0, perm, false, 0)
3309 
3310 // allocate a VM region with size and permissions
3311 // and an address hint to allocate after
3312 // and deallocate it at end of scope
3313 #define SMART_ALLOCATE_VM_AFTER(map, address_hint, size, perm)          \
3314     __attribute__((cleanup(cleanup_allocation))) = create_allocation_after(map, address_hint, size, 0, perm, false, 0)
3315 
3316 // allocate a VM region with size and permissions and alignment
3317 // and deallocate it at end of scope
3318 #define SMART_ALLOCATE_ALIGNED_VM(map, size, align_mask, perm)          \
3319     __attribute__((cleanup(cleanup_allocation))) = create_allocation(map, size, align_mask, perm, false, 0)
3320 
3321 // allocate a VM region with size and permissions
3322 // and deallocate it at end of scope
3323 // If no such region could be allocated, return {.addr = 0}
3324 #define SMART_TRY_ALLOCATE_VM(map, size, perm)                              \
3325     __attribute__((cleanup(cleanup_allocation))) = create_allocation(map, size, 0, perm, true, 0)
3326 
3327 // a VM allocation with unallocated pages around it
3328 typedef struct {
3329 	MAP_T map;
3330 	addr_t guard_size;
3331 	addr_t guard_prefix;        // guard_size bytes
3332 	addr_t unallocated_prefix;  // guard_size bytes
3333 	addr_t addr;
3334 	addr_t size;
3335 	addr_t unallocated_suffix;  // guard_size bytes
3336 	addr_t guard_suffix;        // guard_size bytes
3337 } allocation_t;
3338 
3339 static allocation_t
create_allocation_after(MAP_T new_map,mach_vm_address_t address_hint,mach_vm_address_t new_size,mach_vm_size_t align_mask,vm_prot_t perm,bool allow_failure,int additional_map_flags)3340 create_allocation_after(MAP_T new_map, mach_vm_address_t address_hint, mach_vm_address_t new_size, mach_vm_size_t align_mask,
3341     vm_prot_t perm, bool allow_failure, int additional_map_flags)
3342 {
3343 	// allocations in address order:
3344 	// 16K guard_prefix (allocated, prot none)
3345 	// 16K unallocated_prefix (unallocated)
3346 	// N   addr..addr+size
3347 	// 16K unallocated_suffix (unallocated)
3348 	// 16K guard_suffix (allocated, prot none)
3349 
3350 	// allocate new_size + 4 * 16K bytes
3351 	// then carve it up into our regions
3352 
3353 	allocation_t result;
3354 
3355 	result.map = new_map;
3356 
3357 	// this implementation only works with some alignment values
3358 	assert(align_mask == 0 || align_mask == KB4 - 1 || align_mask == KB16 - 1);
3359 
3360 	result.guard_size = KB16;
3361 	result.size = round_up_page(new_size, KB16);
3362 	if (result.size == 0 && allow_failure) {
3363 		return (allocation_t){new_map, 0, 0, 0, 0, 0, 0, 0};
3364 	}
3365 	assert(result.size != 0);
3366 
3367 	mach_vm_address_t allocated_base = address_hint;
3368 	mach_vm_size_t allocated_size = result.size;
3369 	if (__builtin_add_overflow(result.size, result.guard_size * 4, &allocated_size)) {
3370 		if (allow_failure) {
3371 			return (allocation_t){new_map, 0, 0, 0, 0, 0, 0, 0};
3372 		} else {
3373 			assert(false);
3374 		}
3375 	}
3376 
3377 	kern_return_t kr;
3378 	kr = allocate_after(result.map, &allocated_base, allocated_size,
3379 	    align_mask, additional_map_flags);
3380 	if (kr != 0 && allow_failure) {
3381 		return (allocation_t){new_map, 0, 0, 0, 0, 0, 0, 0};
3382 	}
3383 	assert(kr == 0);
3384 
3385 	result.guard_prefix = (addr_t)allocated_base;
3386 	result.unallocated_prefix = result.guard_prefix + result.guard_size;
3387 	result.addr = result.unallocated_prefix + result.guard_size;
3388 	result.unallocated_suffix = result.addr + result.size;
3389 	result.guard_suffix = result.unallocated_suffix + result.guard_size;
3390 
3391 	kr = mach_vm_protect(result.map, result.addr, result.size, false, perm);
3392 	assert(kr == 0);
3393 	kr = mach_vm_protect(result.map, result.guard_prefix, result.guard_size, true, VM_PROT_NONE);
3394 	assert(kr == 0);
3395 	kr = mach_vm_protect(result.map, result.guard_suffix, result.guard_size, true, VM_PROT_NONE);
3396 	assert(kr == 0);
3397 	kr = mach_vm_deallocate(result.map, result.unallocated_prefix, result.guard_size);
3398 	assert(kr == 0);
3399 	kr = mach_vm_deallocate(result.map, result.unallocated_suffix, result.guard_size);
3400 	assert(kr == 0);
3401 
3402 	return result;
3403 }
3404 
3405 static allocation_t
create_allocation(MAP_T new_map,mach_vm_address_t new_size,mach_vm_size_t align_mask,vm_prot_t perm,bool allow_failure,int additional_map_flags)3406 create_allocation(MAP_T new_map, mach_vm_address_t new_size, mach_vm_size_t align_mask,
3407     vm_prot_t perm, bool allow_failure, int additional_map_flags)
3408 {
3409 	mach_vm_address_t address_hint = default_allocation_address_hint();
3410 	return create_allocation_after(new_map, address_hint, new_size, align_mask, perm, allow_failure, additional_map_flags);
3411 }
3412 
3413 // Mark this allocation as deallocated by something else.
3414 // This means cleanup_allocation() won't deallocate it twice.
3415 // cleanup_allocation() will still free the guard pages.
3416 static void
set_already_deallocated(allocation_t * allocation)3417 set_already_deallocated(allocation_t *allocation)
3418 {
3419 	allocation->addr = 0;
3420 	allocation->size = 0;
3421 }
3422 
3423 static void
cleanup_allocation(allocation_t * allocation)3424 cleanup_allocation(allocation_t *allocation)
3425 {
3426 	// fixme verify allocations and unallocated spaces still exist where we expect
3427 	if (allocation->size) {
3428 		(void)mach_vm_deallocate(allocation->map, allocation->addr, allocation->size);
3429 	}
3430 	if (allocation->guard_size) {
3431 		(void)mach_vm_deallocate(allocation->map, allocation->guard_prefix, allocation->guard_size);
3432 		(void)mach_vm_deallocate(allocation->map, allocation->guard_suffix, allocation->guard_size);
3433 	}
3434 }
3435 
3436 
3437 // unallocate a VM region with size
3438 // and deallocate it at end of scope
3439 #define SMART_UNALLOCATE_VM(map, size)                                  \
3440 	__attribute__((cleanup(cleanup_unallocation))) = create_unallocation(map, size)
3441 
3442 // unallocate a VM region with size
3443 // and an address hint to allocate above
3444 // and deallocate it at end of scope
3445 #define SMART_UNALLOCATE_VM_AFTER(map, address_hint, size)              \
3446 	__attribute__((cleanup(cleanup_unallocation))) = create_unallocation_after(map, address_hint, size, false)
3447 
3448 // unallocate a VM region with size
3449 // and deallocate it at end of scope
3450 // If no such region could be allocated, return {.addr = 0}
3451 #define SMART_TRY_UNALLOCATE_VM(map, size)                                  \
3452 	__attribute__((cleanup(cleanup_unallocation))) = create_unallocation(map, size, true)
3453 
3454 // a VM space with allocated pages around it
3455 typedef struct {
3456 	MAP_T map;
3457 	addr_t guard_size;
3458 	addr_t guard_prefix;  // 16K
3459 	addr_t addr;
3460 	addr_t size;
3461 	addr_t guard_suffix;  // 16K
3462 } unallocation_t;
3463 
3464 static unallocation_t __attribute__((overloadable))
create_unallocation_after(MAP_T new_map,mach_vm_address_t address_hint,mach_vm_address_t new_size,bool allow_failure)3465 create_unallocation_after(MAP_T new_map, mach_vm_address_t address_hint, mach_vm_address_t new_size, bool allow_failure)
3466 {
3467 	// allocations in address order:
3468 	// 16K guard_prefix (allocated, prot none)
3469 	// N   addr..addr+size (unallocated)
3470 	// 16K guard_suffix (allocated, prot none)
3471 
3472 	// allocate new_size + 2 * 16K bytes
3473 	// then carve it up into our regions
3474 
3475 	unallocation_t result;
3476 
3477 	result.map = new_map;
3478 
3479 	result.guard_size = KB16;
3480 	result.size = round_up_page(new_size, KB16);
3481 	if (result.size == 0 && allow_failure) {
3482 		return (unallocation_t){new_map, 0, 0, 0, 0, 0};
3483 	}
3484 	assert(result.size != 0);
3485 
3486 	mach_vm_address_t allocated_base = address_hint;
3487 	mach_vm_size_t allocated_size = result.size;
3488 	if (__builtin_add_overflow(result.size, result.guard_size * 2, &allocated_size)) {
3489 		if (allow_failure) {
3490 			return (unallocation_t){new_map, 0, 0, 0, 0, 0};
3491 		} else {
3492 			assert(false);
3493 		}
3494 	}
3495 	kern_return_t kr;
3496 	kr = allocate_after(result.map, &allocated_base, allocated_size, 0, 0);
3497 	if (kr != 0 && allow_failure) {
3498 		return (unallocation_t){new_map, 0, 0, 0, 0, 0};
3499 	}
3500 	assert(kr == 0);
3501 
3502 	result.guard_prefix = (addr_t)allocated_base;
3503 	result.addr = result.guard_prefix + result.guard_size;
3504 	result.guard_suffix = result.addr + result.size;
3505 
3506 	kr = mach_vm_deallocate(result.map, result.addr, result.size);
3507 	assert(kr == 0);
3508 	kr = mach_vm_protect(result.map, result.guard_prefix, result.guard_size, true, VM_PROT_NONE);
3509 	assert(kr == 0);
3510 	kr = mach_vm_protect(result.map, result.guard_suffix, result.guard_size, true, VM_PROT_NONE);
3511 	assert(kr == 0);
3512 
3513 	return result;
3514 }
3515 
3516 static unallocation_t __attribute__((overloadable))
create_unallocation(MAP_T new_map,mach_vm_address_t new_size,bool allow_failure)3517 create_unallocation(MAP_T new_map, mach_vm_address_t new_size, bool allow_failure)
3518 {
3519 	mach_vm_address_t address_hint = default_allocation_address_hint();
3520 	return create_unallocation_after(new_map, address_hint, new_size, allow_failure);
3521 }
3522 
3523 static unallocation_t __attribute__((overloadable))
create_unallocation(MAP_T new_map,mach_vm_address_t new_size)3524 create_unallocation(MAP_T new_map, mach_vm_address_t new_size)
3525 {
3526 	return create_unallocation(new_map, new_size, false /*allow_failure*/);
3527 }
3528 
3529 static void
cleanup_unallocation(unallocation_t * unallocation)3530 cleanup_unallocation(unallocation_t *unallocation)
3531 {
3532 	// fixme verify allocations and unallocated spaces still exist where we expect
3533 	if (unallocation->guard_size) {
3534 		(void)mach_vm_deallocate(unallocation->map, unallocation->guard_prefix, unallocation->guard_size);
3535 		(void)mach_vm_deallocate(unallocation->map, unallocation->guard_suffix, unallocation->guard_size);
3536 	}
3537 }
3538 
3539 // TODO: re-enable deferred reclaim tests (rdar://136157720)
3540 #if 0
3541 // vm_deferred_reclamation_buffer_init_internal tests
3542 typedef struct {
3543 	task_t task;
3544 	mach_vm_address_t address;
3545 	mach_vm_reclaim_count_t initial_capacity;
3546 	mach_vm_reclaim_count_t max_capacity;
3547 	char *name;
3548 } reclamation_buffer_init_trial_t;
3549 
3550 typedef struct {
3551 	unsigned count;
3552 	unsigned capacity;
3553 	reclamation_buffer_init_trial_t list[];
3554 } reclamation_buffer_init_trials_t;
3555 
3556 TRIALS_IMPL(reclamation_buffer_init)
3557 
3558 #define RECLAMATION_BUFFER_INIT_TRIAL(new_task, new_address, new_initial_capacity, new_max_capacity, new_name) \
3559 (reclamation_buffer_init_trial_t){ .task = (task_t)(new_task), \
3560 	    .address = (mach_vm_address_t)(new_address), \
3561 	    .initial_capacity= (mach_vm_reclaim_count_t)(new_initial_capacity), \
3562 	    .max_capacity= (mach_vm_reclaim_count_t)(new_max_capacity), \
3563 	    .name = new_name }
3564 
3565 #define RECLAMATION_BUFFER_INIT_EXTRA_TRIALS   7
3566 
3567 reclamation_buffer_init_trials_t *
3568 generate_reclamation_buffer_init_trials(void)
3569 {
3570 	MAP_T map SMART_MAP;
3571 	allocation_t base SMART_ALLOCATE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT);
3572 	addr_trials_t *addr_trials SMART_ADDR_TRIALS(0);
3573 	reclamation_buffer_init_trials_t *trials = allocate_reclamation_buffer_init_trials(addr_trials->count + RECLAMATION_BUFFER_INIT_EXTRA_TRIALS);
3574 	for (size_t i = 0; i < addr_trials->count; i++) {
3575 		char *buf;
3576 		mach_vm_size_t size = i * 512;
3577 		kasprintf(&buf, "%s, size: 0x%llu", addr_trials->list[i].name, size);
3578 		append_trial(trials, RECLAMATION_BUFFER_INIT_TRIAL(current_task(), addr_trials->list[i].addr, size, size, buf));
3579 	}
3580 
3581 	append_trial(trials, RECLAMATION_BUFFER_INIT_TRIAL(current_task(), base.addr, 0, 0, "size: 0"));
3582 	append_trial(trials, RECLAMATION_BUFFER_INIT_TRIAL(current_task(), base.addr, VM_RECLAIM_MAX_CAPACITY - 1, VM_RECLAIM_MAX_CAPACITY - 1, "size: MAX - 1"));
3583 	append_trial(trials, RECLAMATION_BUFFER_INIT_TRIAL(current_task(), base.addr, VM_RECLAIM_MAX_CAPACITY, VM_RECLAIM_MAX_CAPACITY, "size: MAX"));
3584 	append_trial(trials, RECLAMATION_BUFFER_INIT_TRIAL(current_task(), base.addr, UINT32_MAX, UINT32_MAX, "size: UINT32_MAX"));
3585 	append_trial(trials, RECLAMATION_BUFFER_INIT_TRIAL(current_task(), base.addr, 2, 1, "size: max < initial"));
3586 	append_trial(trials, RECLAMATION_BUFFER_INIT_TRIAL(NULL, NULL, 0, 0, "null task, null address, size: 0"));
3587 	append_trial(trials, RECLAMATION_BUFFER_INIT_TRIAL(current_task(), NULL, 0, 0, "null address, size: 0"));
3588 	append_trial(trials, RECLAMATION_BUFFER_INIT_TRIAL(current_task(), base.addr, 1024, 1024, "valid arguments to test KERN_NOT_SUPPORTED"));
3589 
3590 	return trials;
3591 }
3592 
3593 #define SMART_RECLAMATION_BUFFER_INIT_TRIALS()    \
3594 	__attribute__((cleanup(cleanup_reclamation_buffer_init_trials))) \
3595 	= generate_reclamation_buffer_init_trials();
3596 
3597 static void __attribute__((used))
3598 cleanup_reclamation_buffer_init_trials(reclamation_buffer_init_trials_t **trials)
3599 {
3600 	for (size_t i = 0; i < (*trials)->count - RECLAMATION_BUFFER_INIT_EXTRA_TRIALS; i++) {
3601 		kfree_str((*trials)->list[i].name);
3602 	}
3603 	free_trials(*trials);
3604 }
3605 
3606 static kern_return_t
3607 call_mach_vm_deferred_reclamation_buffer_init(task_t task, mach_vm_address_t address, mach_vm_reclaim_count_t initial_capacity, mach_vm_reclaim_count_t max_capacity)
3608 {
3609 	kern_return_t kr = 0;
3610 	mach_vm_address_t saved_address = address;
3611 	if (task && max_capacity > 0 && address == 0) {
3612 		// prevent assert3u(*address, !=, 0)
3613 		return PANIC;
3614 	}
3615 
3616 	kr = mach_vm_deferred_reclamation_buffer_allocate(task, &address, initial_capacity, max_capacity);
3617 
3618 	//Out-param validation, failure shouldn't change inout address.
3619 	if (kr != KERN_SUCCESS && saved_address != address) {
3620 		kr = OUT_PARAM_BAD;
3621 	}
3622 	if (kr == KERN_SUCCESS && saved_address == address) {
3623 		kr = OUT_PARAM_BAD;
3624 	}
3625 
3626 	return kr;
3627 }
3628 #endif // 0
3629 
3630 
3631 // mach_vm_remap_external/vm_remap_external/vm32_remap/mach_vm_remap_new_external infra
3632 // mach_vm_remap/mach_vm_remap_new_kernel infra
3633 
3634 /*
3635  * This comment describes the testing approach that was fleshed out through
3636  * writing the tests for the map family of functions, and more fully realized
3637  * for the remap family of functions.
3638  *
3639  * This method attempts to radically minimize code reuse, at the expense of
3640  * decreased navigability (cmd+click is unlikely to work for you for this code)
3641  * and increased upfront costs for understanding this code. Maintainability
3642  * should be better in most cases: if a fix needs to happen, it can be
3643  * implemented in the right place once and doesn’t need to be copy-and-pasted
3644  * in multiple duplicated functions. There may however be cases where the
3645  * change you want to make doesn’t fit the spirit of this approach (for
3646  * instance changing the behavior of the test for only one function in the
3647  * family).
3648  *
3649  * The framework is built around the idea that there are three types of
3650  * parameters:
3651  * 1. Parameters that will be fixed for all calls to the function (e.g. some
3652  *    uncommon type specific to the function that doesn’t impact the input
3653  *    validation flow)
3654  * 2. Parameters that cause input validation to change significantly (typically
3655  *    flags, e.g. fixed vs anywhere). For those we basically want to treat
3656  *    different values of the flags as calling into different functions (for
3657  *    the purpose of input validation).
3658  * 3. Parameters that can be tested. For every test this is further broken down
3659  *    into 2 subtypes:
3660  *        A. Parameters being iterated over during the test (e.g. start+size)
3661  *        B. Parameters that should stay fixed during this test (e.g. pick a
3662  *           sane value of prot and pass that same value for all values of
3663  *           start/size)
3664  *
3665  * Often, many functions have very similar signatures (they are in the same
3666  * function family). We want to avoid copy/pasting tests for each function in
3667  * the family.
3668  *
3669  * Here is the flow used for the remap family of functions:
3670  * 1. Typedef a function type with shared parameters (see remap_fn_t)
3671  * 2. Define function wrappers that fit the above typedef for each function
3672  *    in the family (see e.g. mach_vm_remap_new_kernel_wrapped). These might
3673  *    set values for “type 1” params.
3674  * 3. Define “helper” functions that take in parameters of types 2 and 3.A.,
3675  *    and call the wrapper, filling in type 3.B. params. See, e.g.,
3676  *    help_call_remap_fn__src_size. For remap, all helpers can easily be
3677  *    implemented as a single call to a core helper function
3678  *    help_call_remap_fn__src_size_etc.
3679  * 4. Define generic “caller” functions that take in a wrapper and parameters
3680  *    of type 3.A. and call the helper. Macros are used to mass implement these
3681  *    for all values of type 2 parameters and for all functions in the family.
3682  *    See, e.g., `IMPL_FROM_HELPER(dst_size);`.
3683  * 5. Specialize the above "caller" functions for each wrapper in the family,
3684  *    again using macros. See `#define IMPL(remap_fn)` and its uses below.
3685  *    This results in a number of specialized caller functions that is the
3686  *    product of the number of functions in the family by the number of
3687  *    variants induced by type 2 parameters.
3688  * 6. Use macros to call test harnesses on caller functions en masse at test
3689  *    time for all functions. See the call sites in `vm_parameter_validation.c`
3690  *    e.g. `RUN_ALL(mach_vm_remap_new_user, , mach_vm_remap_new);`.
3691  */
3692 
3693 typedef kern_return_t (*remap_fn_t)(vm_map_t target_task,
3694     mach_vm_address_t *target_address,
3695     mach_vm_size_t size,
3696     mach_vm_offset_t mask,
3697     int flags,
3698     vm_map_t src_task,
3699     mach_vm_address_t src_address,
3700     boolean_t copy,
3701     vm_prot_t *cur_protection,
3702     vm_prot_t *max_protection,
3703     vm_inherit_t inheritance);
3704 
3705 // helpers that call a provided function with certain sets of params
3706 
3707 static kern_return_t
help_call_remap_fn__src_size_etc(remap_fn_t fn,MAP_T map,int flags,bool copy,mach_vm_address_t src,mach_vm_size_t size,vm_prot_t cur,vm_prot_t max,vm_inherit_t inherit)3708 help_call_remap_fn__src_size_etc(remap_fn_t fn, MAP_T map, int flags, bool copy, mach_vm_address_t src, mach_vm_size_t size, vm_prot_t cur, vm_prot_t max, vm_inherit_t inherit)
3709 {
3710 	kern_return_t kr;
3711 #if KERNEL
3712 	if (is_random_anywhere(flags)) {
3713 		// RANDOM_ADDR is likely to fall outside pmap's range
3714 		return PANIC;
3715 	}
3716 #endif
3717 	if (is_fixed_overwrite(flags)) {
3718 		// Try to allocate a dest for vm_remap to fixed-overwrite at.
3719 		allocation_t dst_alloc SMART_TRY_ALLOCATE_VM(map, size, VM_PROT_DEFAULT);
3720 		mach_vm_address_t out_addr = dst_alloc.addr;
3721 		if (out_addr == 0) {
3722 			// Failed to allocate. Clear VM_FLAGS_OVERWRITE
3723 			// to prevent wild mappings.
3724 			flags &= ~VM_FLAGS_OVERWRITE;
3725 		}
3726 		kr = fn(map, &out_addr, size, 0, flags,
3727 		    map, src, copy, &cur, &max, inherit);
3728 	} else {
3729 		// vm_remap will allocate anywhere. Deallocate if it succeeds.
3730 		mach_vm_address_t out_addr = 0;
3731 		kr = fn(map, &out_addr, size, 0, flags,
3732 		    map, src, copy, &cur, &max, inherit);
3733 		if (kr == 0) {
3734 			(void)mach_vm_deallocate(map, out_addr, size);
3735 		}
3736 	}
3737 	return kr;
3738 }
3739 
3740 static kern_return_t
help_call_remap_fn__src_size(remap_fn_t fn,MAP_T map,int unused_flags __unused,bool copy,mach_vm_address_t src,mach_vm_size_t size)3741 help_call_remap_fn__src_size(remap_fn_t fn, MAP_T map, int unused_flags __unused, bool copy, mach_vm_address_t src, mach_vm_size_t size)
3742 {
3743 	assert(unused_flags == 0);
3744 	return help_call_remap_fn__src_size_etc(fn, map, VM_FLAGS_ANYWHERE, copy, src, size, 0, 0, VM_INHERIT_NONE);
3745 }
3746 
3747 static kern_return_t
help_call_remap_fn__dst_size(remap_fn_t fn,MAP_T map,int flags,bool copy,mach_vm_address_t dst,mach_vm_size_t size)3748 help_call_remap_fn__dst_size(remap_fn_t fn, MAP_T map, int flags, bool copy, mach_vm_address_t dst, mach_vm_size_t size)
3749 {
3750 	allocation_t src SMART_ALLOCATE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT);
3751 	mach_vm_address_t out_addr = dst;
3752 	vm_prot_t cur = 0;
3753 	vm_prot_t max = 0;
3754 	kern_return_t kr = fn(map, &out_addr, size, 0, flags,
3755 	    map, src.addr, copy, &cur, &max, VM_INHERIT_NONE);
3756 	deallocate_if_not_fixed_overwrite(kr, map, out_addr, size, flags);
3757 	return kr;
3758 }
3759 
3760 static kern_return_t
help_call_remap_fn__inherit(remap_fn_t fn,MAP_T map,int flags,bool copy,mach_vm_address_t src,mach_vm_size_t size,vm_inherit_t inherit)3761 help_call_remap_fn__inherit(remap_fn_t fn, MAP_T map, int flags, bool copy, mach_vm_address_t src, mach_vm_size_t size, vm_inherit_t inherit)
3762 {
3763 	return help_call_remap_fn__src_size_etc(fn, map, flags, copy, src, size, 0, 0, inherit);
3764 }
3765 
3766 static kern_return_t
help_call_remap_fn__flags(remap_fn_t fn,MAP_T map,int unused_flags __unused,bool copy,mach_vm_address_t src,mach_vm_size_t size,int trial_flags)3767 help_call_remap_fn__flags(remap_fn_t fn, MAP_T map, int unused_flags __unused, bool copy, mach_vm_address_t src, mach_vm_size_t size, int trial_flags)
3768 {
3769 	assert(unused_flags == 0);
3770 	return help_call_remap_fn__src_size_etc(fn, map, trial_flags, copy, src, size, 0, 0, VM_INHERIT_NONE);
3771 }
3772 
3773 static kern_return_t
help_call_remap_fn__prot_pairs(remap_fn_t fn,MAP_T map,int flags,bool copy,mach_vm_address_t src,mach_vm_size_t size,vm_prot_t cur,vm_prot_t max)3774 help_call_remap_fn__prot_pairs(remap_fn_t fn, MAP_T map, int flags, bool copy, mach_vm_address_t src, mach_vm_size_t size, vm_prot_t cur, vm_prot_t max)
3775 {
3776 	return help_call_remap_fn__src_size_etc(fn, map, flags, copy, src, size, cur, max, VM_INHERIT_NONE);
3777 }
3778 
3779 static kern_return_t
help_call_remap_fn__src_dst_size(remap_fn_t fn,MAP_T map,int flags,bool copy,mach_vm_address_t src,mach_vm_size_t size,mach_vm_address_t dst)3780 help_call_remap_fn__src_dst_size(remap_fn_t fn, MAP_T map, int flags, bool copy, mach_vm_address_t src, mach_vm_size_t size, mach_vm_address_t dst)
3781 {
3782 	mach_vm_address_t out_addr = dst;
3783 	vm_prot_t cur = 0;
3784 	vm_prot_t max = 0;
3785 	kern_return_t kr = fn(map, &out_addr, size, 0, flags,
3786 	    map, src, copy, &cur, &max, VM_INHERIT_NONE);
3787 	deallocate_if_not_fixed_overwrite(kr, map, out_addr, size, flags);
3788 	return kr;
3789 }
3790 
3791 #define GET_INSTANCE(_0, _1, _2, _3, _4, _5, _6, _7, _8, NAME, ...) NAME
3792 
3793 #define DROP_TYPES_8(a, b, ...) , b DROP_TYPES_6(__VA_ARGS__)
3794 #define DROP_TYPES_6(a, b, ...) , b DROP_TYPES_4(__VA_ARGS__)
3795 #define DROP_TYPES_4(a, b, ...) , b DROP_TYPES_2(__VA_ARGS__)
3796 #define DROP_TYPES_2(a, b, ...) , b
3797 #define DROP_TYPES_0()
3798 
3799 // Parses lists of "type1, arg1, type2, arg" into "arg1, arg2"
3800 #define DROP_TYPES(...) GET_INSTANCE(_0 __VA_OPT__(,) __VA_ARGS__, DROP_TYPES_8, DROP_TYPES_8, DROP_TYPES_6, DROP_TYPES_6, DROP_TYPES_4, DROP_TYPES_4, DROP_TYPES_2, DROP_TYPES_2, DROP_TYPES_0, DROP_TYPES_0)(__VA_ARGS__)
3801 
3802 #define DROP_COMMAS_8(a, b, ...) , a b DROP_COMMAS_6(__VA_ARGS__)
3803 #define DROP_COMMAS_6(a, b, ...) , a b DROP_COMMAS_4(__VA_ARGS__)
3804 #define DROP_COMMAS_4(a, b, ...) , a b DROP_COMMAS_2(__VA_ARGS__)
3805 #define DROP_COMMAS_2(a, b) , a b
3806 #define DROP_COMMAS_0()
3807 
3808 // Parses lists of "type1, arg1, type2, arg" into "type1 arg1, type2 arg2"
3809 #define DROP_COMMAS(...) GET_INSTANCE(_0 __VA_OPT__(,) __VA_ARGS__, DROP_COMMAS_8, DROP_COMMAS_8, DROP_COMMAS_6, DROP_COMMAS_6, DROP_COMMAS_4, DROP_COMMAS_4, DROP_COMMAS_2, DROP_COMMAS_2, DROP_COMMAS_0)(__VA_ARGS__)
3810 
3811 // specialize helpers into implementations of call functions that are still agnostic to the remap function
3812 
3813 #define IMPL_ONE_FROM_HELPER(type, variant, flags, copy, ...)                                                                                           \
3814 	static kern_return_t                                                                                                                            \
3815 	call_remap_fn ## __ ## variant ## __ ## type(remap_fn_t fn, MAP_T map, mach_vm_address_t src, mach_vm_size_t size DROP_COMMAS(__VA_ARGS__)) {   \
3816 	        return help_call_remap_fn__ ## type(fn, map, flags, copy, src, size DROP_TYPES(__VA_ARGS__));                                           \
3817 	}
3818 
3819 #define IMPL_FROM_HELPER(type, ...) \
3820 	IMPL_ONE_FROM_HELPER(type, fixed, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, false, ##__VA_ARGS__)         \
3821 	IMPL_ONE_FROM_HELPER(type, fixed_copy, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, true, ##__VA_ARGS__)     \
3822 	IMPL_ONE_FROM_HELPER(type, anywhere, VM_FLAGS_ANYWHERE, false, ##__VA_ARGS__)   \
3823 
3824 IMPL_FROM_HELPER(dst_size);
3825 IMPL_FROM_HELPER(inherit, vm_inherit_t, inherit);
3826 IMPL_FROM_HELPER(prot_pairs, vm_prot_t, cur, vm_prot_t, max);
3827 IMPL_FROM_HELPER(src_dst_size, mach_vm_address_t, dst);
3828 
3829 IMPL_ONE_FROM_HELPER(flags, nocopy, 0 /*ignored*/, false, int, flag)
3830 IMPL_ONE_FROM_HELPER(flags, copy, 0 /*ignored*/, true, int, flag)
3831 
3832 IMPL_ONE_FROM_HELPER(src_size, nocopy, 0 /*ignored*/, false)
3833 IMPL_ONE_FROM_HELPER(src_size, copy, 0 /*ignored*/, true)
3834 
3835 #undef IMPL_FROM_HELPER
3836 #undef IMPL_ONE_FROM_HELPER
3837 
3838 // define call functions that are specific to the remap function, and rely on implementations above under the hood
3839 
3840 #define IMPL_REMAP_FN_HELPER(remap_fn, instance, type, ...)                                             \
3841     static kern_return_t                                                                                \
3842     call_ ## remap_fn ## __ ## instance ## __ ## type(MAP_T map DROP_COMMAS(__VA_ARGS__))               \
3843     {                                                                                                   \
3844 	return call_remap_fn__ ## instance ## __ ## type(remap_fn, map DROP_TYPES(__VA_ARGS__));        \
3845     }
3846 
3847 #define IMPL_REMAP_FN_SRC_SIZE(remap_fn, instance) IMPL_REMAP_FN_HELPER(remap_fn, instance, src_size, mach_vm_address_t, src, mach_vm_size_t, size)
3848 #define IMPL_REMAP_FN_DST_SIZE(remap_fn, instance) IMPL_REMAP_FN_HELPER(remap_fn, instance, dst_size, mach_vm_address_t, src, mach_vm_size_t, size)
3849 #define IMPL_REMAP_FN_SRC_DST_SIZE(remap_fn, instance) IMPL_REMAP_FN_HELPER(remap_fn, instance, src_dst_size, mach_vm_address_t, src, mach_vm_size_t, size, mach_vm_address_t, dst)
3850 #define IMPL_REMAP_FN_SRC_SIZE_INHERIT(remap_fn, instance) IMPL_REMAP_FN_HELPER(remap_fn, instance, inherit, mach_vm_address_t, src, mach_vm_size_t, size, vm_inherit_t, inherit)
3851 #define IMPL_REMAP_FN_SRC_SIZE_FLAGS(remap_fn, instance) IMPL_REMAP_FN_HELPER(remap_fn, instance, flags, mach_vm_address_t, src, mach_vm_size_t, size, int, flags)
3852 #define IMPL_REMAP_FN_PROT_PAIRS(remap_fn, instance) IMPL_REMAP_FN_HELPER(remap_fn, instance, prot_pairs, mach_vm_address_t, src, mach_vm_size_t, size, vm_prot_t, cur, vm_prot_t, max)
3853 
3854 #define IMPL(remap_fn)                                          \
3855 	IMPL_REMAP_FN_SRC_SIZE(remap_fn, nocopy);               \
3856 	IMPL_REMAP_FN_SRC_SIZE(remap_fn, copy);                 \
3857                                                                 \
3858 	IMPL_REMAP_FN_DST_SIZE(remap_fn, fixed);                \
3859 	IMPL_REMAP_FN_DST_SIZE(remap_fn, fixed_copy);           \
3860 	IMPL_REMAP_FN_DST_SIZE(remap_fn, anywhere);             \
3861                                                                 \
3862 	IMPL_REMAP_FN_SRC_SIZE_INHERIT(remap_fn, fixed);        \
3863 	IMPL_REMAP_FN_SRC_SIZE_INHERIT(remap_fn, fixed_copy);   \
3864 	IMPL_REMAP_FN_SRC_SIZE_INHERIT(remap_fn, anywhere);     \
3865                                                                 \
3866 	IMPL_REMAP_FN_SRC_SIZE_FLAGS(remap_fn, nocopy);         \
3867 	IMPL_REMAP_FN_SRC_SIZE_FLAGS(remap_fn, copy);           \
3868                                                                 \
3869 	IMPL_REMAP_FN_PROT_PAIRS(remap_fn, fixed);              \
3870 	IMPL_REMAP_FN_PROT_PAIRS(remap_fn, fixed_copy);         \
3871 	IMPL_REMAP_FN_PROT_PAIRS(remap_fn, anywhere);           \
3872                                                                 \
3873 	IMPL_REMAP_FN_SRC_DST_SIZE(remap_fn, fixed);            \
3874 	IMPL_REMAP_FN_SRC_DST_SIZE(remap_fn, fixed_copy);       \
3875 	IMPL_REMAP_FN_SRC_DST_SIZE(remap_fn, anywhere);         \
3876 
3877 static inline void
check_mach_vm_map_outparam_changes(kern_return_t * kr,mach_vm_address_t addr,mach_vm_address_t saved_addr,int flags,MAP_T map)3878 check_mach_vm_map_outparam_changes(kern_return_t * kr, mach_vm_address_t addr, mach_vm_address_t saved_addr,
3879     int flags, MAP_T map)
3880 {
3881 	if (*kr == KERN_SUCCESS) {
3882 		if (is_fixed(flags)) {
3883 			if (addr != truncate_vm_map_addr_with_flags(map, saved_addr, flags)) {
3884 				*kr = OUT_PARAM_BAD;
3885 			}
3886 		}
3887 	} else {
3888 		if (addr != saved_addr) {
3889 			*kr = OUT_PARAM_BAD;
3890 		}
3891 	}
3892 }
3893 
3894 static inline void
check_mach_vm_remap_outparam_changes(kern_return_t * kr,mach_vm_address_t addr,mach_vm_address_t saved_addr,int flags,vm_prot_t cur_prot,vm_prot_t saved_cur_prot,vm_prot_t max_prot,vm_prot_t saved_max_prot,MAP_T map,mach_vm_address_t src_addr)3895 check_mach_vm_remap_outparam_changes(kern_return_t * kr, mach_vm_address_t addr, mach_vm_address_t saved_addr,
3896     int flags, vm_prot_t cur_prot, vm_prot_t saved_cur_prot, vm_prot_t max_prot, vm_prot_t saved_max_prot, MAP_T map,
3897     mach_vm_address_t src_addr)
3898 {
3899 	if (*kr == KERN_SUCCESS) {
3900 		if (is_fixed(flags)) {
3901 			mach_vm_address_t expected_misalignment = get_expected_remap_misalignment(map, src_addr, flags);
3902 			if (addr != trunc_down_map(map, saved_addr) + expected_misalignment) {
3903 				*kr = OUT_PARAM_BAD;
3904 			}
3905 		}
3906 	} else {
3907 		if ((addr != saved_addr) || (cur_prot != saved_cur_prot) ||
3908 		    (max_prot != saved_max_prot)) {
3909 			*kr = OUT_PARAM_BAD;
3910 		}
3911 	}
3912 }
3913 
3914 #if KERNEL
3915 
3916 static inline kern_return_t
mach_vm_remap_wrapped_kern(vm_map_t target_task,mach_vm_address_t * target_address,mach_vm_size_t size,mach_vm_offset_t mask,int flags,vm_map_t src_task,mach_vm_address_t src_address,boolean_t copy,vm_prot_t * cur_protection,vm_prot_t * max_protection,vm_inherit_t inheritance)3917 mach_vm_remap_wrapped_kern(vm_map_t target_task,
3918     mach_vm_address_t *target_address,
3919     mach_vm_size_t size,
3920     mach_vm_offset_t mask,
3921     int flags,
3922     vm_map_t src_task,
3923     mach_vm_address_t src_address,
3924     boolean_t copy,
3925     vm_prot_t *cur_protection,
3926     vm_prot_t *max_protection,
3927     vm_inherit_t inheritance)
3928 {
3929 	if (dealloc_would_time_out(*target_address, size, target_task)) {
3930 		return ACCEPTABLE;
3931 	}
3932 
3933 	mach_vm_address_t saved_addr = *target_address;
3934 	vm_prot_t saved_cur_prot = *cur_protection;
3935 	vm_prot_t saved_max_prot = *max_protection;
3936 	kern_return_t kr = mach_vm_remap(target_task, target_address, size, mask, flags, src_task, src_address, copy, cur_protection, max_protection, inheritance);
3937 	check_mach_vm_remap_outparam_changes(&kr, *target_address, saved_addr, flags,
3938 	    *cur_protection, saved_cur_prot, *max_protection, saved_max_prot, target_task, src_address);
3939 	return kr;
3940 }
IMPL(mach_vm_remap_wrapped_kern)3941 IMPL(mach_vm_remap_wrapped_kern)
3942 
3943 static inline kern_return_t
3944 mach_vm_remap_new_kernel_wrapped(vm_map_t target_task,
3945     mach_vm_address_t *target_address,
3946     mach_vm_size_t size,
3947     mach_vm_offset_t mask,
3948     int flags,
3949     vm_map_t src_task,
3950     mach_vm_address_t src_address,
3951     boolean_t copy,
3952     vm_prot_t *cur_protection,
3953     vm_prot_t *max_protection,
3954     vm_inherit_t inheritance)
3955 {
3956 	if (dealloc_would_time_out(*target_address, size, target_task)) {
3957 		return ACCEPTABLE;
3958 	}
3959 
3960 	mach_vm_address_t saved_addr = *target_address;
3961 	vm_prot_t saved_cur_prot = *cur_protection;
3962 	vm_prot_t saved_max_prot = *max_protection;
3963 	kern_return_t kr = mach_vm_remap_new_kernel(target_task, target_address, size, mask, FLAGS_AND_TAG(flags, VM_KERN_MEMORY_OSFMK), src_task, src_address, copy, cur_protection, max_protection, inheritance);
3964 	// remap_new sets VM_FLAGS_RETURN_DATA_ADDR
3965 	check_mach_vm_remap_outparam_changes(&kr, *target_address, saved_addr, flags | VM_FLAGS_RETURN_DATA_ADDR,
3966 	    *cur_protection, saved_cur_prot, *max_protection, saved_max_prot, target_task, src_address);
3967 	return kr;
3968 }
3969 IMPL(mach_vm_remap_new_kernel_wrapped)
3970 
3971 #else /* !KERNEL */
3972 
3973 static inline kern_return_t
3974 mach_vm_remap_user(vm_map_t target_task,
3975     mach_vm_address_t *target_address,
3976     mach_vm_size_t size,
3977     mach_vm_offset_t mask,
3978     int flags,
3979     vm_map_t src_task,
3980     mach_vm_address_t src_address,
3981     boolean_t copy,
3982     vm_prot_t *cur_protection,
3983     vm_prot_t *max_protection,
3984     vm_inherit_t inheritance)
3985 {
3986 	mach_vm_address_t saved_addr = *target_address;
3987 	vm_prot_t saved_cur_prot = *cur_protection;
3988 	vm_prot_t saved_max_prot = *max_protection;
3989 	kern_return_t kr = mach_vm_remap(target_task, target_address, size, mask, flags, src_task, src_address, copy, cur_protection, max_protection, inheritance);
3990 	check_mach_vm_remap_outparam_changes(&kr, *target_address, saved_addr, flags,
3991 	    *cur_protection, saved_cur_prot, *max_protection, saved_max_prot, target_task, src_address);
3992 	return kr;
3993 }
3994 IMPL(mach_vm_remap_user)
3995 
3996 static inline kern_return_t
3997 mach_vm_remap_new_user(vm_map_t target_task,
3998     mach_vm_address_t *target_address,
3999     mach_vm_size_t size,
4000     mach_vm_offset_t mask,
4001     int flags,
4002     vm_map_t src_task,
4003     mach_vm_address_t src_address,
4004     boolean_t copy,
4005     vm_prot_t *cur_protection,
4006     vm_prot_t *max_protection,
4007     vm_inherit_t inheritance)
4008 {
4009 	mach_vm_address_t saved_addr = *target_address;
4010 	vm_prot_t saved_cur_prot = *cur_protection;
4011 	vm_prot_t saved_max_prot = *max_protection;
4012 	kern_return_t kr = mach_vm_remap_new(target_task, target_address, size, mask, flags, src_task, src_address, copy, cur_protection, max_protection, inheritance);
4013 	// remap_new sets VM_FLAGS_RETURN_DATA_ADDR
4014 	check_mach_vm_remap_outparam_changes(&kr, *target_address, saved_addr, flags | VM_FLAGS_RETURN_DATA_ADDR,
4015 	    *cur_protection, saved_cur_prot, *max_protection, saved_max_prot, target_task, src_address);
4016 	return kr;
4017 }
4018 IMPL(mach_vm_remap_new_user)
4019 
4020 #if TEST_OLD_STYLE_MACH
4021 static inline kern_return_t
4022 vm_remap_retyped(vm_map_t target_task,
4023     mach_vm_address_t *target_address,
4024     mach_vm_size_t size,
4025     mach_vm_offset_t mask,
4026     int flags,
4027     vm_map_t src_task,
4028     mach_vm_address_t src_address,
4029     boolean_t copy,
4030     vm_prot_t *cur_protection,
4031     vm_prot_t *max_protection,
4032     vm_inherit_t inheritance)
4033 {
4034 	vm_address_t addr = (vm_address_t)*target_address;
4035 	vm_prot_t saved_cur_prot = *cur_protection;
4036 	vm_prot_t saved_max_prot = *max_protection;
4037 	kern_return_t kr = vm_remap(target_task, &addr, (vm_size_t)size, (vm_address_t)mask, flags, src_task, (vm_address_t)src_address, copy, cur_protection, max_protection, inheritance);
4038 	check_mach_vm_remap_outparam_changes(&kr, addr, (vm_address_t) *target_address, flags,
4039 	    *cur_protection, saved_cur_prot, *max_protection, saved_max_prot, target_task, src_address);
4040 	*target_address = addr;
4041 	return kr;
4042 }
4043 
4044 IMPL(vm_remap_retyped)
4045 
4046 #endif /* TEST_OLD_STYLE_MACH */
4047 #endif /* !KERNEL */
4048 
4049 #undef IMPL
4050 #undef IMPL_REMAP_FN_SRC_SIZE
4051 #undef IMPL_REMAP_FN_DST_SIZE
4052 #undef IMPL_REMAP_FN_SRC_DST_SIZE
4053 #undef IMPL_REMAP_FN_SRC_SIZE_INHERIT
4054 #undef IMPL_REMAP_FN_SRC_SIZE_FLAGS
4055 #undef IMPL_REMAP_FN_PROT_PAIRS
4056 #undef IMPL_REMAP_FN_HELPER
4057 
4058 
4059 /////////////////////////////////////////////////////
4060 // Test runners for functions with commonly-used parameter types and setup code.
4061 
4062 #define IMPL(NAME, T)                                                   \
4063 	/* Test a Mach function */                                      \
4064 	/* Run each trial with an allocated vm region and start/size parameters that reference it. */ \
4065 	typedef kern_return_t (*NAME ## mach_with_start_size_fn)(MAP_T map, T start, T size); \
4066                                                                         \
4067 	/* ...and the allocation has a specified minimum alignment */   \
4068 	static results_t * __attribute__((used))                        \
4069 	test_ ## NAME ## mach_with_allocated_aligned_start_size(NAME ## mach_with_start_size_fn fn, T align_mask, const char *testname) \
4070 	{                                                               \
4071 	        MAP_T map SMART_MAP;                                    \
4072 	        allocation_t base SMART_ALLOCATE_ALIGNED_VM(map, TEST_ALLOC_SIZE, align_mask, VM_PROT_DEFAULT); \
4073 	        start_size_trials_t *trials SMART_START_SIZE_TRIALS(base.addr); \
4074 	        results_t *results = alloc_results(testname, eSMART_START_SIZE_TRIALS, base.addr, trials->count); \
4075                                                                         \
4076 	        for (unsigned i = 0; i < trials->count; i++) {          \
4077 	                T start = (T)trials->list[i].start;             \
4078 	                T size = (T)trials->list[i].size;               \
4079 	                kern_return_t ret = fn(map, start, size);       \
4080 	                append_result(results, ret, trials->list[i].name); \
4081 	        }                                                       \
4082 	        return results;                                         \
4083 	}                                                               \
4084                                                                         \
4085 	/* ...and the allocation gets default alignment */              \
4086 	static results_t * __attribute__((used))                        \
4087 	test_ ## NAME ## mach_with_allocated_start_size(NAME ## mach_with_start_size_fn fn, const char *testname) \
4088 	{                                                               \
4089 	        return test_ ## NAME ## mach_with_allocated_aligned_start_size(fn, 0, testname); \
4090 	}                                                               \
4091                                                                         \
4092 	/* Test a Mach function. */                                     \
4093 	/* Run each trial with an allocated vm region and an addr parameter that reference it. */ \
4094 	typedef kern_return_t (*NAME ## mach_with_addr_fn)(MAP_T map, T addr); \
4095                                                                         \
4096 	static results_t * __attribute__((used))                        \
4097 	test_ ## NAME ## mach_with_allocated_addr_of_size_n(NAME ## mach_with_addr_fn fn, size_t obj_size, const char *testname) \
4098 	{                                                               \
4099 	        MAP_T map SMART_MAP;                                    \
4100 	        allocation_t base SMART_ALLOCATE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT); \
4101 	        addr_trials_t *trials SMART_ADDR_TRIALS(base.addr);     \
4102 	/* Do all the addr trials and an additional trial such that obj_size + addr == 0 */ \
4103 	        uint64_t trial_args[TRIALSARGUMENTS_SIZE] = {base.addr, obj_size}; \
4104 	        results_t *results = alloc_results(testname, eSMART_ADDR_TRIALS, trial_args, TRIALSARGUMENTS_SIZE, trials->count+1); \
4105                                                                         \
4106 	        for (unsigned i = 0; i < trials->count; i++) {          \
4107 	                T addr = (T)trials->list[i].addr;               \
4108 	                kern_return_t ret = fn(map, addr);              \
4109 	                append_result(results, ret, trials->list[i].name); \
4110 	        }                                                       \
4111 	        kern_return_t ret = fn(map,  - ((T) obj_size));         \
4112 	        char *trial_desc;                                       \
4113 	        kasprintf(&trial_desc, "addr: -0x%lx", obj_size);       \
4114 	        append_result(results, ret, trial_desc);                \
4115 	        kfree_str(trial_desc);                                  \
4116 	        return results;                                         \
4117 	}                                                               \
4118                                                                         \
4119 	/* Test a Mach function. */                                     \
4120 	/* Run each trial with an allocated vm region and an addr parameter that reference it. */ \
4121 	typedef kern_return_t (*NAME ## mach_with_addr_fn)(MAP_T map, T addr); \
4122                                                                         \
4123 	static results_t * __attribute__((used))                        \
4124 	test_ ## NAME ## mach_with_allocated_addr(NAME ## mach_with_addr_fn fn, const char *testname) \
4125 	{                                                               \
4126 	        MAP_T map SMART_MAP;                                    \
4127 	        allocation_t base SMART_ALLOCATE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT); \
4128 	        addr_trials_t *trials SMART_ADDR_TRIALS(base.addr);     \
4129 	        results_t *results = alloc_results(testname, eSMART_ADDR_TRIALS, base.addr, trials->count); \
4130                                                                         \
4131 	        for (unsigned i = 0; i < trials->count; i++) {          \
4132 	                T addr = (T)trials->list[i].addr;               \
4133 	                kern_return_t ret = fn(map, addr);              \
4134 	                append_result(results, ret, trials->list[i].name); \
4135 	        }                                                       \
4136 	        return results;                                         \
4137 	}                                                               \
4138                                                                         \
4139 	static results_t * __attribute__((used))                        \
4140 	test_ ## NAME ## mach_with_allocated_purgeable_addr(NAME ## mach_with_addr_fn fn, const char *testname) \
4141 	{                                                               \
4142 	        MAP_T map SMART_MAP;                                    \
4143 	        allocation_t base SMART_ALLOCATE_PURGEABLE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT); \
4144 	        addr_trials_t *trials SMART_ADDR_TRIALS(base.addr);     \
4145 	        results_t *results = alloc_results(testname, eSMART_ADDR_TRIALS, base.addr, trials->count); \
4146                                                                         \
4147 	        for (unsigned i = 0; i < trials->count; i++) {          \
4148 	                T addr = (T)trials->list[i].addr;               \
4149 	                kern_return_t ret = fn(map, addr);              \
4150 	                append_result(results, ret, trials->list[i].name); \
4151 	        }                                                       \
4152 	        return results;                                         \
4153 	}                                                               \
4154                                                                         \
4155 	/* Test a Mach function. */                                     \
4156 	/* Run each trial with a size parameter. */                     \
4157 	typedef kern_return_t (*NAME ## mach_with_size_fn)(MAP_T map, T size); \
4158                                                                         \
4159 	static results_t * __attribute__((used))                        \
4160 	test_ ## NAME ## mach_with_size(NAME ## mach_with_size_fn fn, const char *testname) \
4161 	{                                                               \
4162 	        MAP_T map SMART_MAP;                                    \
4163 	        size_trials_t *trials SMART_SIZE_TRIALS();              \
4164 	        results_t *results = alloc_results(testname, eSMART_SIZE_TRIALS, trials->count); \
4165                                                                         \
4166 	        for (unsigned i = 0; i < trials->count; i++) {          \
4167 	                T size = (T)trials->list[i].size;               \
4168 	                kern_return_t ret = fn(map, size);              \
4169 	                append_result(results, ret, trials->list[i].name); \
4170 	        }                                                       \
4171 	        return results;                                         \
4172 	}                                                               \
4173                                                                         \
4174 	/* Test a Mach function. */                                     \
4175 	/* Run each trial with a size parameter. */                     \
4176 	typedef kern_return_t (*NAME ## mach_with_start_size_offset_object_fn)(MAP_T map, T addr, T size, T offset, T obj_size); \
4177                                                                         \
4178 	static results_t * __attribute__((used))                        \
4179 	test_ ## NAME ## mach_with_allocated_start_size_offset_object(NAME ## mach_with_start_size_offset_object_fn fn, const char *testname) \
4180 	{                                                               \
4181 	        MAP_T map SMART_MAP;                                    \
4182 	        allocation_t base SMART_ALLOCATE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT); \
4183 	        start_size_offset_object_trials_t *trials SMART_START_SIZE_OFFSET_OBJECT_TRIALS(); \
4184 	        results_t *results = alloc_results(testname, eSMART_START_SIZE_OFFSET_OBJECT_TRIALS, trials->count); \
4185                                                                         \
4186 	        for (unsigned i = 0; i < trials->count; i++) {          \
4187 	                start_size_offset_object_trial_t trial = slide_trial(trials->list[i], base.addr); \
4188 	                T start = (T)trial.start;                       \
4189 	                T size = (T)trial.size;                         \
4190 	                T offset = (T)trial.offset;                     \
4191 	                T obj_size = (T)trial.obj_size;                 \
4192 	                kern_return_t ret = fn(map, start, size, offset, obj_size); \
4193 	                append_result(results, ret, trials->list[i].name); \
4194 	        }                                                       \
4195 	        return results;                                         \
4196 	}                                                               \
4197 	/* Test a Mach function. */                                     \
4198 	/* Run each trial with a size parameter. */                     \
4199 	typedef kern_return_t (*NAME ## mach_with_start_size_offset_fn)(MAP_T map, T addr, T size, T offset, T obj_size); \
4200                                                                         \
4201 	static results_t * __attribute__((used))                        \
4202 	test_ ## NAME ## mach_with_allocated_start_size_offset(NAME ## mach_with_start_size_offset_fn fn, const char *testname) \
4203 	{                                                               \
4204 	        MAP_T map SMART_MAP;                                    \
4205 	        allocation_t base SMART_ALLOCATE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT); \
4206 	        start_size_offset_trials_t *trials SMART_START_SIZE_OFFSET_TRIALS(); \
4207 	        results_t *results = alloc_results(testname, eSMART_START_SIZE_OFFSET_TRIALS, trials->count); \
4208                                                                         \
4209 	        for (unsigned i = 0; i < trials->count; i++) {          \
4210 	                start_size_offset_trial_t trial = slide_trial(trials->list[i], base.addr); \
4211 	                T start = (T)trial.start;                       \
4212 	                T size = (T)trial.size;                         \
4213 	                T offset = (T)trial.offset;                     \
4214 	                kern_return_t ret = fn(map, start, size, offset, 1); \
4215 	                append_result(results, ret, trials->list[i].name); \
4216 	        }                                                       \
4217 	        return results;                                         \
4218 	}                                                               \
4219                                                                         \
4220 	/* Test a Mach function. */                                     \
4221 	/* Run each trial with an allocated vm region and a set of mmap flags. */ \
4222 	typedef kern_return_t (*NAME ## mach_with_allocated_mmap_flags_fn)(MAP_T map, T addr, T size, int flags); \
4223                                                                         \
4224 	static results_t * __attribute__((used))                        \
4225 	test_ ## NAME ## mach_with_allocated_mmap_flags(NAME ## mach_with_allocated_mmap_flags_fn fn, const char *testname) \
4226 	{                                                               \
4227 	        MAP_T map SMART_MAP;                                    \
4228 	        allocation_t base SMART_ALLOCATE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT); \
4229 	        mmap_flags_trials_t *trials SMART_MMAP_FLAGS_TRIALS();  \
4230 	        results_t *results = alloc_results(testname, eSMART_MMAP_FLAGS_TRIALS, trials->count); \
4231                                                                         \
4232 	        for (unsigned i = 0; i < trials->count; i++) {          \
4233 	                int flags = trials->list[i].flags;              \
4234 	                kern_return_t ret = fn(map, (T)base.addr, (T)base.size, flags); \
4235 	                append_result(results, ret, trials->list[i].name); \
4236 	        }                                                       \
4237 	        return results;                                         \
4238 	}                                                               \
4239                                                                         \
4240 	/* Test a Mach function. */                                     \
4241 	/* Run each trial with an allocated vm region and a generic 32 bit flag. */ \
4242 	typedef kern_return_t (*NAME ## mach_with_allocated_generic_flag)(MAP_T map, T addr, T size, int flag); \
4243                                                                         \
4244 	static results_t * __attribute__((used))                        \
4245 	test_ ## NAME ## mach_with_allocated_generic_flag(NAME ## mach_with_allocated_generic_flag fn, const char *testname) \
4246 	{                                                               \
4247 	        MAP_T map SMART_MAP;                                    \
4248 	        allocation_t base SMART_ALLOCATE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT); \
4249 	        generic_flag_trials_t *trials SMART_GENERIC_FLAG_TRIALS();      \
4250 	        results_t *results = alloc_results(testname, eSMART_GENERIC_FLAG_TRIALS, trials->count); \
4251                                                                         \
4252 	        for (unsigned i = 0; i < trials->count; i++) {          \
4253 	                int flag = trials->list[i].flag;                \
4254 	                kern_return_t ret = fn(map, (T)base.addr, (T)base.size, flag); \
4255 	                append_result(results, ret, trials->list[i].name); \
4256 	        }                                                       \
4257 	        return results;                                         \
4258 	}                                                               \
4259                                                                         \
4260 	/* Test a Mach function. */                                     \
4261 	/* Run each trial with a vm_prot_t. */                          \
4262 	typedef kern_return_t (*NAME ## mach_with_prot_fn)(MAP_T map, T size, vm_prot_t prot); \
4263                                                                         \
4264 	static results_t * __attribute__((used))                        \
4265 	test_ ## NAME ## mach_vm_prot(NAME ## mach_with_prot_fn fn, const char *testname) \
4266 	{                                                               \
4267 	        MAP_T map SMART_MAP;                                    \
4268 	        vm_prot_trials_t *trials SMART_VM_PROT_TRIALS();        \
4269 	        results_t *results = alloc_results(testname, eSMART_VM_PROT_TRIALS, trials->count); \
4270                                                                         \
4271 	        for (unsigned i = 0; i < trials->count; i++) {          \
4272 	                kern_return_t ret = fn(map, TEST_ALLOC_SIZE, trials->list[i].prot); \
4273 	                append_result(results, ret, trials->list[i].name); \
4274 	        }                                                       \
4275 	        return results;                                         \
4276 	}                                                               \
4277                                                                         \
4278 	/* Test a Mach function. */                                     \
4279 	/* Run each trial with a pair of vm_prot_t's. */                \
4280 	typedef kern_return_t (*NAME ## mach_with_prot_pair_fn)(MAP_T map, vm_prot_t cur, vm_prot_t max); \
4281                                                                         \
4282 	static results_t * __attribute__((used))                        \
4283 	test_ ## NAME ## mach_vm_prot_pair(NAME ## mach_with_prot_pair_fn fn, const char *testname) \
4284 	{                                                               \
4285 	        MAP_T map SMART_MAP;                                    \
4286 	        vm_prot_pair_trials_t *trials SMART_VM_PROT_PAIR_TRIALS();      \
4287 	        results_t *results = alloc_results(testname, eSMART_VM_PROT_PAIR_TRIALS, trials->count); \
4288                                                                         \
4289 	        for (unsigned i = 0; i < trials->count; i++) {          \
4290 	                kern_return_t ret = fn(map, trials->list[i].cur, trials->list[i].max); \
4291 	                append_result(results, ret, trials->list[i].name); \
4292 	        }                                                       \
4293 	        return results;                                         \
4294 	}                                                               \
4295                                                                         \
4296 	/* Test a Mach function. */                                     \
4297 	/* Run each trial with a pair of vm_prot_t's. */ \
4298 	typedef kern_return_t (*NAME ## mach_with_allocated_prot_pair_fn)(MAP_T map, T addr, T size, vm_prot_t cur, vm_prot_t max); \
4299                                                                         \
4300 	static results_t * __attribute__((used))                        \
4301 	test_ ## NAME ## mach_with_allocated_vm_prot_pair(NAME ## mach_with_allocated_prot_pair_fn fn, const char *testname) \
4302 	{                                                               \
4303 	        MAP_T map SMART_MAP;                                    \
4304 	        allocation_t base SMART_ALLOCATE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT); \
4305 	        vm_prot_pair_trials_t *trials SMART_VM_PROT_PAIR_TRIALS(); \
4306 	        results_t *results = alloc_results(testname, eSMART_VM_PROT_PAIR_TRIALS, trials->count); \
4307                                                                         \
4308 	        for (unsigned i = 0; i < trials->count; i++) {          \
4309 	                kern_return_t ret = fn(map, (T)base.addr, (T)base.size, trials->list[i].cur, trials->list[i].max); \
4310 	                append_result(results, ret, trials->list[i].name); \
4311 	        }                                                       \
4312 	        return results;                                         \
4313 	}                                                               \
4314                                                                         \
4315 	/* Test a Mach function. */                                     \
4316 	/* Run each trial with an allocated vm region and a vm_prot_t. */ \
4317 	typedef kern_return_t (*NAME ## mach_with_allocated_prot_fn)(MAP_T map, T addr, T size, vm_prot_t prot); \
4318                                                                         \
4319 	static results_t * __attribute__((used))                        \
4320 	test_ ## NAME ## mach_with_allocated_vm_prot_t(NAME ## mach_with_allocated_prot_fn fn, const char *testname) \
4321 	{                                                               \
4322 	        MAP_T map SMART_MAP;                                    \
4323 	        allocation_t base SMART_ALLOCATE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT); \
4324 	        vm_prot_trials_t *trials SMART_VM_PROT_TRIALS();        \
4325 	        results_t *results = alloc_results(testname, eSMART_VM_PROT_TRIALS, trials->count); \
4326                                                                         \
4327 	        for (unsigned i = 0; i < trials->count; i++) {          \
4328 	                vm_prot_t prot = trials->list[i].prot;          \
4329 	                kern_return_t ret = fn(map, (T)base.addr, (T)base.size, prot); \
4330 	                append_result(results, ret, trials->list[i].name); \
4331 	        }                                                       \
4332 	        return results;                                         \
4333 	}                                                               \
4334                                                                         \
4335 	/* Test a Mach function. */                                     \
4336 	/* Run each trial with a ledger flag. */ \
4337 	typedef kern_return_t (*NAME ## mach_ledger_flag_fn)(MAP_T map, int ledger_flag); \
4338                                                                         \
4339 	static results_t * __attribute__((used))                        \
4340 	test_ ## NAME ## mach_with_ledger_flag(NAME ## mach_ledger_flag_fn fn, const char *testname) \
4341 	{                                                               \
4342 	        MAP_T map SMART_MAP;                                    \
4343 	        ledger_flag_trials_t *trials SMART_LEDGER_FLAG_TRIALS();        \
4344 	        results_t *results = alloc_results(testname, eSMART_LEDGER_FLAG_TRIALS, trials->count); \
4345                                                                         \
4346 	        for (unsigned i = 0; i < trials->count; i++) {          \
4347 	                kern_return_t ret = fn(map, trials->list[i].flag); \
4348 	                append_result(results, ret, trials->list[i].name); \
4349 	        }                                                       \
4350 	        return results;                                         \
4351 	}                                                               \
4352 	/* Test a Mach function. */                                     \
4353 	/* Run each trial with a ledger tag. */                         \
4354 	typedef kern_return_t (*NAME ## mach_ledger_tag_fn)(MAP_T map, int ledger_tag); \
4355                                                                         \
4356 	static results_t * __attribute__((used))                        \
4357 	test_ ## NAME ## mach_with_ledger_tag(NAME ## mach_ledger_tag_fn fn, const char *testname) \
4358 	{                                                               \
4359 	        MAP_T map SMART_MAP;                                    \
4360 	        ledger_tag_trials_t *trials SMART_LEDGER_TAG_TRIALS();  \
4361 	        results_t *results = alloc_results(testname, eSMART_LEDGER_TAG_TRIALS, trials->count); \
4362                                                                         \
4363 	        for (unsigned i = 0; i < trials->count; i++) {          \
4364 	                kern_return_t ret = fn(map, trials->list[i].tag); \
4365 	                append_result(results, ret, trials->list[i].name); \
4366 	        }                                                       \
4367 	        return results;                                         \
4368 	}                                                               \
4369                                                                         \
4370 	/* Test a Mach function. */                                     \
4371 	/* Run each trial with an allocated region and a vm_inherit_t. */ \
4372 	typedef kern_return_t (*NAME ## mach_inherit_fn)(MAP_T map, T addr, T size, vm_inherit_t inherit); \
4373                                                                         \
4374 	static results_t * __attribute__((used))                        \
4375 	test_ ## NAME ## mach_with_allocated_vm_inherit_t(NAME ## mach_inherit_fn fn, const char * testname) { \
4376 	        MAP_T map SMART_MAP;                                    \
4377 	        allocation_t base SMART_ALLOCATE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT); \
4378 	        vm_inherit_trials_t *trials SMART_VM_INHERIT_TRIALS();  \
4379 	        results_t *results = alloc_results(testname, eSMART_VM_INHERIT_TRIALS, trials->count); \
4380                                                                         \
4381 	        for (unsigned i = 0; i < trials->count; i++) {          \
4382 	                vm_inherit_trial_t trial = trials->list[i];     \
4383 	                int ret = fn(map, (T)base.addr, (T)base.size, trial.value); \
4384 	                append_result(results, ret, trial.name); \
4385 	        }                                                       \
4386 	        return results;                                         \
4387 	}                                                               \
4388 	/* Test a Mach function. */                                     \
4389 	/* Run each trial with an allocated vm region and a vm_prot_t. */ \
4390 	typedef kern_return_t (*NAME ## with_start_end_fn)(MAP_T map, T addr, T end); \
4391                                                                         \
4392 	static results_t * __attribute__((used))                        \
4393 	test_ ## NAME ## mach_with_allocated_start_end(NAME ## with_start_end_fn fn, const char *testname) \
4394 	{                                                               \
4395 	        MAP_T map SMART_MAP;                                    \
4396 	        allocation_t base SMART_ALLOCATE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT); \
4397 	        start_size_trials_t *trials SMART_START_SIZE_TRIALS(base.addr); \
4398 	        results_t *results = alloc_results(testname, eSMART_START_SIZE_TRIALS, base.addr, trials->count); \
4399                                                                         \
4400 	        for (unsigned i = 0; i < trials->count; i++) {          \
4401 	                T start = (T)trials->list[i].start;             \
4402 	                T size = (T)trials->list[i].size;               \
4403 	                kern_return_t ret = fn(map, start, start + size);       \
4404 	                append_result(results, ret, trials->list[i].name); \
4405 	        }                                                       \
4406 	        return results;                                         \
4407 	}                                                               \
4408 	/* Test a Mach function. */                                     \
4409 	/* Run each trial with an allocated vm region and a vm_prot_t. */ \
4410 	typedef kern_return_t (*NAME ## with_tag_fn)(MAP_T map, T addr, T end, vm_tag_t tag); \
4411                                                                         \
4412 	static results_t * __attribute__((used))                        \
4413 	test_ ## NAME ## mach_with_allocated_tag(NAME ## with_tag_fn fn, const char *testname) \
4414 	{                                                               \
4415 	        MAP_T map SMART_MAP;                                    \
4416 	        allocation_t base SMART_ALLOCATE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT); \
4417 	        vm_tag_trials_t *trials SMART_VM_TAG_TRIALS();  \
4418 	        results_t *results = alloc_results(testname, eSMART_VM_TAG_TRIALS, trials->count); \
4419                                                                         \
4420 	        for (unsigned i = 0; i < trials->count; i++) {          \
4421 	                kern_return_t ret = fn(map, (T)base.addr, (T)(base.addr + base.size), trials->list[i].tag); \
4422 	                append_result(results, ret, trials->list[i].name); \
4423 	        }                                                       \
4424 	        return results;                                         \
4425 	}                                                               \
4426 	/* Test a Mach function. */                                     \
4427 	/* Run each trial with an allocated region and a vm_behavior_t. */ \
4428 	typedef kern_return_t (*NAME ## mach_behavior_fn)(MAP_T map, T addr, T size, vm_behavior_t behavior); \
4429                                                                         \
4430 	static results_t * __attribute__((used))                        \
4431 	test_ ## NAME ## mach_with_allocated_aligned_vm_behavior_t(NAME ## mach_behavior_fn fn, mach_vm_size_t align_mask, const char * testname) { \
4432 	        MAP_T map SMART_MAP;                                    \
4433 	        allocation_t base SMART_ALLOCATE_ALIGNED_VM(map, TEST_ALLOC_SIZE, align_mask, VM_PROT_DEFAULT); \
4434 	        vm_behavior_trials_t *trials SMART_VM_BEHAVIOR_TRIALS();  \
4435 	        results_t *results = alloc_results(testname, eSMART_VM_BEHAVIOR_TRIALS, trials->count); \
4436                                                                         \
4437 	        for (unsigned i = 0; i < trials->count; i++) {          \
4438 	                vm_behavior_trial_t trial = trials->list[i];     \
4439 	                int ret = fn(map, (T)base.addr, (T)base.size, trial.value); \
4440 	                append_result(results, ret, trial.name); \
4441 	        }                                                       \
4442 	        return results;                                         \
4443 	}                                                               \
4444                                                                         \
4445 	static results_t * __attribute__((used))                        \
4446 	test_ ## NAME ## mach_with_allocated_vm_behavior_t(NAME ## mach_behavior_fn fn, const char * testname) { \
4447 	        return test_ ## NAME ## mach_with_allocated_aligned_vm_behavior_t(fn, 0, testname); \
4448 	}                                                               \
4449                                                                         \
4450 	/* Test a Mach function. */                                     \
4451 	/* Run each trial with an allocated region and a vm_sync_t. */ \
4452 	typedef kern_return_t (*NAME ## mach_sync_fn)(MAP_T map, T addr, T size, vm_sync_t behavior); \
4453                                                                         \
4454 	static results_t * __attribute__((used))                        \
4455 	test_ ## NAME ## mach_with_allocated_vm_sync_t(NAME ## mach_sync_fn fn, const char * testname) { \
4456 	        MAP_T map SMART_MAP;                                    \
4457 	        allocation_t base SMART_ALLOCATE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT); \
4458 	        vm_sync_trials_t *trials SMART_VM_SYNC_TRIALS(); \
4459 	        results_t *results = alloc_results(testname, eSMART_VM_SYNC_TRIALS, trials->count); \
4460                                                                         \
4461 	        for (unsigned i = 0; i < trials->count; i++) {          \
4462 	                vm_sync_trial_t trial = trials->list[i];    \
4463 	                int ret = fn(map, (T)base.addr, (T)base.size, trial.value); \
4464 	                append_result(results, ret, trial.name);        \
4465 	        }                                                       \
4466 	        return results;                                         \
4467 	}                                                               \
4468 	/* Test a Mach function. */                                     \
4469 	/* Run each trial with an allocated region and a vm_machine_attribute_t. */ \
4470 	typedef kern_return_t (*NAME ## mach_attribute_fn)(MAP_T map, T addr, T size, vm_machine_attribute_t attr); \
4471                                                                         \
4472 	static results_t * __attribute__((used))                        \
4473 	test_ ## NAME ## mach_with_allocated_vm_machine_attribute_t(NAME ## mach_attribute_fn fn, const char * testname) { \
4474 	        MAP_T map SMART_MAP;                                    \
4475 	        allocation_t base SMART_ALLOCATE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT); \
4476 	        vm_machine_attribute_trials_t *trials SMART_VM_MACHINE_ATTRIBUTE_TRIALS(); \
4477 	        results_t *results = alloc_results(testname, eSMART_VM_MACHINE_ATTRIBUTE_TRIALS, trials->count); \
4478                                                                         \
4479 	        for (unsigned i = 0; i < trials->count; i++) {          \
4480 	                vm_machine_attribute_trial_t trial = trials->list[i];    \
4481 	                int ret = fn(map, (T)base.addr, (T)base.size, trial.value); \
4482 	                append_result(results, ret, trial.name);        \
4483 	        }                                                       \
4484 	        return results;                                         \
4485 	}                                                               \
4486 	/* Test a Mach function. */                                     \
4487 	/* Run each trial with an allocated region and a purgeable trial. */ \
4488 	typedef kern_return_t (*NAME ## mach_purgable_fn)(MAP_T map, T addr, vm_purgable_t control, int state); \
4489                                                                         \
4490 	static results_t * __attribute__((used))                        \
4491 	test_ ## NAME ## mach_with_allocated_purgeable_and_state(NAME ## mach_purgable_fn fn, const char * testname) { \
4492 	        MAP_T map SMART_MAP;                                    \
4493 	        vm_purgeable_and_state_trials_t *trials SMART_VM_PURGEABLE_AND_STATE_TRIALS(); \
4494 	        results_t *results = alloc_results(testname, eSMART_VM_PURGEABLE_AND_STATE_TRIALS, trials->count); \
4495                                                                         \
4496 	        for (unsigned i = 0; i < trials->count; i++) {          \
4497 	                allocation_t base SMART_ALLOCATE_PURGEABLE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT); \
4498 	                vm_purgeable_and_state_trial_t trial = trials->list[i];    \
4499 	                int ret = fn(map, (T)base.addr, trial.control, trial.state); \
4500 	                append_result(results, ret, trial.name);        \
4501 	        }                                                       \
4502 	        return results;                                         \
4503 	}
4504 
4505 IMPL(, uint64_t)
4506 #if TEST_OLD_STYLE_MACH
IMPL(old,uint32_t)4507 IMPL(old, uint32_t)
4508 #endif
4509 #undef IMPL
4510 
4511 #if KERNEL && CONFIG_MAP_RANGES
4512 /*
4513  * The vm_range_create tests assume we don't ever do range_creates that should succeed
4514  * that take more than 2 * PAGE_SIZE. This enforces that.
4515  */
4516 void
4517 verify_largest_valid_trial_size_fits(start_size_start_size_trial_t trial)
4518 {
4519 	if (trial.size > 2 * PAGE_SIZE) {
4520 		assert(trial.size > 0xfffffffffffffff);
4521 	}
4522 	if (trial.second_size > 2 * PAGE_SIZE) {
4523 		assert(trial.second_size > 0xfffffffffffffff);
4524 	}
4525 }
4526 
4527 /* Run each trial with start/size/start/size parameters. */
4528 typedef kern_return_t (mach_with_start_size_start_size_fn)(MAP_T map, mach_vm_address_t addr,
4529     mach_vm_size_t size, mach_vm_address_t second_addr, mach_vm_size_t second_size);
4530 
4531 static results_t * __attribute__((used))
test_mach_vm_range_create(mach_with_start_size_start_size_fn fn,const char * testname)4532 test_mach_vm_range_create(mach_with_start_size_start_size_fn fn, const char *testname)
4533 {
4534 	start_size_start_size_trials_t *trials SMART_START_SIZE_START_SIZE_TRIALS();
4535 	results_t *results = alloc_results(testname, eSMART_START_SIZE_START_SIZE_TRIALS, trials->count);
4536 
4537 	for (unsigned i = 0; i < trials->count; i++) {
4538 		/*
4539 		 * Allocate and configure a new map for every trial so that the map has no user ranges.
4540 		 */
4541 		MAP_T map SMART_RANGE_MAP;
4542 		bool has_ranges = vm_map_range_configure(map, false) == KERN_SUCCESS;
4543 		bool has_space_in_ranges = false;
4544 
4545 		struct mach_vm_range void1 = {
4546 			.min_address = map->default_range.max_address,
4547 			.max_address = map->data_range.min_address,
4548 		};
4549 		struct mach_vm_range void2 = {
4550 			.min_address = map->data_range.max_address,
4551 			.max_address = vm_map_max(map),
4552 		};
4553 		struct mach_vm_range range_to_test;
4554 
4555 		/*
4556 		 * For our tests to succeed for good cases, but also trigger failures
4557 		 * when overlap occurs we need:
4558 		 * range1 = {.start = addr}, range2 = {.start = addr + PAGE_SIZE * 2}.
4559 		 * We also want at least 2 * PAGE_SIZE memory available after the start of range2.
4560 		 * We additionally start our first range 2 PAGE_SIZE away from the start.
4561 		 */
4562 		if (void1.min_address + (PAGE_SIZE * 6) < void1.max_address) {
4563 			range_to_test = void1;
4564 			has_space_in_ranges = true;
4565 		} else if (void2.min_address + (PAGE_SIZE * 6) < void2.max_address) {
4566 			range_to_test = void2;
4567 			has_space_in_ranges = true;
4568 		}
4569 
4570 		mach_vm_address_t addr_base = range_to_test.min_address + PAGE_SIZE * 2;
4571 		if (has_ranges && has_space_in_ranges) {
4572 			mach_vm_address_t second_addr_base = addr_base + PAGE_SIZE * 2;
4573 
4574 			start_size_start_size_trial_t trial = slide_trial(trials->list[i], addr_base, second_addr_base);
4575 
4576 			verify_largest_valid_trial_size_fits(trial);
4577 
4578 			mach_vm_address_t start = trial.start;
4579 			mach_vm_size_t size = trial.size;
4580 			mach_vm_address_t second_start = trial.second_start;
4581 			mach_vm_size_t second_size = trial.second_size;
4582 			kern_return_t ret = fn(map, start, size, second_start, second_size);
4583 			append_result(results, ret, trials->list[i].name);
4584 		} else {
4585 			append_result(results, IGNORED, trials->list[i].name);
4586 		}
4587 	}
4588 	return results;
4589 }
4590 #endif /* KERNEL && CONFIG_MAP_RANGES */
4591 
4592 // Test a mach allocation function with a start/size
4593 static results_t *
test_mach_allocation_func_with_start_size(kern_return_t (* func)(MAP_T map,mach_vm_address_t * start,mach_vm_size_t size),const char * testname)4594 test_mach_allocation_func_with_start_size(kern_return_t (*func)(MAP_T map, mach_vm_address_t * start, mach_vm_size_t size), const char * testname)
4595 {
4596 	MAP_T map SMART_MAP;
4597 	start_size_trials_t *trials SMART_START_SIZE_TRIALS(0);
4598 	results_t *results = alloc_results(testname, eSMART_START_SIZE_TRIALS, 0, trials->count);
4599 
4600 	for (unsigned i = 0; i < trials->count; i++) {
4601 		unallocation_t dst SMART_UNALLOCATE_VM(map, TEST_ALLOC_SIZE);
4602 		start_size_trial_t trial = slide_trial(trials->list[i], dst.addr);
4603 		mach_vm_address_t addr = trial.start;
4604 		kern_return_t ret = func(map, &addr, trial.size);
4605 		if (ret == 0) {
4606 			(void)mach_vm_deallocate(map, addr, trial.size);
4607 		}
4608 		append_result(results, ret, trial.name);
4609 	}
4610 	return results;
4611 }
4612 
4613 // Test a mach allocation function with a vm_map_kernel_flags_t
4614 static results_t *
test_mach_allocation_func_with_vm_map_kernel_flags_t(kern_return_t (* func)(MAP_T map,mach_vm_address_t * start,mach_vm_size_t size,int flags),const char * testname)4615 test_mach_allocation_func_with_vm_map_kernel_flags_t(kern_return_t (*func)(MAP_T map, mach_vm_address_t * start, mach_vm_size_t size, int flags), const char * testname)
4616 {
4617 	MAP_T map SMART_MAP;
4618 	vm_map_kernel_flags_trials_t * trials SMART_VM_MAP_KERNEL_FLAGS_TRIALS();
4619 	results_t *results = alloc_results(testname, eSMART_VM_MAP_KERNEL_FLAGS_TRIALS, trials->count);
4620 
4621 	for (unsigned i = 0; i < trials->count; i++) {
4622 		allocation_t fixed_overwrite_dst SMART_ALLOCATE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT);
4623 		vm_map_kernel_flags_trial_t trial = trials->list[i];
4624 #if KERNEL
4625 		if (is_random_anywhere(trial.flags)) {
4626 			// RANDOM_ADDR is likely to fall outside pmap's range
4627 			append_result(results, PANIC, trial.name);
4628 			continue;
4629 		}
4630 #endif
4631 		mach_vm_address_t addr = 0;
4632 		if (is_fixed_overwrite(trial.flags)) {
4633 			// use a pre-existing destination for fixed-overwrite
4634 			addr = fixed_overwrite_dst.addr;
4635 		}
4636 		kern_return_t ret = func(map, &addr, TEST_ALLOC_SIZE, trial.flags);
4637 		deallocate_if_not_fixed_overwrite(ret, map, addr, TEST_ALLOC_SIZE, trial.flags);
4638 		append_result(results, ret, trial.name);
4639 	}
4640 	return results;
4641 }
4642 
4643 static results_t *
test_mach_with_allocated_vm_map_kernel_flags_t(kern_return_t (* func)(MAP_T map,mach_vm_address_t src,mach_vm_size_t size,int flags),const char * testname)4644 test_mach_with_allocated_vm_map_kernel_flags_t(kern_return_t (*func)(MAP_T map, mach_vm_address_t src, mach_vm_size_t size, int flags), const char * testname)
4645 {
4646 	MAP_T map SMART_MAP;
4647 
4648 	allocation_t base SMART_ALLOCATE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT);
4649 	vm_map_kernel_flags_trials_t * trials SMART_VM_MAP_KERNEL_FLAGS_TRIALS();
4650 	results_t *results = alloc_results(testname, eSMART_VM_MAP_KERNEL_FLAGS_TRIALS, trials->count);
4651 
4652 	for (unsigned i = 0; i < trials->count; i++) {
4653 		kern_return_t ret = func(map, base.addr, base.size, trials->list[i].flags);
4654 		append_result(results, ret, trials->list[i].name);
4655 	}
4656 	return results;
4657 }
4658 
4659 static results_t *
test_unix_with_allocated_vm_prot_t(int (* func)(void * start,size_t size,int flags),const char * testname)4660 test_unix_with_allocated_vm_prot_t(int (*func)(void * start, size_t size, int flags), const char * testname)
4661 {
4662 	MAP_T map CURRENT_MAP;
4663 	allocation_t base SMART_ALLOCATE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT);
4664 	vm_prot_trials_t * trials SMART_VM_PROT_TRIALS();
4665 	results_t *results = alloc_results(testname, eSMART_VM_PROT_TRIALS, trials->count);
4666 
4667 	for (unsigned i = 0; i < trials->count; i++) {
4668 		int ret = func((void *) base.addr, (size_t) base.size, (int) trials->list[i].prot);
4669 		append_result(results, ret, trials->list[i].name);
4670 	}
4671 	return results;
4672 }
4673 
4674 // Test a Unix function.
4675 // Run each trial with an allocated vm region and start/size parameters that reference it.
4676 typedef int (*unix_with_start_size_fn)(void *start, size_t size);
4677 
4678 static results_t * __unused
test_unix_with_allocated_aligned_start_size(unix_with_start_size_fn fn,mach_vm_size_t align_mask,const char * testname)4679 test_unix_with_allocated_aligned_start_size(unix_with_start_size_fn fn, mach_vm_size_t align_mask, const char *testname)
4680 {
4681 	MAP_T map CURRENT_MAP;
4682 	allocation_t base SMART_ALLOCATE_ALIGNED_VM(map, TEST_ALLOC_SIZE, align_mask, VM_PROT_DEFAULT);
4683 	start_size_trials_t *trials SMART_START_SIZE_TRIALS(base.addr);
4684 	results_t *results = alloc_results(testname, eSMART_START_SIZE_TRIALS, base.addr, trials->count);
4685 
4686 	for (unsigned i = 0; i < trials->count; i++) {
4687 		addr_t start = trials->list[i].start;
4688 		addr_t size = trials->list[i].size;
4689 		int ret = fn((void*)(uintptr_t)start, (size_t)size);
4690 		append_result(results, ret, trials->list[i].name);
4691 	}
4692 	return results;
4693 }
4694 
4695 static results_t * __unused
test_unix_with_allocated_start_size(unix_with_start_size_fn fn,const char * testname)4696 test_unix_with_allocated_start_size(unix_with_start_size_fn fn, const char *testname)
4697 {
4698 	return test_unix_with_allocated_aligned_start_size(fn, 0, testname);
4699 }
4700 
4701 #if KERNEL
4702 static results_t * __unused
test_kext_unix_with_allocated_start_size(unix_with_start_size_fn fn,const char * testname)4703 test_kext_unix_with_allocated_start_size(unix_with_start_size_fn fn, const char *testname)
4704 {
4705 	MAP_T map CURRENT_MAP;
4706 	allocation_t base SMART_ALLOCATE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT);
4707 	start_size_trials_t *trials SMART_START_SIZE_TRIALS(base.addr);
4708 	results_t *results = alloc_results(testname, eSMART_START_SIZE_TRIALS, base.addr, trials->count);
4709 
4710 	for (unsigned i = 0; i < trials->count; i++) {
4711 		addr_t start = trials->list[i].start;
4712 		addr_t size = trials->list[i].size;
4713 		int ret = fn((void*)(uintptr_t)start, (size_t)size);
4714 		append_result(results, ret, trials->list[i].name);
4715 	}
4716 	return results;
4717 }
4718 
4719 /* Test a Kext function requiring memory allocated with a specific tag. */
4720 /* Run each trial with an allocated vm region and an addr parameter that reference it. */
4721 
4722 static results_t * __attribute__((used))
test_kext_tagged_with_allocated_addr(kern_return_t (* func)(MAP_T map,mach_vm_address_t addr),const char * testname)4723 test_kext_tagged_with_allocated_addr(kern_return_t (*func)(MAP_T map, mach_vm_address_t addr), const char *testname)
4724 {
4725 	MAP_T map CURRENT_MAP;
4726 	allocation_t base SMART_ALLOCATE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT);
4727 	addr_trials_t *trials SMART_ADDR_TRIALS(base.addr);
4728 	results_t *results = alloc_results(testname, eSMART_ADDR_TRIALS, base.addr, trials->count);
4729 
4730 	for (unsigned i = 0; i < trials->count; i++) {
4731 		mach_vm_address_t addr = (mach_vm_address_t)trials->list[i].addr;
4732 		kern_return_t ret = func(map, addr);
4733 		append_result(results, ret, trials->list[i].name);
4734 	}
4735 	return results;
4736 }
4737 #endif /* KERNEL */
4738 
4739 static results_t * __attribute__((used))
test_with_int64(kern_return_t (* func)(int64_t),const char * testname)4740 test_with_int64(kern_return_t (*func)(int64_t), const char *testname)
4741 {
4742 	size_trials_t *trials SMART_SIZE_TRIALS();
4743 	results_t *results = alloc_results(testname, eSMART_SIZE_TRIALS, trials->count);
4744 
4745 	for (unsigned i = 0; i < trials->count; i++) {
4746 		int64_t val = (int64_t)trials->list[i].size;
4747 		kern_return_t ret = func(val);
4748 		append_result(results, ret, trials->list[i].name);
4749 	}
4750 	return results;
4751 }
4752 
4753 
4754 #if !KERNEL
4755 
4756 // For deallocators like munmap and vm_deallocate.
4757 // Return a non-zero error code if we should avoid performing this trial.
4758 // Call this BEFORE sliding the trial to a non-zero base address.
4759 extern
4760 kern_return_t
4761 short_circuit_deallocator(MAP_T map, start_size_trial_t trial);
4762 
4763 // implemented in vm_parameter_validation.c
4764 
4765 #else /* KERNEL */
4766 
4767 static inline
4768 kern_return_t
short_circuit_deallocator(MAP_T map __unused,start_size_trial_t trial __unused)4769 short_circuit_deallocator(MAP_T map __unused, start_size_trial_t trial __unused)
4770 {
4771 	// Kernel tests run with an empty vm_map so we're free to deallocate whatever we want.
4772 	return 0;
4773 }
4774 
4775 #endif /* KERNEL */
4776 
4777 
4778 // Test mach_vm_deallocate or munmap.
4779 // Similar to test_mach_with_allocated_addr_size, but mach_vm_deallocate is destructive
4780 // so we can't test all values and we need to re-allocate the vm allocation each time.
4781 static results_t *
test_deallocator(kern_return_t (* func)(MAP_T map,mach_vm_address_t start,mach_vm_size_t size),const char * testname)4782 test_deallocator(kern_return_t (*func)(MAP_T map, mach_vm_address_t start, mach_vm_size_t size), const char *testname)
4783 {
4784 	MAP_T map SMART_MAP;
4785 
4786 	// allocate trials relative to address zero
4787 	// later we slide them to each allocation's address
4788 	start_size_trials_t *trials SMART_START_SIZE_TRIALS(0);
4789 
4790 	results_t *results = alloc_results(testname, eSMART_START_SIZE_TRIALS, 0, trials->count);
4791 
4792 	for (unsigned i = 0; i < trials->count; i++) {
4793 		start_size_trial_t trial = trials->list[i];
4794 		allocation_t base SMART_ALLOCATE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT);
4795 
4796 		// Avoid trials that might deallocate wildly.
4797 		// Check this BEFORE sliding the trial.
4798 		kern_return_t ret = short_circuit_deallocator(map, trial);
4799 		if (ret == 0) {
4800 			// Adjust start and/or size, if that value includes the allocated address
4801 			trial = slide_trial(trial, base.addr);
4802 
4803 			ret = func(map, trial.start, trial.size);
4804 			if (ret == 0) {
4805 				// Deallocation succeeded. Don't deallocate again.
4806 				set_already_deallocated(&base);
4807 			}
4808 		}
4809 		append_result(results, ret, trial.name);
4810 	}
4811 
4812 	return results;
4813 }
4814 
4815 static results_t *
test_allocated_src_unallocated_dst_size(kern_return_t (* func)(MAP_T map,mach_vm_address_t src,mach_vm_size_t size,mach_vm_address_t dst),const char * testname)4816 test_allocated_src_unallocated_dst_size(kern_return_t (*func)(MAP_T map, mach_vm_address_t src, mach_vm_size_t size, mach_vm_address_t dst), const char * testname)
4817 {
4818 	MAP_T map SMART_MAP;
4819 	allocation_t src_base SMART_ALLOCATE_VM(map, TEST_ALLOC_SIZE, VM_PROT_DEFAULT);
4820 	src_dst_size_trials_t * trials SMART_SRC_DST_SIZE_TRIALS();
4821 	results_t *results = alloc_results(testname, eSMART_SRC_DST_SIZE_TRIALS, trials->count);
4822 
4823 	for (unsigned i = 0; i < trials->count; i++) {
4824 		/*
4825 		 * Require src < dst. Some tests may get different error codes if src > dst.
4826 		 *
4827 		 * Example: size == -dst-1 for functions like vm_remap where dst
4828 		 * is a hint (i.e. dst + size overflow is ok) (rdar://132099195).
4829 		 * If src > dst then src + size overflows and the
4830 		 *   function returns KERN_INVALID_ARGUMENT.
4831 		 * If src < dst then src + size does not overflow and the
4832 		 *   function fails and returns KERN_INVALID_ADDRESS because
4833 		 *   [src, src + size) is an unreasonable address range.
4834 		 *
4835 		 * TODO: test both src < dst and src > dst.
4836 		 */
4837 		src_dst_size_trial_t trial = trials->list[i];
4838 		unallocation_t dst_base SMART_UNALLOCATE_VM_AFTER(map, src_base.addr, TEST_ALLOC_SIZE);
4839 		assert(src_base.addr < dst_base.addr);
4840 
4841 		trial = slide_trial_src(trial, src_base.addr);
4842 		trial = slide_trial_dst(trial, dst_base.addr);
4843 		int ret = func(map, trial.src, trial.size, trial.dst);
4844 		// func deallocates its own allocation
4845 		append_result(results, ret, trial.name);
4846 	}
4847 	return results;
4848 }
4849 
4850 
4851 static inline void
check_mach_vm_allocate_outparam_changes(kern_return_t * kr,mach_vm_address_t addr,mach_vm_size_t size,mach_vm_address_t saved_start,int flags,MAP_T map)4852 check_mach_vm_allocate_outparam_changes(kern_return_t * kr, mach_vm_address_t addr, mach_vm_size_t size,
4853     mach_vm_address_t saved_start, int flags, MAP_T map)
4854 {
4855 	if (*kr == KERN_SUCCESS) {
4856 		if (size == 0) {
4857 			if (addr != 0) {
4858 				*kr = OUT_PARAM_BAD;
4859 			}
4860 		} else {
4861 			if (is_fixed(flags)) {
4862 				if (addr != trunc_down_map(map, saved_start)) {
4863 					*kr = OUT_PARAM_BAD;
4864 				}
4865 			}
4866 		}
4867 	} else {
4868 		if (saved_start != addr) {
4869 			*kr = OUT_PARAM_BAD;
4870 		}
4871 	}
4872 }
4873 
4874 static kern_return_t
call_mach_vm_behavior_set__start_size__default(MAP_T map,mach_vm_address_t start,mach_vm_size_t size)4875 call_mach_vm_behavior_set__start_size__default(MAP_T map, mach_vm_address_t start, mach_vm_size_t size)
4876 {
4877 	kern_return_t kr = mach_vm_behavior_set(map, start, size, VM_BEHAVIOR_DEFAULT);
4878 	return kr;
4879 }
4880 
4881 /*
4882  * VM_BEHAVIOR_CAN_REUSE is additionally tested as it uses slightly different page rounding semantics
4883  */
4884 static kern_return_t
call_mach_vm_behavior_set__start_size__can_reuse(MAP_T map,mach_vm_address_t start,mach_vm_size_t size)4885 call_mach_vm_behavior_set__start_size__can_reuse(MAP_T map, mach_vm_address_t start, mach_vm_size_t size)
4886 {
4887 	kern_return_t kr = mach_vm_behavior_set(map, start, size, VM_BEHAVIOR_CAN_REUSE);
4888 	return kr;
4889 }
4890 
4891 static kern_return_t
call_mach_vm_behavior_set__vm_behavior(MAP_T map,mach_vm_address_t start,mach_vm_size_t size,vm_behavior_t behavior)4892 call_mach_vm_behavior_set__vm_behavior(MAP_T map, mach_vm_address_t start, mach_vm_size_t size, vm_behavior_t behavior)
4893 {
4894 	kern_return_t kr = mach_vm_behavior_set(map, start, size, behavior);
4895 	return kr;
4896 }
4897 
4898 static void
check_mach_vm_purgable_control_outparam_changes(kern_return_t * kr,int state,int saved_state,int control)4899 check_mach_vm_purgable_control_outparam_changes(kern_return_t * kr, int state, int saved_state, int control)
4900 {
4901 	if (*kr == KERN_SUCCESS) {
4902 		if (control == VM_PURGABLE_PURGE_ALL || VM_PURGABLE_SET_STATE) {
4903 			if (state != saved_state) {
4904 				*kr = OUT_PARAM_BAD;
4905 			}
4906 		}
4907 		if (control == VM_PURGABLE_GET_STATE) {
4908 			/*
4909 			 * The default state is VM_PURGABLE_NONVOLATILE for a newly created region
4910 			 */
4911 			if (state != VM_PURGABLE_NONVOLATILE) {
4912 				*kr = OUT_PARAM_BAD;
4913 			}
4914 		}
4915 	} else {
4916 		if (state != saved_state) {
4917 			*kr = OUT_PARAM_BAD;
4918 		}
4919 	}
4920 }
4921 
4922 static void
check_mach_vm_region_outparam_changes(kern_return_t * kr,MAP_T map,void * info,void * saved_info,size_t info_size,mach_port_t object_name,mach_port_t saved_object_name,mach_vm_address_t addr,mach_vm_address_t saved_addr,mach_vm_size_t size,mach_vm_size_t saved_size)4923 check_mach_vm_region_outparam_changes(kern_return_t * kr, MAP_T map, void * info, void * saved_info, size_t info_size,
4924     mach_port_t object_name, mach_port_t saved_object_name, mach_vm_address_t addr, mach_vm_address_t saved_addr,
4925     mach_vm_size_t size, mach_vm_size_t saved_size)
4926 {
4927 	if (*kr == KERN_SUCCESS) {
4928 		if (object_name != 0) {
4929 			*kr = OUT_PARAM_BAD;
4930 		}
4931 		if (addr < trunc_down_map(map, saved_addr)) {
4932 			*kr = OUT_PARAM_BAD;
4933 		}
4934 		if (size == saved_size) {
4935 			*kr = OUT_PARAM_BAD;
4936 		}
4937 		if (memcmp(info, saved_info, info_size) == 0) {
4938 			*kr = OUT_PARAM_BAD;
4939 		}
4940 	} else {
4941 		if (object_name != saved_object_name || addr != saved_addr || size != saved_size || memcmp(info, saved_info, info_size) != 0) {
4942 			*kr = OUT_PARAM_BAD;
4943 		}
4944 	}
4945 }
4946 
4947 static int
call_mach_vm_region(MAP_T map,mach_vm_address_t addr)4948 call_mach_vm_region(MAP_T map, mach_vm_address_t addr)
4949 {
4950 	mach_vm_address_t addr_cpy = addr;
4951 	mach_vm_size_t size_out = UNLIKELY_INITIAL_SIZE;
4952 	mach_vm_size_t saved_size = size_out;
4953 	mach_port_t object_name_out = UNLIKELY_INITIAL_MACH_PORT;
4954 	mach_port_t saved_name = object_name_out;
4955 	vm_region_basic_info_data_64_t info;
4956 	info.inheritance = INVALID_INHERIT;
4957 	vm_region_basic_info_data_64_t saved_info = info;
4958 
4959 	mach_msg_type_number_t infoCnt = VM_REGION_BASIC_INFO_COUNT_64;
4960 	kern_return_t kr = mach_vm_region(map, &addr_cpy, &size_out, VM_REGION_BASIC_INFO_64, (vm_region_info_t)&info,
4961 	    &infoCnt, &object_name_out);
4962 	check_mach_vm_region_outparam_changes(&kr, map, &info, &saved_info, sizeof(info), object_name_out, saved_name, addr_cpy, addr, size_out, saved_size);
4963 
4964 	return kr;
4965 }
4966 
4967 #if TEST_OLD_STYLE_MACH || KERNEL
4968 static int
call_vm_region(MAP_T map,vm_address_t addr)4969 call_vm_region(MAP_T map, vm_address_t addr)
4970 {
4971 	vm_address_t addr_cpy = addr;
4972 	vm_size_t size_out = UNLIKELY_INITIAL_SIZE;
4973 	vm_size_t saved_size = size_out;
4974 	mach_port_t object_name_out = UNLIKELY_INITIAL_MACH_PORT;
4975 	mach_port_t saved_name = object_name_out;
4976 	vm_region_basic_info_data_64_t info;
4977 	info.inheritance = INVALID_INHERIT;
4978 	vm_region_basic_info_data_64_t saved_info = info;
4979 
4980 	mach_msg_type_number_t infoCnt = VM_REGION_BASIC_INFO_COUNT_64;
4981 	kern_return_t kr = vm_region(map, &addr_cpy, &size_out, VM_REGION_BASIC_INFO_64, (vm_region_info_t)&info,
4982 	    &infoCnt, &object_name_out);
4983 	check_mach_vm_region_outparam_changes(&kr, map, &info, &saved_info, sizeof(info), object_name_out, saved_name, addr_cpy, addr, size_out, saved_size);
4984 
4985 	return kr;
4986 }
4987 #endif /* TEST_OLD_STYLE_MACH || KERNEL */
4988 
4989 static void
check_mach_vm_page_info_outparam_changes(kern_return_t * kr,vm_page_info_basic_data_t info,vm_page_info_basic_data_t saved_info,mach_msg_type_number_t count,mach_msg_type_number_t saved_count)4990 check_mach_vm_page_info_outparam_changes(kern_return_t * kr, vm_page_info_basic_data_t info, vm_page_info_basic_data_t saved_info,
4991     mach_msg_type_number_t count, mach_msg_type_number_t saved_count)
4992 {
4993 	if (*kr == KERN_SUCCESS) {
4994 		if (memcmp(&info, &saved_info, sizeof(vm_page_info_basic_data_t)) == 0) {
4995 			*kr = OUT_PARAM_BAD;
4996 		}
4997 	} else {
4998 		if (memcmp(&info, &saved_info, sizeof(vm_page_info_basic_data_t)) != 0) {
4999 			*kr = OUT_PARAM_BAD;
5000 		}
5001 	}
5002 	if (count != saved_count) {
5003 		*kr = OUT_PARAM_BAD;
5004 	}
5005 }
5006 
5007 #pragma clang diagnostic pop
5008 
5009 // VM_PARAMETER_VALIDATION_H
5010 #endif
5011