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