1 #include <darwintest.h>
2 #include <darwintest_multiprocess.h>
3 #include <launch.h>
4 #include <servers/bootstrap.h>
5 #include <sys/sysctl.h>
6 #include "exc_helpers.h"
7
8 T_GLOBAL_META(
9 T_META_NAMESPACE("xnu.ipc"),
10 T_META_RADAR_COMPONENT_NAME("xnu"),
11 T_META_RADAR_COMPONENT_VERSION("IPC"),
12 T_META_RUN_CONCURRENTLY(true));
13
14 #pragma mark - helpers
15
16 #define SERVICE_NAME "com.apple.xnu.test.mach_port"
17
18 struct one_port_msg {
19 mach_msg_header_t header;
20 mach_msg_body_t body;
21 mach_msg_port_descriptor_t port_descriptor;
22 mach_msg_trailer_t trailer; // subtract this when sending
23 };
24
25 static mach_port_t
server_checkin(void)26 server_checkin(void)
27 {
28 mach_port_t mp;
29 kern_return_t kr;
30
31 kr = bootstrap_check_in(bootstrap_port, SERVICE_NAME, &mp);
32 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "bootstrap_check_in");
33 return mp;
34 }
35
36 static mach_port_t
server_lookup(void)37 server_lookup(void)
38 {
39 mach_port_t mp;
40 kern_return_t kr;
41
42 kr = bootstrap_look_up(bootstrap_port, SERVICE_NAME, &mp);
43 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "bootstrap_look_up");
44 return mp;
45 }
46
47 static mach_port_t
make_sr_port(void)48 make_sr_port(void)
49 {
50 mach_port_options_t opts = {
51 .flags = MPO_INSERT_SEND_RIGHT,
52 };
53 kern_return_t kr;
54 mach_port_t port;
55
56 kr = mach_port_construct(mach_task_self(), &opts, 0ull, &port);
57 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_port_construct");
58 return port;
59 }
60
61 static void
destroy_port(mach_port_t port,bool receive,int srights)62 destroy_port(mach_port_t port, bool receive, int srights)
63 {
64 kern_return_t kr;
65
66 if (srights) {
67 kr = mach_port_mod_refs(mach_task_self(), port,
68 MACH_PORT_RIGHT_SEND, -srights);
69 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "srights -= %d", srights);
70 }
71 if (receive) {
72 kr = mach_port_mod_refs(mach_task_self(), port,
73 MACH_PORT_RIGHT_RECEIVE, -1);
74 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "receive -= 1");
75 }
76 }
77
78 static void
send_port(mach_msg_id_t id,mach_port_t dest,mach_port_t right,mach_msg_type_name_t disp)79 send_port(
80 mach_msg_id_t id,
81 mach_port_t dest,
82 mach_port_t right,
83 mach_msg_type_name_t disp)
84 {
85 struct one_port_msg msg = {
86 .header = {
87 .msgh_remote_port = dest,
88 .msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND,
89 0, MACH_MSG_TYPE_MOVE_SEND, MACH_MSGH_BITS_COMPLEX),
90 .msgh_id = id,
91 .msgh_size = offsetof(struct one_port_msg, trailer),
92 },
93 .body = {
94 .msgh_descriptor_count = 1,
95 },
96 .port_descriptor = {
97 .name = right,
98 .disposition = disp,
99 .type = MACH_MSG_PORT_DESCRIPTOR,
100 },
101 };
102 kern_return_t kr;
103
104 kr = mach_msg(&msg.header, MACH_SEND_MSG | MACH_SEND_TIMEOUT,
105 msg.header.msgh_size, 0, MACH_PORT_NULL, 10000, 0);
106
107 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "send(%d)", id);
108 }
109
110 #pragma mark - basic test about right deduplication
111
112 static mach_port_t
receive_port(mach_msg_id_t expected_id,mach_port_t rcv_port,mach_msg_type_name_t expected_disp)113 receive_port(
114 mach_msg_id_t expected_id,
115 mach_port_t rcv_port,
116 mach_msg_type_name_t expected_disp)
117 {
118 struct one_port_msg msg = { };
119 kern_return_t kr;
120
121 T_LOG("waiting for message %d", expected_id);
122 kr = mach_msg(&msg.header, MACH_RCV_MSG, 0,
123 sizeof(msg), rcv_port, 0, 0);
124 T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "receive(%d)", expected_id);
125 T_QUIET; T_ASSERT_EQ(msg.header.msgh_id, expected_id, "message id matches");
126 T_QUIET; T_ASSERT_NE(msg.header.msgh_bits & MACH_MSGH_BITS_COMPLEX, 0,
127 "message is complex");
128 T_QUIET; T_ASSERT_EQ(msg.body.msgh_descriptor_count, 1, "message has one right");
129 T_QUIET; T_ASSERT_EQ((mach_msg_type_name_t)msg.port_descriptor.disposition, expected_disp,
130 "port has right disposition");
131 return msg.port_descriptor.name;
132 }
133
134 T_HELPER_DECL(right_dedup_server, "right_dedup_server")
135 {
136 mach_port_t svc_port = server_checkin();
137 mach_port_t ports[3];
138
139 ports[0] = receive_port(1, svc_port, MACH_MSG_TYPE_MOVE_RECEIVE);
140 ports[1] = receive_port(2, svc_port, MACH_MSG_TYPE_MOVE_SEND);
141 ports[2] = receive_port(3, svc_port, MACH_MSG_TYPE_MOVE_SEND);
142 T_ASSERT_EQ(ports[0], ports[1], "receive, send, send");
143 T_ASSERT_EQ(ports[0], ports[2], "receive, send, send");
144 destroy_port(ports[0], true, 2);
145
146 ports[0] = receive_port(4, svc_port, MACH_MSG_TYPE_MOVE_SEND);
147 ports[1] = receive_port(5, svc_port, MACH_MSG_TYPE_MOVE_RECEIVE);
148 ports[2] = receive_port(6, svc_port, MACH_MSG_TYPE_MOVE_SEND);
149 T_ASSERT_EQ(ports[0], ports[1], "send, receive, send");
150 T_ASSERT_EQ(ports[0], ports[2], "send, receive, send");
151 destroy_port(ports[0], true, 2);
152
153 ports[0] = receive_port(7, svc_port, MACH_MSG_TYPE_MOVE_SEND);
154 ports[1] = receive_port(8, svc_port, MACH_MSG_TYPE_MOVE_SEND);
155 ports[2] = receive_port(9, svc_port, MACH_MSG_TYPE_MOVE_RECEIVE);
156 T_ASSERT_EQ(ports[0], ports[1], "send, send, receive");
157 T_ASSERT_EQ(ports[0], ports[2], "send, send, receive");
158 destroy_port(ports[0], true, 2);
159
160 T_END;
161 }
162
163 T_HELPER_DECL(right_dedup_client, "right_dedup_client")
164 {
165 mach_port_t svc_port = server_lookup();
166 mach_port_t port;
167
168 port = make_sr_port();
169 send_port(1, svc_port, port, MACH_MSG_TYPE_MOVE_RECEIVE);
170 send_port(2, svc_port, port, MACH_MSG_TYPE_COPY_SEND);
171 send_port(3, svc_port, port, MACH_MSG_TYPE_MOVE_SEND);
172
173 port = make_sr_port();
174 send_port(4, svc_port, port, MACH_MSG_TYPE_COPY_SEND);
175 send_port(5, svc_port, port, MACH_MSG_TYPE_MOVE_RECEIVE);
176 send_port(6, svc_port, port, MACH_MSG_TYPE_MOVE_SEND);
177
178 port = make_sr_port();
179 send_port(7, svc_port, port, MACH_MSG_TYPE_COPY_SEND);
180 send_port(8, svc_port, port, MACH_MSG_TYPE_MOVE_SEND);
181 send_port(9, svc_port, port, MACH_MSG_TYPE_MOVE_RECEIVE);
182 }
183
184 T_DECL(right_dedup, "make sure right deduplication works")
185 {
186 dt_helper_t helpers[] = {
187 dt_launchd_helper_domain("com.apple.xnu.test.mach_port.plist",
188 "right_dedup_server", NULL, LAUNCH_SYSTEM_DOMAIN),
189 dt_fork_helper("right_dedup_client"),
190 };
191 dt_run_helpers(helpers, 2, 600);
192 }
193