]> git.saurik.com Git - apple/xnu.git/blob - tools/tests/unit_tests/guarded_fd_tests_11746236_src/guarded_test.c
ae587fdffcad6fca33cf13a3fa600f5a549ec846
[apple/xnu.git] / tools / tests / unit_tests / guarded_fd_tests_11746236_src / guarded_test.c
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <errno.h>
6 #include <sys/fcntl.h>
7 #include <sys/guarded.h>
8 #include <mach/mach.h>
9 #include <pthread.h>
10 #include <sys/types.h>
11 #include <sys/socket.h>
12 #include <sys/un.h>
13 #include <netinet/in.h>
14 #include "guarded_test_common.h"
15
16 #include <sys/syscall.h>
17
18 #if !defined(SYS_guarded_kqueue_np)
19 #define guarded_kqueue_np(gp, gf) syscall(443, gp, gf)
20 #endif
21
22 #if !defined(SYS_change_fdguard_np)
23 #define change_fdguard_np(fd, gp, gf, ngp, nfg, flp) \
24 syscall(444, fd, gp, gf, ngp, nfg, flp)
25 #endif
26
27 #define SERVER_NAME "/tmp/fdserver"
28
29 typedef union {
30 struct cmsghdrcmsghdr;
31 u_char msg_control[0];
32 } cmsghdr_msg_control_t;
33
34 /* Test case for closing a guarded fd */
35 void close_guarded_fd(int);
36 /* Test case for duping a guarded fd */
37 void dup_guarded_fd(int);
38 /* Test case for removing flag from guarded fd */
39 void remove_flag_guarded_fd(int);
40 /* Test case for closing guarded fd with bad guard */
41 void badguard_close_guarded_fd(int, guardid_t);
42 /* Test case for guarded closing an unguarded fd */
43 void guard_close_unguarded_fd(guardid_t);
44 /* Test case for guarded closing a guarded fd correctly */
45 void guard_close_guarded_fd(int, guardid_t);
46 /* Test case for creating a file port from a guarded fd */
47 void fileport_makeport_guarded_fd(int);
48 /* Test case for sending guarded fd over socket */
49 void sendmsg_guarded_fd(int);
50 /* Test case for removing the guard from a guarded fd */
51 void remove_guard(int, guardid_t, u_int, int);
52 /* Test case for adding a guard to a tcp socket */
53 void add_guard_to_socket(guardid_t);
54 /* Test case for a guarded kqueue */
55 void create_and_close_guarded_kqueue(guardid_t);
56
57 /* Helper routines */
58 void *client_recv_fd(void *);
59 int receive_fd_using_sockfd(int *, int);
60 int send_fd_using_sockfd(int, int);
61 int setup_server(const char *);
62
63 const guardid_t guard = 0x123456789abcdefull;
64 char *pname;
65
66 static void usage(void)
67 {
68 printf("usage: %s [test number]\n", pname);
69 printf("test 0: Test case for closing a guarded fd\n");
70 printf("test 1: Test case for duping a guarded fd\n");
71 printf("test 2: Test case for removing FD_CLOEXEC flag from a guarded fd\n");
72 printf("test 3: Test case for closing a guarded fd with a bad guard\n");
73 printf("test 4: Test case for closing an unguarded fd using a guarded close\n");
74 printf("test 5: Test case for closing a guarded fd with the correct guard\n");
75 printf("test 6: Test case for creating a file port from a guarded fd\n");
76 printf("test 7: Test case for sending a guarded fd over a socket\n");
77 printf("test 8: Test case for removing the guard from a guarded fd\n");
78 printf("test 9: Test case for adding a guard to a tcp socket\n");
79 printf("test 10: Test case for a guarded kqueue\n");
80 }
81
82 int main(int argc, char *argv[])
83 {
84 int option, fd;
85
86 pname = argv[0];
87 if (argc != 2) {
88 usage();
89 exit(1);
90 }
91 printf("Test Program invoked with option [%s]\n", argv[1]);
92 option = atoi(argv[1]);
93
94 close(TEST_FD);
95 fd = guarded_open_np(
96 "/tmp/try.txt",
97 &guard,
98 GUARD_CLOSE | GUARD_DUP | GUARD_SOCKET_IPC | GUARD_FILEPORT,
99 O_CREAT | O_CLOEXEC | O_RDWR,
100 0666);
101
102 if (-1 == fd) {
103 perror("guarded_open_np");
104 exit(1);
105 }
106
107 switch(option) {
108
109 case 0:
110 close_guarded_fd(fd);
111 break;
112 case 1:
113 dup_guarded_fd(fd);
114 break;
115 case 2:
116 remove_flag_guarded_fd(fd);
117 break;
118 case 3:
119 badguard_close_guarded_fd(fd, guard);
120 break;
121 case 4:
122 guard_close_unguarded_fd(guard);
123 break;
124 case 5:
125 guard_close_guarded_fd(fd, guard);
126 break;
127 case 6:
128 fileport_makeport_guarded_fd(fd);
129 break;
130 case 7:
131 sendmsg_guarded_fd(fd);
132 break;
133 case 8:
134 remove_guard(fd, guard, GUARD_CLOSE | GUARD_DUP |
135 GUARD_SOCKET_IPC | GUARD_FILEPORT, FD_CLOEXEC);
136 break;
137 case 9:
138 add_guard_to_socket(guard);
139 break;
140 case 10:
141 create_and_close_guarded_kqueue(guard);
142 break;
143 default:
144 usage();
145 exit(1);
146 }
147
148 return 0;
149 }
150
151 void close_guarded_fd(int fd)
152 {
153 int ret_val;
154 printf("Performing close on a guarded fd...\n");
155
156 /* Brute force way of ensuring that the child process
157 * uses the TEST_FD which is checked by the parent
158 */
159 while(fd != TEST_FD && fd <= TEST_FD) {
160 fd = guarded_open_np(
161 "/tmp/try.txt",
162 &guard,
163 GUARD_CLOSE | GUARD_DUP | GUARD_SOCKET_IPC | GUARD_FILEPORT,
164 O_CREAT | O_CLOEXEC | O_RDWR,
165 0666);
166
167 if (-1 == fd) {
168 perror("guarded_open_np");
169 exit(1);
170 }
171 }
172
173 ret_val = close(TEST_FD);
174 fprintf(stderr, "close() returned (%d) on a guarded fd?!\n", ret_val);
175 exit(1);
176 }
177
178 void dup_guarded_fd(int fd)
179 {
180 int ret_val;
181 printf("Performing dup on a guarded fd...\n");
182 ret_val = dup(fd);
183 fprintf(stderr, "dup() returned (%d) on a guarded fd?!\n", ret_val);
184 exit(1);
185 }
186
187 void remove_flag_guarded_fd(int fd)
188 {
189 int ret_val, value;
190 printf("Removing FD_CLOEXEC from a guarded fd...\n");
191 value = fcntl(fd, F_GETFD);
192 if (-1 == value) {
193 fprintf(stderr, "fcntl:F_GETFD failed with %s!\n", strerror(errno));
194 exit(1);
195 }
196 ret_val = fcntl(fd, F_SETFD, value & ~FD_CLOEXEC);
197 fprintf(stderr, "fcntl:F_SETFD returned (%d) on a guarded fd?!\n", ret_val);
198 exit(1);
199 }
200
201 void badguard_close_guarded_fd(int fd, guardid_t guard)
202 {
203 int ret_val;
204 printf("Closing guarded fd with a bad guard...\n");
205 guardid_t badguard = guard << 1;
206 ret_val = guarded_close_np(fd, &badguard);
207 if (-1 == ret_val) {
208 switch (errno) {
209 case EPERM:
210 /* Expected */
211 perror("guarded_close_np");
212 exit(0);
213 default:
214 perror("guarded_close_np");
215 break;
216 }
217 }
218 fprintf(stderr,
219 "Close with bad guard returned (%d) on a guarded fd?!\n", ret_val);
220 exit(1);
221 }
222
223 void guard_close_unguarded_fd(guardid_t guard)
224 {
225 printf("Closing Unguarded fd with guarded_close_np...\n");
226 int newfd, ret_val;
227
228 if ((newfd = dup(fileno(stderr))) == -1) {
229 fprintf(stderr, "Failed to dup stderr!\n");
230 exit(1);
231 }
232
233 ret_val = guarded_close_np(newfd, &guard);
234 if (-1 == ret_val) {
235 /* Expected */
236 perror("guarded_close_np");
237 exit(0);
238 }
239 else {
240 fprintf(stderr, "Closing unguarded fd with guarded_fd succeeded with return value (%d)?!\n", ret_val);
241 exit(1);
242 }
243 }
244
245 void guard_close_guarded_fd(int fd, guardid_t guard)
246 {
247 printf("Closing a guarded fd with correct guard...\n");
248 if (-1 == guarded_close_np(fd, &guard)) {
249 fprintf(stderr, "Closing guarded fd with correct guard failed?!\n");
250 exit(1);
251 }
252 /* Expected */
253 exit(0);
254 }
255
256 void fileport_makeport_guarded_fd(int fd)
257 {
258 mach_port_name_t fdname = MACH_PORT_NULL;
259 int ret_val;
260 printf("Creating a file port from a guarded fd...\n");
261 ret_val = fileport_makeport(fd, &fdname);
262 fprintf(stderr, "Creating a file port from guarded fd returned (%d)?!\n", ret_val);
263 exit(1);
264 }
265
266 void sendmsg_guarded_fd(int fd)
267 {
268 int sockfd, err;
269 int csockfd;
270 socklen_t len;
271 struct sockaddr_un client_unix_addr;
272 pthread_t client_thread;
273 int ret_val;
274
275 /* Setup fd server */
276 if ((sockfd = setup_server(SERVER_NAME)) < 0) {
277 exit(1);
278 }
279
280 if(-1 == listen(sockfd, 5)) {
281 perror("listen");
282 exit(1);
283 }
284
285 /* Create client thread */
286 if ((err = pthread_create(&client_thread, NULL, client_recv_fd, 0)) != 0) {
287 fprintf(stderr, "pthread_create server_thread: %s\n", strerror(err));
288 exit(1);
289 }
290
291 pthread_detach(client_thread);
292
293 for (;;) {
294 len = sizeof (client_unix_addr);
295 csockfd = accept(sockfd,
296 (struct sockaddr *)&client_unix_addr, &len);
297 if (csockfd < 0) {
298 perror("accept");
299 exit(1);
300 }
301
302 printf("Sending guarded fd on a socket...\n");
303 ret_val = send_fd_using_sockfd(fd, csockfd);
304 if(ret_val < 0) {
305 /* Expected */
306 fprintf(stderr, "sendmsg failed with return value (%d)!\n", ret_val);
307 }
308 else {
309 fprintf(stderr, "Sending guarded fd on socket succeeded with return value (%d)?!\n", ret_val);
310 }
311 }
312
313 exit(0);
314 }
315
316 void
317 remove_guard(int fd, guardid_t guard, u_int guardflags, int fdflags)
318 {
319 printf("Remove the guard from a guarded fd, then dup(2) it ...\n");
320
321 int ret_val = change_fdguard_np(fd, &guard, guardflags, NULL, 0, &fdflags);
322
323 if (ret_val == -1) {
324 perror("change_fdguard_np");
325 exit(1);
326 }
327
328 printf("Dup-ing the unguarded fd ...\n");
329
330 /*
331 * Now that the GUARD_DUP has been removed, we should be able
332 * to dup the descriptor with no exception generation.
333 */
334 int newfd = dup(fd);
335
336 if (-1 == newfd) {
337 perror("dup");
338 exit(1);
339 }
340 exit(0);
341 }
342
343 void
344 add_guard_to_socket(guardid_t guard)
345 {
346 printf("Add a close guard to an unguarded socket fd, then close it ...\n");
347
348 int s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
349
350 if (-1 == s) {
351 perror("socket");
352 exit(1);
353 }
354
355 int ret_val = change_fdguard_np(s, NULL, 0, &guard, GUARD_CLOSE | GUARD_DUP, NULL);
356
357 if (-1 == ret_val) {
358 perror("change_fdguard_np");
359 exit(1);
360 }
361
362 /*
363 * Now we've added a GUARD_CLOSE successfully, let's try and do a close
364 */
365 if (-1 == close(s))
366 perror("close");
367 /*
368 * This is an error, because we should've received a fatal EXC_GUARD
369 */
370 exit(1);
371 }
372
373 void
374 create_and_close_guarded_kqueue(guardid_t guard)
375 {
376 printf("Create a guarded kqueue, then guarded_close_np() it ...\n");
377
378 int kq = guarded_kqueue_np(&guard, GUARD_CLOSE | GUARD_DUP);
379
380 int ret_val = guarded_close_np(kq, &guard);
381 if (-1 == ret_val) {
382 perror("guarded_close_np");
383 exit(1);
384 }
385
386 printf("Create a guarded kqueue, then close() it ...\n");
387
388 kq = guarded_kqueue_np(&guard, GUARD_CLOSE | GUARD_DUP);
389 if (-1 == close(kq))
390 perror("close");
391 /*
392 * This is always an error, because we should've received a fatal EXC_GUARD
393 */
394 exit(1);
395 }
396
397 /*
398 * Helper Routines
399 */
400
401 int setup_server(const char *name)
402 {
403 int sockfd, len;
404 struct sockaddr_un server_unix_addr;
405
406 if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
407 perror("socket");
408 return (sockfd);
409 }
410
411 (void) unlink(name);
412 bzero(&server_unix_addr, sizeof (server_unix_addr));
413 server_unix_addr.sun_family = AF_LOCAL;
414 (void) strcpy(server_unix_addr.sun_path, name);
415 len = strlen(name) + 1;
416 len += sizeof (server_unix_addr.sun_family);
417
418 if (bind(sockfd, (struct sockaddr *)&server_unix_addr, len) < 0) {
419 (void) close(sockfd);
420 return (-1);
421 }
422 return (sockfd);
423 }
424
425 int send_fd_using_sockfd(int fd, int sockfd)
426 {
427 ssize_t ret;
428 struct iovec iovec[1];
429 struct msghdr msg;
430 struct cmsghdr *cmsghdrp;
431 cmsghdr_msg_control_t *cmsghdr_msg_control;
432
433 cmsghdr_msg_control = malloc(CMSG_SPACE(sizeof (int)));
434
435 iovec[0].iov_base = "";
436 iovec[0].iov_len = 1;
437
438 msg.msg_name = 0;
439 msg.msg_namelen = 0;
440 msg.msg_iov = iovec;
441 msg.msg_iovlen = 1;
442 msg.msg_control = cmsghdr_msg_control->msg_control;
443 msg.msg_controllen = CMSG_SPACE(sizeof (int));
444 msg.msg_flags = 0;
445
446 cmsghdrp = CMSG_FIRSTHDR(&msg);
447 cmsghdrp->cmsg_len = CMSG_LEN(sizeof (int));
448 cmsghdrp->cmsg_level = SOL_SOCKET;
449 cmsghdrp->cmsg_type = SCM_RIGHTS;
450
451 *((int *)CMSG_DATA(cmsghdrp)) = fd;
452
453 if ((ret = sendmsg(sockfd, &msg, 0)) < 0) {
454 perror("sendmsg");
455 return ret;
456 }
457
458 return 0;
459 }
460
461 int receive_fd_using_sockfd(int *fd, int sockfd)
462 {
463 ssize_t ret;
464 u_char c;
465 int errcount = 0;
466 struct iovec iovec[1];
467 struct msghdr msg;
468 struct cmsghdr *cmsghdrp;
469 cmsghdr_msg_control_t *cmsghdr_msg_control;
470
471 cmsghdr_msg_control = malloc(CMSG_SPACE(sizeof (int)));
472
473 iovec[0].iov_base = &c;
474 iovec[0].iov_len = 1;
475
476 msg.msg_name = 0;
477 msg.msg_namelen = 0;
478 msg.msg_iov = iovec;
479 msg.msg_iovlen = 1;
480 msg.msg_control = cmsghdr_msg_control->msg_control;
481 msg.msg_controllen = CMSG_SPACE(sizeof (int));
482 msg.msg_flags = 0;
483
484 if ((ret = recvmsg(sockfd, &msg, 0)) < 0) {
485 perror("recvmsg");
486 return ret;
487 }
488
489 cmsghdrp = CMSG_FIRSTHDR(&msg);
490 if (cmsghdrp == NULL) {
491 *fd = -1;
492 return ret;
493 }
494
495 if (cmsghdrp->cmsg_len != CMSG_LEN(sizeof (int)))
496 errcount++;
497 if (cmsghdrp->cmsg_level != SOL_SOCKET)
498 errcount++;
499 if (cmsghdrp->cmsg_type != SCM_RIGHTS)
500 errcount++;
501 if (errcount) {
502 *fd = -1;
503 } else
504 *fd = *((int *)CMSG_DATA(cmsghdrp));
505 return ret;
506 }
507
508 void *client_recv_fd(void *arg)
509 {
510 char buf[512];
511 int fd = -1, sockfd, len, ret;
512 struct sockaddr_un server_unix_addr;
513
514 bzero(&server_unix_addr, sizeof (server_unix_addr));
515 strcpy(server_unix_addr.sun_path, SERVER_NAME);
516 server_unix_addr.sun_family = AF_LOCAL;
517 len = strlen(SERVER_NAME) + 1;
518 len += sizeof (server_unix_addr.sun_family);
519
520 if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
521 perror("socket");
522 exit(1);
523 }
524
525 if (connect(sockfd, (struct sockaddr *)&server_unix_addr, len) < 0) {
526 perror("connect");
527 exit(1);
528 }
529
530 ret = receive_fd_using_sockfd(&fd, sockfd);
531 return (NULL);
532 }