xref: /xnu-12377.81.4/tests/mach_service_port.c (revision 043036a2b3718f7f0be807e2870f8f47d3fa0796)
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 	uint64_t fpid = 0;
54 	boolean_t is_throttled;
55 
56 	struct mach_service_port_info sp_info = {};
57 
58 	strcpy(sp_info.mspi_string_name, SERVICE_NAME);
59 	sp_info.mspi_domain_type = (uint8_t)SERVICE_DOMAIN;
60 	kern_return_t kr;
61 
62 	mach_port_options_t opts = {
63 		.flags = MPO_SERVICE_PORT | MPO_INSERT_SEND_RIGHT | MPO_CONTEXT_AS_GUARD | MPO_STRICT,
64 		.service_port_info = &sp_info,
65 	};
66 
67 	kr = mach_port_construct(mach_task_self(), &opts, SP_CONTEXT, &service_port);
68 	T_ASSERT_MACH_SUCCESS(kr, "mach_port_construct %u", service_port);
69 
70 	mach_port_options_t opts2 = {
71 		.flags = MPO_CONNECTION_PORT,
72 		.service_port_name = service_port,
73 	};
74 
75 	kr = mach_port_construct(mach_task_self(), &opts2, 0x0, &connection_port);
76 	T_ASSERT_MACH_SUCCESS(kr, "mach_port_construct %u", connection_port);
77 
78 	kr = mach_port_is_connection_for_service(mach_task_self(), connection_port, service_port, &fpid);
79 	T_ASSERT_MACH_SUCCESS(kr, "mach_port_is_connection_for_service");
80 
81 	/* Test port throttling flag */
82 	kr = service_port_get_throttled(&is_throttled);
83 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "get throttled flag value on port");
84 	T_ASSERT_EQ(is_throttled, 0, "newly created service port is not throttled");
85 
86 	kr = service_port_set_throttled(1);
87 	T_ASSERT_MACH_SUCCESS(kr, "set throttled flag on port");
88 
89 	kr = service_port_get_throttled(&is_throttled);
90 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "get throttled flag value on port");
91 	T_ASSERT_EQ(is_throttled, 1, "port is throttled");
92 
93 	kr = service_port_set_throttled(0);
94 	T_ASSERT_MACH_SUCCESS(kr, "unset throttled flag on port");
95 
96 	kr = service_port_get_throttled(&is_throttled);
97 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "get throttled flag value on port");
98 	T_ASSERT_EQ(is_throttled, false, "port is no longer throttled");
99 
100 	/* Attempt to destroy port */
101 	kr = mach_port_destruct(mach_task_self(), service_port, 0, SP_CONTEXT);
102 	T_ASSERT_MACH_SUCCESS(kr, "mach_port_destruct service_port");
103 
104 	T_LOG("done");
105 }
106 
107 kern_return_t
do_mach_notify_port_destroyed(mach_port_t notify,mach_port_t name)108 do_mach_notify_port_destroyed(mach_port_t notify, mach_port_t name)
109 {
110 	kern_return_t kr;
111 
112 	T_LOG("Received a service port destroyed notification notify = 0x%x name = 0x%x", notify, name);
113 	if (name == MACH_PORT_NULL) {
114 		T_FAIL("do_mach_notify_port_destroyed: MACH_PORT_NULL?");
115 	}
116 
117 	if (name != service_port) {
118 		T_FAIL("do_mach_notify_port_destroyed: name 0x%x != service_port: 0x%x", name, service_port);
119 	}
120 
121 	struct mach_service_port_info sp_info = {};
122 	kr = mach_port_get_service_port_info(mach_task_self(), service_port, &sp_info);
123 	T_ASSERT_MACH_SUCCESS(kr, "mach_port_get_service_port_info");
124 
125 	if (strcmp(sp_info.mspi_string_name, SERVICE_NAME)) {
126 		T_FAIL("Service port name = %s is incorrect", sp_info.mspi_string_name);
127 	}
128 	T_ASSERT_EQ(sp_info.mspi_domain_type, SERVICE_DOMAIN, "Service domain = %u", sp_info.mspi_domain_type);
129 
130 	mach_port_guard_info_t mpgi = {SP_CONTEXT};
131 	kr = mach_port_assert_attributes(mach_task_self(), service_port, MACH_PORT_GUARD_INFO, (mach_port_info_t)&mpgi, MACH_PORT_GUARD_INFO_COUNT);
132 	T_ASSERT_MACH_SUCCESS(kr, "mach_port_assert_attributes");
133 
134 	return KERN_SUCCESS;
135 }
136 
137 kern_return_t
do_mach_notify_port_deleted(__unused mach_port_t notify,__unused mach_port_name_t name)138 do_mach_notify_port_deleted(__unused mach_port_t notify, __unused mach_port_name_t name)
139 {
140 	return KERN_SUCCESS;
141 }
142 
143 kern_return_t
do_mach_notify_no_senders(__unused mach_port_t notify,__unused mach_port_mscount_t mscount)144 do_mach_notify_no_senders(__unused mach_port_t notify, __unused mach_port_mscount_t mscount)
145 {
146 	return KERN_SUCCESS;
147 }
148 
149 kern_return_t
do_mach_notify_send_once(__unused mach_port_t notify)150 do_mach_notify_send_once(__unused mach_port_t notify)
151 {
152 	return KERN_SUCCESS;
153 }
154 
155 kern_return_t
do_mach_notify_dead_name(__unused mach_port_t notify,__unused mach_port_name_t name)156 do_mach_notify_dead_name(__unused mach_port_t notify, __unused mach_port_name_t name)
157 {
158 	return KERN_SUCCESS;
159 }
160 
161 #define SERVICE_NAME_2 "com.apple.testservice2"
162 #define SERVICE_DOMAIN_2 (2)
163 
164 T_DECL(mach_fake_service_port, "Create a connection port with a fake service port", T_META_CHECK_LEAKS(false))
165 {
166 	mach_port_t connection_port;
167 	mach_port_t fake_service_port;
168 	mach_port_t service_port_2;
169 
170 	kern_return_t kr;
171 
172 	struct mach_service_port_info sp_info = {};
173 
174 	strcpy(sp_info.mspi_string_name, SERVICE_NAME_2);
175 	sp_info.mspi_domain_type = (uint8_t)SERVICE_DOMAIN_2;
176 
177 	mach_port_options_t opts = {
178 		.flags = MPO_CONNECTION_PORT | MPO_SERVICE_PORT | MPO_INSERT_SEND_RIGHT | MPO_CONTEXT_AS_GUARD,
179 		.service_port_info = &sp_info,
180 	};
181 
182 	kr = mach_port_construct(mach_task_self(), &opts, SP_CONTEXT, &service_port_2);
183 	T_ASSERT_MACH_ERROR(kr, KERN_INVALID_ARGUMENT, "mach_port_construct with extra flags %u", service_port_2);
184 
185 	mach_port_options_t opts2 = {
186 		.flags = MPO_SERVICE_PORT | MPO_INSERT_SEND_RIGHT | MPO_CONTEXT_AS_GUARD,
187 		.service_port_info = NULL,
188 	};
189 
190 	kr = mach_port_construct(mach_task_self(), &opts2, SP_CONTEXT, &service_port_2);
191 	T_ASSERT_MACH_ERROR(kr, KERN_INVALID_ARGUMENT, "mach_port_construct with missing service port info %u", service_port_2);
192 
193 	mach_port_options_t opts3 = {
194 		.flags = MPO_INSERT_SEND_RIGHT | MPO_CONTEXT_AS_GUARD,
195 	};
196 
197 	kr = mach_port_construct(mach_task_self(), &opts3, SP_CONTEXT, &fake_service_port);
198 	T_ASSERT_MACH_SUCCESS(kr, "mach_port_construct with missing flag %u", fake_service_port);
199 
200 	struct mach_service_port_info sp_info3 = {};
201 	kr = mach_port_get_service_port_info(mach_task_self(), fake_service_port, &sp_info3);
202 	T_ASSERT_MACH_ERROR(kr, KERN_INVALID_CAPABILITY, "mach_port_get_service_port_info");
203 
204 	mach_port_options_t opts4 = {
205 		.flags = MPO_CONNECTION_PORT,
206 		.service_port_name = fake_service_port,
207 	};
208 
209 	kr = mach_port_construct(mach_task_self(), &opts4, 0x0, &connection_port);
210 	T_ASSERT_MACH_ERROR(kr, KERN_INVALID_CAPABILITY, "mach_port_construct connection port %u", connection_port);
211 
212 	T_LOG("done");
213 }
214 
215 T_DECL(mach_dead_service_port, "Create a connection port with a dead service port", T_META_CHECK_LEAKS(false))
216 {
217 	mach_port_t connection_port;
218 	mach_port_t service_port_2;
219 
220 	kern_return_t kr;
221 
222 	struct mach_service_port_info sp_info = {};
223 
224 	strcpy(sp_info.mspi_string_name, SERVICE_NAME_2);
225 	sp_info.mspi_domain_type = (uint8_t)SERVICE_DOMAIN_2;
226 
227 	mach_port_options_t opts = {
228 		.flags = MPO_SERVICE_PORT | MPO_INSERT_SEND_RIGHT,
229 		.service_port_info = &sp_info,
230 	};
231 
232 	kr = mach_port_construct(mach_task_self(), &opts, 0, &service_port_2);
233 	T_ASSERT_MACH_SUCCESS(kr, "mach_port_construct %u", service_port_2);
234 
235 	kr = mach_port_mod_refs(mach_task_self(), service_port_2, MACH_PORT_RIGHT_RECEIVE, -1);
236 	T_ASSERT_MACH_SUCCESS(kr, "mach_port_mod_refs");
237 
238 	mach_port_options_t opts3 = {
239 		.flags = MPO_CONNECTION_PORT,
240 		.service_port_name = service_port_2,
241 	};
242 
243 	kr = mach_port_construct(mach_task_self(), &opts3, 0x0, &connection_port);
244 	T_LOG("mach_port_construct connection port kr = %d", kr);
245 
246 	if (kr == KERN_INVALID_RIGHT || kr == KERN_INVALID_NAME) {
247 		T_PASS("Invalid service port");
248 	} else {
249 		T_FAIL("mach_port_construct incorrect return value");
250 	}
251 
252 	T_LOG("done");
253 }
254