]>
Commit | Line | Data |
---|---|---|
d9a64523 A |
1 | #ifdef T_NAMESPACE |
2 | #undef T_NAMESPACE | |
3 | #endif | |
4 | ||
5 | ||
6 | #include <darwintest.h> | |
7 | #include <dispatch/dispatch.h> | |
8 | #include <stdlib.h> | |
9 | #include <spawn.h> | |
10 | #include <spawn_private.h> | |
11 | ||
12 | #include <mach-o/dyld.h> | |
13 | #include <mach/mach.h> | |
14 | #include <mach/task.h> | |
15 | ||
16 | #include <signal.h> | |
17 | #include <sys/sysctl.h> | |
18 | #include <sys/syslimits.h> | |
19 | ||
20 | #include <excserver.h> | |
21 | ||
22 | static dispatch_semaphore_t sync_sema; | |
23 | ||
24 | kern_return_t | |
25 | catch_mach_exception_raise(mach_port_t exception_port, | |
0a7de745 A |
26 | mach_port_t thread, |
27 | mach_port_t task, | |
28 | exception_type_t exception, | |
29 | mach_exception_data_t code, | |
30 | mach_msg_type_number_t code_count) | |
d9a64523 A |
31 | { |
32 | #pragma unused(exception_port, thread, task, code, code_count) | |
33 | pid_t pid; | |
34 | pid_for_task(task, &pid); | |
35 | T_ASSERT_EQ(exception, EXC_CORPSE_NOTIFY, "exception type"); | |
36 | T_ASSERT_POSIX_ZERO(kill(pid, SIGKILL), "kill"); | |
37 | dispatch_semaphore_signal(sync_sema); | |
38 | return KERN_SUCCESS; | |
39 | } | |
40 | ||
41 | kern_return_t | |
42 | catch_mach_exception_raise_state(mach_port_t exception_port, | |
0a7de745 A |
43 | exception_type_t exception, |
44 | const mach_exception_data_t code, | |
45 | mach_msg_type_number_t code_count, | |
46 | int * flavor, | |
47 | const thread_state_t old_state, | |
48 | mach_msg_type_number_t old_state_count, | |
49 | thread_state_t new_state, | |
50 | mach_msg_type_number_t * new_state_count) | |
d9a64523 A |
51 | { |
52 | #pragma unused(exception_port, exception, code, code_count, flavor, old_state, old_state_count, new_state, new_state_count) | |
53 | T_FAIL("Unsupported catch_mach_exception_raise_state"); | |
54 | return KERN_NOT_SUPPORTED; | |
55 | } | |
56 | ||
57 | kern_return_t | |
58 | catch_mach_exception_raise_state_identity(mach_port_t exception_port, | |
0a7de745 A |
59 | mach_port_t thread, |
60 | mach_port_t task, | |
61 | exception_type_t exception, | |
62 | mach_exception_data_t code, | |
63 | mach_msg_type_number_t code_count, | |
64 | int * flavor, | |
65 | thread_state_t old_state, | |
66 | mach_msg_type_number_t old_state_count, | |
67 | thread_state_t new_state, | |
68 | mach_msg_type_number_t * new_state_count) | |
d9a64523 A |
69 | { |
70 | #pragma unused(exception_port, thread, task, exception, code, code_count, flavor, old_state, old_state_count, new_state, new_state_count) | |
71 | T_FAIL("Unsupported catch_mach_exception_raise_state_identity"); | |
72 | return KERN_NOT_SUPPORTED; | |
73 | } | |
74 | ||
75 | ||
76 | /* | |
77 | * setup exception handling port for EXC_CORPSE_NOTIFY. | |
78 | * runs mach_msg_server once for receiving exception messages from kernel. | |
79 | */ | |
80 | static void * | |
81 | exc_handler(void * arg) | |
82 | { | |
83 | #pragma unused(arg) | |
84 | kern_return_t kret; | |
85 | mach_port_t exception_port; | |
86 | ||
87 | kret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exception_port); | |
0a7de745 | 88 | if (kret != KERN_SUCCESS) { |
d9a64523 | 89 | T_FAIL("mach_port_allocate: %s (%d)", mach_error_string(kret), kret); |
0a7de745 | 90 | } |
d9a64523 A |
91 | |
92 | kret = mach_port_insert_right(mach_task_self(), exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND); | |
0a7de745 | 93 | if (kret != KERN_SUCCESS) { |
d9a64523 | 94 | T_FAIL("mach_port_insert_right: %s (%d)", mach_error_string(kret), kret); |
0a7de745 | 95 | } |
d9a64523 A |
96 | |
97 | kret = task_set_exception_ports(mach_task_self(), EXC_MASK_CRASH | EXC_MASK_CORPSE_NOTIFY, exception_port, | |
0a7de745 A |
98 | (exception_behavior_t)(EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES), 0); |
99 | if (kret != KERN_SUCCESS) { | |
d9a64523 | 100 | T_FAIL("task_set_exception_ports: %s (%d)", mach_error_string(kret), kret); |
0a7de745 | 101 | } |
d9a64523 A |
102 | |
103 | dispatch_semaphore_signal(sync_sema); | |
104 | ||
105 | kret = mach_msg_server(mach_exc_server, MACH_MSG_SIZE_RELIABLE, exception_port, 0); | |
0a7de745 | 106 | if (kret != KERN_SUCCESS) { |
d9a64523 | 107 | T_FAIL("mach_msg_server: %s (%d)", mach_error_string(kret), kret); |
0a7de745 | 108 | } |
d9a64523 A |
109 | |
110 | return NULL; | |
111 | } | |
112 | ||
113 | static void* | |
0a7de745 A |
114 | dummy_thread(void *arg) |
115 | { | |
d9a64523 A |
116 | #pragma unused(arg) |
117 | while (1) { | |
118 | sleep(60); | |
119 | } | |
120 | } | |
121 | ||
122 | #define THREAD_LIMIT 2 | |
123 | ||
124 | T_HELPER_DECL(exc_resource_helper, "exc_resource helper") | |
125 | { | |
126 | pthread_t tid; | |
127 | for (int i = 0; i < THREAD_LIMIT; i++) { | |
128 | T_QUIET; | |
129 | T_EXPECT_POSIX_SUCCESS(pthread_create(&tid, NULL, dummy_thread, NULL), "pthread_create"); | |
130 | } | |
131 | while (1) { | |
132 | sleep(60); | |
133 | } | |
134 | } | |
135 | ||
136 | static void | |
137 | check_exc_resource_threads_enabled() | |
138 | { | |
139 | int err; | |
140 | int enabled; | |
141 | size_t enabled_size = sizeof(enabled); | |
142 | err = sysctlbyname("kern.exc_resource_threads_enabled", &enabled, &enabled_size, NULL, 0); | |
143 | ||
0a7de745 | 144 | if (err || !enabled) { |
d9a64523 | 145 | T_SKIP("EXC_RESOURCE RESOURCE_TYPE_THREADS not enabled on this system"); |
0a7de745 | 146 | } |
d9a64523 A |
147 | } |
148 | ||
149 | T_DECL(exc_resource_threads, "Ensures that a process with a thread_limit set will receive an exc_resource when it crosses its thread limit", | |
0a7de745 A |
150 | T_META_ASROOT(true), |
151 | T_META_CHECK_LEAKS(false)) | |
d9a64523 A |
152 | { |
153 | pthread_t handle_thread; | |
154 | ||
155 | check_exc_resource_threads_enabled(); | |
156 | ||
157 | sync_sema = dispatch_semaphore_create(0); | |
158 | ||
159 | T_ASSERT_POSIX_ZERO(pthread_create(&handle_thread, NULL, exc_handler, NULL), "pthread_create"); | |
160 | dispatch_semaphore_wait(sync_sema, DISPATCH_TIME_FOREVER); | |
161 | ||
162 | pid_t helper_pid; | |
163 | char path[PATH_MAX]; | |
164 | uint32_t path_size = sizeof(path); | |
165 | ||
166 | T_ASSERT_POSIX_ZERO(_NSGetExecutablePath(path, &path_size), "_NSGetExecutablePath"); | |
167 | ||
168 | char *args[] = { path, "-n", "exc_resource_helper", NULL }; | |
169 | ||
170 | posix_spawnattr_t attr; | |
171 | T_ASSERT_POSIX_ZERO(posix_spawnattr_init(&attr), "posix_spawnattr_init"); | |
172 | ||
173 | T_EXPECT_POSIX_ZERO(posix_spawnattr_set_threadlimit_ext(&attr, THREAD_LIMIT), "posix_spawnattr_set_threadlimit_ext"); | |
174 | ||
175 | T_EXPECT_POSIX_ZERO(posix_spawn(&helper_pid, args[0], NULL, &attr, args, NULL), "posix_spawn"); | |
176 | ||
177 | T_ASSERT_POSIX_ZERO(posix_spawnattr_destroy(&attr), "posix_spawnattr_destroy"); | |
178 | ||
179 | dispatch_semaphore_wait(sync_sema, DISPATCH_TIME_FOREVER); | |
180 | } |