]>
Commit | Line | Data |
---|---|---|
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(T_META_RUN_CONCURRENTLY(true)); | |
24 | ||
25 | static mach_port_t | |
26 | get_user_data_port(mach_msg_type_number_t *size) | |
27 | { | |
28 | #define DATA "Hello World!" | |
29 | struct { | |
30 | mach_voucher_attr_recipe_data_t recipe; | |
31 | char data[sizeof(DATA)]; | |
32 | } buf = { | |
33 | .recipe = { | |
34 | .key = MACH_VOUCHER_ATTR_KEY_USER_DATA, | |
35 | .command = MACH_VOUCHER_ATTR_USER_DATA_STORE, | |
36 | .content_size = sizeof(DATA), | |
37 | }, | |
38 | .data = DATA, | |
39 | }; | |
40 | ||
41 | mach_port_t port = MACH_PORT_NULL; | |
42 | kern_return_t kr = host_create_mach_voucher(mach_host_self(), | |
43 | (mach_voucher_attr_raw_recipe_array_t)&buf, | |
44 | sizeof(buf), &port); | |
45 | T_ASSERT_MACH_SUCCESS(kr, "Create USER_DATA voucher: 0x%x", | |
46 | (unsigned int)port); | |
47 | ||
48 | if (size) { | |
49 | *size = sizeof(buf); | |
50 | } | |
51 | return port; | |
52 | } | |
53 | ||
54 | ||
55 | T_DECL(voucher_extract_attr_recipe, "voucher_extract_attr_recipe") | |
56 | { | |
57 | kern_return_t kr; | |
58 | mach_vm_size_t alloc_sz; | |
59 | mach_port_t port; | |
60 | mach_vm_address_t alloc_addr; | |
61 | mach_msg_type_number_t expected_size; | |
62 | ||
63 | /* map at least a page of memory at some arbitrary location */ | |
64 | alloc_sz = (mach_vm_size_t)round_page(MACH_VOUCHER_TRAP_STACK_LIMIT + 1); | |
65 | ||
66 | /* | |
67 | * We could theoretically ask for a fixed location, but this is more | |
68 | * reliable, and we're not actually trying to exploit anything - a | |
69 | * kernel panic on failure should suffice :-) | |
70 | */ | |
71 | alloc_addr = (mach_vm_address_t)round_page(MACH_VOUCHER_ATTR_MAX_RAW_RECIPE_ARRAY_SIZE + 1); | |
72 | kr = mach_vm_allocate(mach_task_self(), &alloc_addr, | |
73 | alloc_sz, VM_FLAGS_ANYWHERE); | |
74 | ||
75 | /* | |
76 | * Make sure that the address of the allocation is larger than the | |
77 | * maximum recipe size: this will test for the bug that was fixed in | |
78 | * <rdar://problem/29379175>. | |
79 | */ | |
80 | T_ASSERT_GT_ULLONG((uint64_t)alloc_addr, | |
81 | (uint64_t)MACH_VOUCHER_ATTR_MAX_RAW_RECIPE_ARRAY_SIZE, | |
82 | "Recipe addr (%llu bytes): 0x%llx > max recipe sz: %llu", | |
83 | (uint64_t)alloc_sz, (uint64_t)alloc_addr, | |
84 | (uint64_t)MACH_VOUCHER_ATTR_MAX_RAW_RECIPE_ARRAY_SIZE); | |
85 | ||
86 | /* make the allocation look like a pointer to an int */ | |
87 | mach_msg_type_number_t *recipe_size; | |
88 | recipe_size = (mach_msg_type_number_t *)((uintptr_t)alloc_addr); | |
89 | bzero(recipe_size, (unsigned long)alloc_sz); | |
90 | if (alloc_sz > MACH_VOUCHER_ATTR_MAX_RAW_RECIPE_ARRAY_SIZE) { | |
91 | *recipe_size = MACH_VOUCHER_ATTR_MAX_RAW_RECIPE_ARRAY_SIZE; | |
92 | } else { | |
93 | *recipe_size = (mach_msg_type_number_t)alloc_sz; | |
94 | } | |
95 | ||
96 | /* recipe buffer on the heap: memset it so panics show up loudly */ | |
97 | size_t size = (size_t)(10 * 1024 * 1024); | |
98 | void *recipe = malloc(size); | |
99 | memset(recipe, 0x41, size); | |
100 | ||
101 | port = get_user_data_port(&expected_size); | |
102 | ||
103 | /* | |
104 | * This should try to extract the USER_DATA attribute using a buffer on the | |
105 | * kernel heap (probably zone memory). | |
106 | */ | |
107 | kr = mach_voucher_extract_attr_recipe_trap(port, | |
108 | MACH_VOUCHER_ATTR_KEY_USER_DATA, recipe, recipe_size); | |
109 | T_ASSERT_MACH_SUCCESS(kr, "Extract attribute data with recipe: heap"); | |
110 | T_ASSERT_EQ(*recipe_size, expected_size, "size should match"); | |
111 | ||
112 | /* reset the recipe memory */ | |
113 | memset(recipe, 0x41, size); | |
114 | /* reduce the size to get an allocation on the kernel stack */ | |
115 | *recipe_size = MACH_VOUCHER_TRAP_STACK_LIMIT - 1; | |
116 | ||
117 | /* | |
118 | * This should try to extract the USER_DATA attribute using a buffer on the | |
119 | * kernel stack. | |
120 | */ | |
121 | kr = mach_voucher_extract_attr_recipe_trap(port, | |
122 | MACH_VOUCHER_ATTR_KEY_USER_DATA, recipe, recipe_size); | |
123 | T_ASSERT_MACH_SUCCESS(kr, "Extract attribute data with recipe: stack"); | |
124 | T_ASSERT_EQ(*recipe_size, expected_size, "size should match"); | |
125 | ||
126 | /* cleanup */ | |
127 | ||
128 | free(recipe); | |
129 | kr = mach_vm_deallocate(mach_task_self(), alloc_addr, alloc_sz); | |
130 | T_ASSERT_MACH_SUCCESS(kr, "Deallocate recipe buffers"); | |
131 | } |