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