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