1 #include <darwintest.h>
2 #include <servers/bootstrap.h>
3 #include <mach/mach.h>
4 #include <mach/message.h>
5 #include <stdlib.h>
6 #include <sys/sysctl.h>
7 #include <unistd.h>
8 #include <mach/port.h>
9 #include <mach/mach_port.h>
10 #include <stdint.h>
11 #include <stdio.h>
12 #include <unistd.h>
13 #include <pthread.h>
14 #include <err.h>
15 #include <sysexits.h>
16
17 #include "notifyServer.h"
18
19 T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true),
20 T_META_NAMESPACE("xnu.ipc"),
21 T_META_RADAR_COMPONENT_NAME("xnu"),
22 T_META_RADAR_COMPONENT_VERSION("IPC"),
23 T_META_TAG_VM_PREFERRED);
24
25 static mach_port_t service_port = MACH_PORT_NULL;
26
27 #define SP_CONTEXT (0x1803)
28 #define NEW_SP_CONTEXT (0x0318)
29 #define SERVICE_NAME "com.apple.testservice"
30 #define SERVICE_DOMAIN (1)
31
32 static inline kern_return_t
service_port_set_throttled(int is_throttled)33 service_port_set_throttled(int is_throttled)
34 {
35 return mach_port_set_attributes(mach_task_self(), service_port, MACH_PORT_SERVICE_THROTTLED, (mach_port_info_t)(&is_throttled),
36 MACH_PORT_SERVICE_THROTTLED_COUNT);
37 }
38
39 static inline kern_return_t
service_port_get_throttled(int * is_throttled)40 service_port_get_throttled(int *is_throttled)
41 {
42 natural_t count = 0;
43 kern_return_t kr;
44
45 kr = mach_port_get_attributes(mach_task_self(), service_port, MACH_PORT_SERVICE_THROTTLED, (mach_port_info_t)(is_throttled), &count);
46 T_QUIET; T_ASSERT_EQ(count, MACH_PORT_SERVICE_THROTTLED_COUNT, NULL);
47
48 return kr;
49 }
50
51 T_DECL(mach_service_port, "Create a port with a service port label", T_META_CHECK_LEAKS(false)) {
52 mach_port_t connection_port;
53 mach_port_t notify_port;
54 mach_port_t previous;
55 uint64_t fpid = 0;
56 boolean_t is_throttled;
57
58 struct mach_service_port_info sp_info = {};
59
60 strcpy(sp_info.mspi_string_name, SERVICE_NAME);
61 sp_info.mspi_domain_type = (uint8_t)SERVICE_DOMAIN;
62 kern_return_t kr;
63
64 mach_port_options_t opts = {
65 .flags = MPO_SERVICE_PORT | MPO_INSERT_SEND_RIGHT | MPO_CONTEXT_AS_GUARD | MPO_STRICT,
66 .service_port_info = &sp_info,
67 };
68
69 kr = mach_port_construct(mach_task_self(), &opts, SP_CONTEXT, &service_port);
70 T_ASSERT_MACH_SUCCESS(kr, "mach_port_construct %u", service_port);
71
72 mach_port_options_t opts2 = {
73 .flags = MPO_CONNECTION_PORT,
74 .service_port_name = service_port,
75 };
76
77 kr = mach_port_construct(mach_task_self(), &opts2, 0x0, &connection_port);
78 T_ASSERT_MACH_SUCCESS(kr, "mach_port_construct %u", connection_port);
79
80 kr = mach_port_is_connection_for_service(mach_task_self(), connection_port, service_port, &fpid);
81 if (kr != KERN_SUCCESS || kr != KERN_NOT_SUPPORTED) {
82 T_LOG("mach_port_is_connection_for_service kr = %d, fpid = %llu", kr, fpid);
83 }
84
85 // notification port for the service port to come back on
86 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, ¬ify_port);
87 T_ASSERT_MACH_SUCCESS(kr, "mach_port_allocate notify_port");
88
89 kr = mach_port_insert_right(mach_task_self(), notify_port, notify_port, MACH_MSG_TYPE_MAKE_SEND);
90 T_ASSERT_MACH_SUCCESS(kr, "mach_port_insert_right notify_port");
91
92 T_LOG("service port: 0x%x, notify port: 0x%x\n", service_port, notify_port);
93
94 kr = mach_port_request_notification(mach_task_self(), service_port, MACH_NOTIFY_PORT_DESTROYED, 0, notify_port,
95 MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous);
96 T_ASSERT_MACH_SUCCESS(kr, "mach_port_request_notification service_port");
97 T_ASSERT_EQ(previous, MACH_PORT_NULL, "previous null");
98
99 /* Test port throttling flag */
100 kr = service_port_get_throttled(&is_throttled);
101 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "get throttled flag value on port");
102 T_ASSERT_EQ(is_throttled, 0, "newly created service port is not throttled");
103
104 kr = service_port_set_throttled(1);
105 T_ASSERT_MACH_SUCCESS(kr, "set throttled flag on port");
106
107 kr = service_port_get_throttled(&is_throttled);
108 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "get throttled flag value on port");
109 T_ASSERT_EQ(is_throttled, 1, "port is throttled");
110
111 kr = service_port_set_throttled(0);
112 T_ASSERT_MACH_SUCCESS(kr, "unset throttled flag on port");
113
114 kr = service_port_get_throttled(&is_throttled);
115 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "get throttled flag value on port");
116 T_ASSERT_EQ(is_throttled, false, "port is no longer throttled");
117
118 /* Attempt to destroy port */
119 kr = mach_port_destruct(mach_task_self(), service_port, 0, SP_CONTEXT);
120 T_ASSERT_MACH_SUCCESS(kr, "mach_port_destruct service_port");
121
122 /*
123 * Recover the service port because the port must have been destroyed and sent the notification by now
124 */
125 kr = mach_msg_server_once(notify_server, MACH_MSG_SIZE_RELIABLE, notify_port, MACH_RCV_TIMEOUT);
126 T_ASSERT_MACH_SUCCESS(kr, "mach_msg_server_once notify_port");
127
128 T_LOG("done");
129 }
130
131 kern_return_t
do_mach_notify_port_destroyed(mach_port_t notify,mach_port_t name)132 do_mach_notify_port_destroyed(mach_port_t notify, mach_port_t name)
133 {
134 kern_return_t kr;
135
136 T_LOG("Received a service port destroyed notification notify = 0x%x name = 0x%x", notify, name);
137 if (name == MACH_PORT_NULL) {
138 T_FAIL("do_mach_notify_port_destroyed: MACH_PORT_NULL?");
139 }
140
141 if (name != service_port) {
142 T_FAIL("do_mach_notify_port_destroyed: name 0x%x != service_port: 0x%x", name, service_port);
143 }
144
145 struct mach_service_port_info sp_info = {};
146 kr = mach_port_get_service_port_info(mach_task_self(), service_port, &sp_info);
147 T_ASSERT_MACH_SUCCESS(kr, "mach_port_get_service_port_info");
148
149 if (strcmp(sp_info.mspi_string_name, SERVICE_NAME)) {
150 T_FAIL("Service port name = %s is incorrect", sp_info.mspi_string_name);
151 }
152 T_ASSERT_EQ(sp_info.mspi_domain_type, SERVICE_DOMAIN, "Service domain = %u", sp_info.mspi_domain_type);
153
154 mach_port_guard_info_t mpgi = {SP_CONTEXT};
155 kr = mach_port_assert_attributes(mach_task_self(), service_port, MACH_PORT_GUARD_INFO, (mach_port_info_t)&mpgi, MACH_PORT_GUARD_INFO_COUNT);
156 T_ASSERT_MACH_SUCCESS(kr, "mach_port_assert_attributes");
157
158 return KERN_SUCCESS;
159 }
160
161 kern_return_t
do_mach_notify_port_deleted(__unused mach_port_t notify,__unused mach_port_name_t name)162 do_mach_notify_port_deleted(__unused mach_port_t notify, __unused mach_port_name_t name)
163 {
164 return KERN_SUCCESS;
165 }
166
167 kern_return_t
do_mach_notify_no_senders(__unused mach_port_t notify,__unused mach_port_mscount_t mscount)168 do_mach_notify_no_senders(__unused mach_port_t notify, __unused mach_port_mscount_t mscount)
169 {
170 return KERN_SUCCESS;
171 }
172
173 kern_return_t
do_mach_notify_send_once(__unused mach_port_t notify)174 do_mach_notify_send_once(__unused mach_port_t notify)
175 {
176 return KERN_SUCCESS;
177 }
178
179 kern_return_t
do_mach_notify_dead_name(__unused mach_port_t notify,__unused mach_port_name_t name)180 do_mach_notify_dead_name(__unused mach_port_t notify, __unused mach_port_name_t name)
181 {
182 return KERN_SUCCESS;
183 }
184
185 #define SERVICE_NAME_2 "com.apple.testservice2"
186 #define SERVICE_DOMAIN_2 (2)
187
188 T_DECL(mach_fake_service_port, "Create a connection port with a fake service port", T_META_CHECK_LEAKS(false))
189 {
190 mach_port_t connection_port;
191 mach_port_t fake_service_port;
192 mach_port_t service_port_2;
193
194 kern_return_t kr;
195
196 struct mach_service_port_info sp_info = {};
197
198 strcpy(sp_info.mspi_string_name, SERVICE_NAME_2);
199 sp_info.mspi_domain_type = (uint8_t)SERVICE_DOMAIN_2;
200
201 mach_port_options_t opts = {
202 .flags = MPO_CONNECTION_PORT | MPO_SERVICE_PORT | MPO_INSERT_SEND_RIGHT | MPO_CONTEXT_AS_GUARD,
203 .service_port_info = &sp_info,
204 };
205
206 kr = mach_port_construct(mach_task_self(), &opts, SP_CONTEXT, &service_port_2);
207 T_ASSERT_MACH_ERROR(kr, KERN_INVALID_ARGUMENT, "mach_port_construct with extra flags %u", service_port_2);
208
209 mach_port_options_t opts2 = {
210 .flags = MPO_SERVICE_PORT | MPO_INSERT_SEND_RIGHT | MPO_CONTEXT_AS_GUARD,
211 .service_port_info = NULL,
212 };
213
214 kr = mach_port_construct(mach_task_self(), &opts2, SP_CONTEXT, &service_port_2);
215 T_ASSERT_MACH_ERROR(kr, KERN_INVALID_ARGUMENT, "mach_port_construct with missing service port info %u", service_port_2);
216
217 mach_port_options_t opts3 = {
218 .flags = MPO_INSERT_SEND_RIGHT | MPO_CONTEXT_AS_GUARD,
219 };
220
221 kr = mach_port_construct(mach_task_self(), &opts3, SP_CONTEXT, &fake_service_port);
222 T_ASSERT_MACH_SUCCESS(kr, "mach_port_construct with missing flag %u", fake_service_port);
223
224 struct mach_service_port_info sp_info3 = {};
225 kr = mach_port_get_service_port_info(mach_task_self(), fake_service_port, &sp_info3);
226 T_ASSERT_MACH_ERROR(kr, KERN_INVALID_CAPABILITY, "mach_port_get_service_port_info");
227
228 mach_port_options_t opts4 = {
229 .flags = MPO_CONNECTION_PORT,
230 .service_port_name = fake_service_port,
231 };
232
233 kr = mach_port_construct(mach_task_self(), &opts4, 0x0, &connection_port);
234 T_ASSERT_MACH_ERROR(kr, KERN_INVALID_CAPABILITY, "mach_port_construct connection port %u", connection_port);
235
236 T_LOG("done");
237 }
238
239 T_DECL(mach_dead_service_port, "Create a connection port with a dead service port", T_META_CHECK_LEAKS(false))
240 {
241 mach_port_t connection_port;
242 mach_port_t service_port_2;
243
244 kern_return_t kr;
245
246 struct mach_service_port_info sp_info = {};
247
248 strcpy(sp_info.mspi_string_name, SERVICE_NAME_2);
249 sp_info.mspi_domain_type = (uint8_t)SERVICE_DOMAIN_2;
250
251 mach_port_options_t opts = {
252 .flags = MPO_SERVICE_PORT | MPO_INSERT_SEND_RIGHT,
253 .service_port_info = &sp_info,
254 };
255
256 kr = mach_port_construct(mach_task_self(), &opts, 0, &service_port_2);
257 T_ASSERT_MACH_SUCCESS(kr, "mach_port_construct %u", service_port_2);
258
259 kr = mach_port_mod_refs(mach_task_self(), service_port_2, MACH_PORT_RIGHT_RECEIVE, -1);
260 T_ASSERT_MACH_SUCCESS(kr, "mach_port_mod_refs");
261
262 mach_port_options_t opts3 = {
263 .flags = MPO_CONNECTION_PORT,
264 .service_port_name = service_port_2,
265 };
266
267 kr = mach_port_construct(mach_task_self(), &opts3, 0x0, &connection_port);
268 T_LOG("mach_port_construct connection port kr = %d", kr);
269
270 if (kr == KERN_INVALID_RIGHT || kr == KERN_INVALID_NAME) {
271 T_PASS("Invalid service port");
272 } else {
273 T_FAIL("mach_port_construct incorrect return value");
274 }
275
276 T_LOG("done");
277 }
278