xref: /xnu-10002.81.5/tests/reply_port_defense_client.c (revision 5e3eaea39dcf651e66cb99ba7d70e32cc4a99587)
1 #include <mach/kern_return.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <pthread.h>
5 #include <unistd.h>
6 #include <mach/mach.h>
7 #include <mach/task.h>
8 #include <assert.h>
9 
10 #define MAX_TEST_NUM 6
11 
12 #if __arm64__
13 #define EXCEPTION_THREAD_STATE          ARM_THREAD_STATE64
14 #define EXCEPTION_THREAD_STATE_COUNT    ARM_THREAD_STATE64_COUNT
15 #elif __x86_64__
16 #define EXCEPTION_THREAD_STATE          x86_THREAD_STATE
17 #define EXCEPTION_THREAD_STATE_COUNT    x86_THREAD_STATE_COUNT
18 #else
19 #error Unsupported architecture
20 #endif
21 
22 static mach_port_t
alloc_server_port(void)23 alloc_server_port(void)
24 {
25 	mach_port_t server_port = MACH_PORT_NULL;
26 	kern_return_t kr;
27 
28 	kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &server_port);
29 	assert(kr == 0);
30 
31 	kr = mach_port_insert_right(mach_task_self(), server_port, server_port, MACH_MSG_TYPE_MAKE_SEND);
32 	assert(kr == 0);
33 
34 	return server_port;
35 }
36 
37 static mach_port_t
alloc_provisional_reply_port()38 alloc_provisional_reply_port()
39 {
40 	kern_return_t kr;
41 	mach_port_t reply_port = MACH_PORT_NULL;
42 	mach_port_t task = mach_task_self();
43 
44 	mach_port_options_t opts = {
45 		.flags = MPO_PROVISIONAL_REPLY_PORT | MPO_INSERT_SEND_RIGHT,
46 	};
47 
48 	kr = mach_port_construct(mach_task_self(), &opts, 0, &reply_port);
49 	assert(kr == 0);
50 
51 	return reply_port;
52 }
53 
54 static mach_port_t
alloc_reply_port()55 alloc_reply_port()
56 {
57 	kern_return_t kr;
58 	mach_port_t reply_port = MACH_PORT_NULL;
59 	mach_port_t task = mach_task_self();
60 
61 	mach_port_options_t opts = {
62 		.flags = MPO_REPLY_PORT | MPO_INSERT_SEND_RIGHT,
63 	};
64 
65 	kr = mach_port_construct(mach_task_self(), &opts, 0, &reply_port);
66 	assert(kr == 0);
67 
68 	return reply_port;
69 }
70 
71 /* The rcv right of the port would be marked immovable. */
72 static void
test_immovable_receive_right(void)73 test_immovable_receive_right(void)
74 {
75 	kern_return_t kr;
76 	mach_port_t server_port = MACH_PORT_NULL, reply_port = MACH_PORT_NULL;
77 	struct {
78 		mach_msg_header_t header;
79 		mach_msg_body_t body;
80 		mach_msg_port_descriptor_t desc;
81 	} msg;
82 
83 	server_port = alloc_server_port();
84 	reply_port = alloc_reply_port();
85 
86 	msg.header.msgh_remote_port = server_port;
87 	msg.header.msgh_local_port = MACH_PORT_NULL;
88 	msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0) | MACH_MSGH_BITS_COMPLEX;
89 	msg.header.msgh_size = sizeof msg;
90 
91 	msg.body.msgh_descriptor_count = 1;
92 
93 	msg.desc.name = reply_port;
94 	msg.desc.disposition = MACH_MSG_TYPE_MOVE_RECEIVE;
95 	msg.desc.type = MACH_MSG_PORT_DESCRIPTOR;
96 
97 	kr = mach_msg_send(&msg.header);
98 
99 	printf("[reply_port_defense_client test_immovable_receive_right]: mach_msg2() returned %d\n", kr);
100 }
101 
102 /* The only way you could create a send once right is when you send the port in local port of a mach msg with MAKE_SEND_ONCE disposition. */
103 static void
test_make_send_once_right(void)104 test_make_send_once_right(void)
105 {
106 	kern_return_t kr;
107 	mach_port_t reply_port = alloc_reply_port();
108 	kr = mach_port_insert_right(mach_task_self(), reply_port, reply_port, MACH_MSG_TYPE_MAKE_SEND_ONCE);
109 	printf("[reply_port_defense_client test_make_send_once_right]: mach_port_insert_right() returned %d\n", kr);
110 }
111 
112 /* The send right of the port would only used for guarding a name in ipc space, it would not allow to send a message. */
113 static void
test_using_send_right(void)114 test_using_send_right(void)
115 {
116 	kern_return_t kr;
117 	mach_port_t reply_port = alloc_reply_port();
118 	struct {
119 		mach_msg_header_t header;
120 		mach_msg_body_t body;
121 	} msg;
122 
123 	msg.header.msgh_remote_port = reply_port;
124 	msg.header.msgh_local_port = MACH_PORT_NULL;
125 	msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
126 	msg.header.msgh_size = sizeof msg;
127 
128 	kr = mach_msg_send(&msg.header);
129 	printf("[reply_port_defense_client test_using_send_right]: mach_msg2() returned %d\n", kr);
130 }
131 
132 /* The send right of the port would only used for guarding a name in ipc space, it would not allowed to get moved. */
133 static void
test_move_send_right(void)134 test_move_send_right(void)
135 {
136 	kern_return_t kr;
137 	mach_port_t server_port = MACH_PORT_NULL, reply_port = MACH_PORT_NULL;
138 	struct {
139 		mach_msg_header_t header;
140 		mach_msg_body_t body;
141 		mach_msg_port_descriptor_t desc;
142 	} msg;
143 
144 	server_port = alloc_server_port();
145 	reply_port = alloc_reply_port();
146 
147 	msg.header.msgh_remote_port = server_port;
148 	msg.header.msgh_local_port = MACH_PORT_NULL;
149 	msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0) | MACH_MSGH_BITS_COMPLEX;
150 	msg.header.msgh_size = sizeof msg;
151 
152 	msg.body.msgh_descriptor_count = 1;
153 
154 	msg.desc.name = reply_port;
155 	msg.desc.disposition = MACH_MSG_TYPE_MOVE_SEND;
156 	msg.desc.type = MACH_MSG_PORT_DESCRIPTOR;
157 
158 	kr = mach_msg_send(&msg.header);
159 	printf("[reply_port_defense_client test_move_send_right]: mach_msg2() returned %d\n", kr);
160 }
161 
162 static void
test_move_provisional_reply_port(void)163 test_move_provisional_reply_port(void)
164 {
165 	kern_return_t kr;
166 	mach_port_t server_port = MACH_PORT_NULL, reply_port = MACH_PORT_NULL;
167 	struct {
168 		mach_msg_header_t header;
169 		mach_msg_body_t body;
170 		mach_msg_port_descriptor_t desc;
171 	} msg;
172 
173 	server_port = alloc_server_port();
174 	reply_port = alloc_provisional_reply_port();
175 
176 	msg.header.msgh_remote_port = server_port;
177 	msg.header.msgh_local_port = MACH_PORT_NULL;
178 	msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0) | MACH_MSGH_BITS_COMPLEX;
179 	msg.header.msgh_size = sizeof msg;
180 
181 	msg.body.msgh_descriptor_count = 1;
182 
183 	msg.desc.name = reply_port;
184 	msg.desc.disposition = MACH_MSG_TYPE_MOVE_RECEIVE;
185 	msg.desc.type = MACH_MSG_PORT_DESCRIPTOR;
186 
187 	kr = mach_msg_send(&msg.header);
188 
189 	printf("[reply_port_defense_client test_immovable_receive_right]: mach_msg2() returned %d\n", kr);
190 }
191 
192 static void
test_unentitled_thread_set_exception_ports(void)193 test_unentitled_thread_set_exception_ports(void)
194 {
195 	mach_port_t exc_port = alloc_server_port();
196 
197 	kern_return_t kr = thread_set_exception_ports(
198 		mach_thread_self(),
199 		EXC_MASK_ALL,
200 		exc_port,
201 		(exception_behavior_t)((unsigned int)EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES),
202 		EXCEPTION_THREAD_STATE);
203 #if defined(TARGET_OS_OSX)
204 	/* This is not yet enforced on macos */
205 	assert(kr == KERN_SUCCESS);
206 #else
207 	assert(kr == KERN_DENIED);
208 	exit(1); /* should not reach here */
209 #endif
210 }
211 
212 int
main(int argc,char * argv[])213 main(int argc, char *argv[])
214 {
215 	printf("[reply_port_defense_client]: My Pid: %d\n", getpid());
216 
217 	void (*tests[MAX_TEST_NUM])(void) = {
218 		test_immovable_receive_right,
219 		test_make_send_once_right,
220 		test_using_send_right,
221 		test_move_send_right,
222 		test_move_provisional_reply_port,
223 		test_unentitled_thread_set_exception_ports
224 	};
225 
226 	if (argc < 2) {
227 		printf("[reply_port_defense_client]: Specify a test to run.");
228 		exit(-1);
229 	}
230 
231 	int test_num = atoi(argv[1]);
232 	if (test_num >= 0 && test_num < MAX_TEST_NUM) {
233 		(*tests[test_num])();
234 	} else {
235 		printf("[reply_port_defense_client]: Invalid test num. Exiting...\n");
236 		exit(-1);
237 	}
238 	exit(0);
239 }
240