1 #include <darwintest.h>
2 #include <darwintest_multiprocess.h>
3 #include <mach-o/dyld.h>
4 #include <spawn.h>
5 #include <unistd.h>
6 #include <sys/wait.h>
7 #include <mach/exception_types.h>
8 #include <mach/mach_port.h>
9 #include <mach/mach.h>
10 #include <mach/mach_interface.h>
11 #include <unistd.h>
12 #include "excserver_protect_state.h"
13 #include <TargetConditionals.h>
14 
15 T_GLOBAL_META(
16 	T_META_NAMESPACE("xnu.ipc"),
17 	T_META_RADAR_COMPONENT_NAME("xnu"),
18 	T_META_RADAR_COMPONENT_VERSION("IPC"),
19 	T_META_TAG_VM_PREFERRED);
20 
21 T_HELPER_DECL(child_exit, "Call exit() which will call sys_perf_notify()")
22 {
23 	T_LOG("Child exiting...");
24 	exit(0);
25 }
26 
27 static mach_port_t exc_port;
28 static bool caught_exceptiion = false;
29 
30 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)31 catch_mach_exception_raise_state(mach_port_t exception_port,
32     exception_type_t exception,
33     const mach_exception_data_t code,
34     mach_msg_type_number_t code_count,
35     int * flavor,
36     const thread_state_t old_state,
37     mach_msg_type_number_t old_state_count,
38     thread_state_t new_state,
39     mach_msg_type_number_t * new_state_count)
40 {
41 #pragma unused(exception_port, exception, code, code_count, flavor, old_state, old_state_count, new_state, new_state_count)
42 	T_FAIL("Unsupported catch_mach_exception_raise_state");
43 	return KERN_NOT_SUPPORTED;
44 }
45 
46 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)47 catch_mach_exception_raise_state_identity(mach_port_t exception_port,
48     mach_port_t thread,
49     mach_port_t task,
50     exception_type_t exception,
51     mach_exception_data_t code,
52     mach_msg_type_number_t code_count,
53     int * flavor,
54     thread_state_t old_state,
55     mach_msg_type_number_t old_state_count,
56     thread_state_t new_state,
57     mach_msg_type_number_t * new_state_count)
58 {
59 #pragma unused(exception_port, thread, task, exception, code, code_count, flavor, old_state, old_state_count, new_state, new_state_count)
60 	T_FAIL("Unsupported catch_mach_exception_raise_state_identity");
61 	return KERN_NOT_SUPPORTED;
62 }
63 
64 kern_return_t
catch_mach_exception_raise_state_identity_protected(mach_port_t exception_port,uint64_t thread_id,mach_port_t task_id_token,exception_type_t exception,mach_exception_data_t codes,mach_msg_type_number_t codeCnt,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)65 catch_mach_exception_raise_state_identity_protected(
66 	mach_port_t exception_port,
67 	uint64_t thread_id,
68 	mach_port_t task_id_token,
69 	exception_type_t exception,
70 	mach_exception_data_t codes,
71 	mach_msg_type_number_t codeCnt,
72 	int * flavor,
73 	thread_state_t old_state,
74 	mach_msg_type_number_t old_state_count,
75 	thread_state_t new_state,
76 	mach_msg_type_number_t * new_state_count)
77 {
78 #pragma unused(exception_port, thread_id, tatask_id_tokensk, exception, codes, codeCnt, flavor, old_state, old_state_count, new_state, new_state_count)
79 	T_FAIL("Unsupported catch_mach_exception_raise_state_identity");
80 	return KERN_NOT_SUPPORTED;
81 }
82 
83 kern_return_t
catch_mach_exception_raise_identity_protected(mach_port_t exception_port,uint64_t thread_id,mach_port_t task_id_token,exception_type_t exception,mach_exception_data_t codes,mach_msg_type_number_t codeCnt)84 catch_mach_exception_raise_identity_protected(
85 	mach_port_t               exception_port,
86 	uint64_t                  thread_id,
87 	mach_port_t               task_id_token,
88 	exception_type_t          exception,
89 	mach_exception_data_t     codes,
90 	mach_msg_type_number_t    codeCnt)
91 {
92 #pragma unused(thread_id, task_id_token)
93 	caught_exceptiion = true;
94 	T_QUIET; T_ASSERT_EQ(exception_port, exc_port, "correct exception port");
95 	T_QUIET; T_ASSERT_EQ(exception, EXC_RPC_ALERT, "exception type is EXC_RPC_ALERT");
96 	T_QUIET; T_ASSERT_EQ(codeCnt, 2, "codeCnt is 2");
97 	T_QUIET; T_ASSERT_EQ(codes[0], 0xFF000001, "codes[0] is 0xFF000001");
98 	return KERN_SUCCESS;
99 }
100 
101 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)102 catch_mach_exception_raise(mach_port_t exception_port,
103     mach_port_t thread,
104     mach_port_t task,
105     exception_type_t exception,
106     mach_exception_data_t code,
107     mach_msg_type_number_t code_count)
108 {
109 #pragma unused(exception_port, thread, task, exception, code, code_count)
110 	T_FAIL("Unsupported catch_mach_exception_raise_state_identity");
111 	return KERN_NOT_SUPPORTED;
112 }
113 
114 static void
run_test(void)115 run_test(void)
116 {
117 	int ret, child_pid;
118 	task_t task;
119 	mach_port_options_t opts = {
120 		.flags = MPO_INSERT_SEND_RIGHT,
121 	};
122 	char path[1024];
123 	uint32_t size = sizeof(path);
124 	exception_mask_t masks[EXC_TYPES_COUNT];
125 	mach_msg_type_number_t nmasks = 0;
126 	exception_port_t old_ports[EXC_TYPES_COUNT];
127 	exception_behavior_t old_behaviors[EXC_TYPES_COUNT];
128 	thread_state_flavor_t old_flavors[EXC_TYPES_COUNT];
129 
130 	/* Save the current host exception port for EXC_MASK_RPC_ALERT */
131 	ret = host_get_exception_ports(mach_host_self(), EXC_MASK_RPC_ALERT,
132 	    masks, &nmasks, old_ports, old_behaviors, old_flavors);
133 	T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "host_get_exception_ports");
134 
135 	/* Allocate a new port to catch exception */
136 	task = mach_task_self();
137 	T_QUIET; T_ASSERT_NE(task, MACH_PORT_NULL, "mach_task_self");
138 
139 	ret = mach_port_construct(task, &opts, 0ull, &exc_port);
140 	T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "mach_port_construct");
141 
142 	/* Set the new port as the host exception port */
143 	ret = host_set_exception_ports(mach_host_self(), EXC_MASK_RPC_ALERT, exc_port, EXCEPTION_IDENTITY_PROTECTED | MACH_EXCEPTION_CODES, old_flavors[0]);
144 	T_ASSERT_MACH_SUCCESS(ret, "Set up host exception port for EXC_MASK_RPC_ALERT");
145 
146 	/* Spawn child to call exit() which calls into sys_perf_notify() */
147 	T_QUIET; T_ASSERT_POSIX_ZERO(_NSGetExecutablePath(path, &size), "_NSGetExecutablePath");
148 	char *args[] = { path, "-n", "child_exit", NULL };
149 	T_ASSERT_POSIX_ZERO(posix_spawn(&child_pid, args[0], NULL, NULL, args, NULL), "Spawn child to call exit()");
150 
151 	/* We should receive an exception */
152 	ret = mach_msg_server_once(mach_exc_server, 4096, exc_port, 0);
153 	T_ASSERT_MACH_SUCCESS(ret, "Host exception port should receive an exception after child exited");
154 	T_ASSERT_EQ(caught_exceptiion, true, "catch_mach_exception_raise_identity_protected() triggered");
155 
156 	/* Reset host exception port */
157 	ret = host_set_exception_ports(mach_host_self(), EXC_MASK_RPC_ALERT, old_ports[0], old_behaviors[0], old_flavors[0]);
158 	T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "resetting host exception port");
159 
160 	/* Deallocate exception port */
161 	ret = mach_port_deallocate(task, exc_port);
162 	T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "mach_port_deallocate");
163 }
164 
165 T_DECL(sys_perf_notify_test, "test sys_perf_notify delivery exception successfully when process exits", T_META_ASROOT(true))
166 {
167 	run_test();
168 }
169