1 /*
2 * Test voucher trap APIs.
3 * There was an unfortunate bug in the trap interface that used the user space
4 * _address_ of a trap parameter as a copyin size. This test validates there
5 * are no other kernel panics in the voucher create and voucher attribute
6 * extraction mach traps.
7 *
8 * clang -o voucher_traps voucher_traps.c -ldarwintest -Weverything -Wno-gnu-flexible-array-initializer
9 *
10 * <rdar://problem/29379175>
11 */
12
13 #include <stdint.h>
14 #include <stdlib.h>
15 #include <mach/mach.h>
16 #include <mach/mach_vm.h>
17 #include <mach/mach_traps.h>
18
19 #include <atm/atm_types.h>
20
21 #include <darwintest.h>
22
23 T_GLOBAL_META(
24 T_META_NAMESPACE("xnu.ipc"),
25 T_META_RUN_CONCURRENTLY(TRUE),
26 T_META_RADAR_COMPONENT_NAME("xnu"),
27 T_META_RADAR_COMPONENT_VERSION("IPC"));
28
29 static mach_port_t
get_user_data_port(mach_msg_type_number_t * size)30 get_user_data_port(mach_msg_type_number_t *size)
31 {
32 #define DATA "Hello World!"
33 struct {
34 mach_voucher_attr_recipe_data_t recipe;
35 char data[sizeof(DATA)];
36 } buf = {
37 .recipe = {
38 .key = MACH_VOUCHER_ATTR_KEY_USER_DATA,
39 .command = MACH_VOUCHER_ATTR_USER_DATA_STORE,
40 .content_size = sizeof(DATA),
41 },
42 .data = DATA,
43 };
44
45 mach_port_t port = MACH_PORT_NULL;
46 kern_return_t kr = host_create_mach_voucher(mach_host_self(),
47 (mach_voucher_attr_raw_recipe_array_t)&buf,
48 sizeof(buf), &port);
49 #if !TARGET_OS_OSX
50 T_ASSERT_NE(kr, KERN_SUCCESS, "User data manager removed on embedded");
51 T_END;
52 #endif
53 T_ASSERT_MACH_SUCCESS(kr, "Create USER_DATA voucher: 0x%x",
54 (unsigned int)port);
55
56 if (size) {
57 *size = sizeof(buf);
58 }
59 return port;
60 }
61
62
63 T_DECL(voucher_extract_attr_recipe, "voucher_extract_attr_recipe")
64 {
65 kern_return_t kr;
66 mach_vm_size_t alloc_sz;
67 mach_port_t port;
68 mach_vm_address_t alloc_addr;
69 mach_msg_type_number_t expected_size;
70
71 /* map at least a page of memory at some arbitrary location */
72 alloc_sz = (mach_vm_size_t)round_page(MACH_VOUCHER_TRAP_STACK_LIMIT + 1);
73
74 /*
75 * We could theoretically ask for a fixed location, but this is more
76 * reliable, and we're not actually trying to exploit anything - a
77 * kernel panic on failure should suffice :-)
78 */
79 alloc_addr = (mach_vm_address_t)round_page(MACH_VOUCHER_ATTR_MAX_RAW_RECIPE_ARRAY_SIZE + 1);
80 kr = mach_vm_allocate(mach_task_self(), &alloc_addr,
81 alloc_sz, VM_FLAGS_ANYWHERE);
82
83 /*
84 * Make sure that the address of the allocation is larger than the
85 * maximum recipe size: this will test for the bug that was fixed in
86 * <rdar://problem/29379175>.
87 */
88 T_ASSERT_GT_ULLONG((uint64_t)alloc_addr,
89 (uint64_t)MACH_VOUCHER_ATTR_MAX_RAW_RECIPE_ARRAY_SIZE,
90 "Recipe addr (%llu bytes): 0x%llx > max recipe sz: %llu",
91 (uint64_t)alloc_sz, (uint64_t)alloc_addr,
92 (uint64_t)MACH_VOUCHER_ATTR_MAX_RAW_RECIPE_ARRAY_SIZE);
93
94 /* make the allocation look like a pointer to an int */
95 mach_msg_type_number_t *recipe_size;
96 recipe_size = (mach_msg_type_number_t *)((uintptr_t)alloc_addr);
97 bzero(recipe_size, (unsigned long)alloc_sz);
98 if (alloc_sz > MACH_VOUCHER_ATTR_MAX_RAW_RECIPE_ARRAY_SIZE) {
99 *recipe_size = MACH_VOUCHER_ATTR_MAX_RAW_RECIPE_ARRAY_SIZE;
100 } else {
101 *recipe_size = (mach_msg_type_number_t)alloc_sz;
102 }
103
104 /* recipe buffer on the heap: memset it so panics show up loudly */
105 size_t size = (size_t)(10 * 1024 * 1024);
106 void *recipe = malloc(size);
107 memset(recipe, 0x41, size);
108
109 port = get_user_data_port(&expected_size);
110
111 /*
112 * This should try to extract the USER_DATA attribute using a buffer on the
113 * kernel heap (probably zone memory).
114 */
115 kr = mach_voucher_extract_attr_recipe_trap(port,
116 MACH_VOUCHER_ATTR_KEY_USER_DATA, recipe, recipe_size);
117 T_ASSERT_MACH_SUCCESS(kr, "Extract attribute data with recipe: heap");
118 T_ASSERT_EQ(*recipe_size, expected_size, "size should match");
119
120 /* reset the recipe memory */
121 memset(recipe, 0x41, size);
122 /* reduce the size to get an allocation on the kernel stack */
123 *recipe_size = MACH_VOUCHER_TRAP_STACK_LIMIT - 1;
124
125 /*
126 * This should try to extract the USER_DATA attribute using a buffer on the
127 * kernel stack.
128 */
129 kr = mach_voucher_extract_attr_recipe_trap(port,
130 MACH_VOUCHER_ATTR_KEY_USER_DATA, recipe, recipe_size);
131 T_ASSERT_MACH_SUCCESS(kr, "Extract attribute data with recipe: stack");
132 T_ASSERT_EQ(*recipe_size, expected_size, "size should match");
133
134 /* cleanup */
135
136 free(recipe);
137 kr = mach_vm_deallocate(mach_task_self(), alloc_addr, alloc_sz);
138 T_ASSERT_MACH_SUCCESS(kr, "Deallocate recipe buffers");
139 }
140