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