1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
14 * Copyright 2020 Joyent, Inc.
15 */
16
17 /*
18 * Test file descriptor passing via SCM_RIGHTS, and in particular what happens
19 * on message truncation in terms of the represented size of the data in the
20 * control message. Ensure that no file descriptors are leaked - the kernel
21 * must close any that would not fit in the available buffer space and the
22 * userland application must close the rest.
23 */
24
25 #include <stdio.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <signal.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <strings.h>
32 #include <unistd.h>
33 #include <libproc.h>
34
35 #include <sys/types.h>
36 #include <sys/param.h>
37 #include <sys/socket.h>
38 #include <sys/stat.h>
39 #include <sys/un.h>
40 #include <sys/wait.h>
41 #include <assert.h>
42 #include <alloca.h>
43 #include <err.h>
44
45 static boolean_t debug;
46
47 typedef struct cmsg_test {
48 char *name; /* Name of the test */
49 uint_t send; /* Number of FDs to send */
50 uint_t recv; /* Size receive buffer for this number of FDs */
51 size_t predata; /* Prepend dummy cmsg of this size */
52 int bufsize; /* Explicitly set receive buffer size. */
53 /* Overrides 'recv' if non-zero */
54 uint_t x_controllen; /* Expected received msg_controllen */
55 uint_t x_cmsg_datalen; /* Expected received cmsg data length */
56 uint32_t x_flags; /* Expected received msf_flags */
57 } cmsg_test_t;
58
59 static cmsg_test_t tests[] = {
60 {
61 .name = "send 1, recv 1",
62 .send = 1,
63 .recv = 1,
64 .predata = 0,
65 .bufsize = 0,
66 .x_controllen = 16,
67 .x_cmsg_datalen = 4,
68 .x_flags = 0,
69 },
70 {
71 .name = "send 10, recv 10",
72 .send = 10,
73 .recv = 10,
74 .predata = 0,
75 .bufsize = 0,
76 .x_controllen = 52,
77 .x_cmsg_datalen = 40,
78 .x_flags = 0,
79 },
80 {
81 .name = "send 2, recv 1",
82 .send = 2,
83 .recv = 1,
84 .predata = 0,
85 .bufsize = 0,
86 .x_controllen = 16,
87 .x_cmsg_datalen = 4,
88 .x_flags = MSG_CTRUNC,
89 },
90 {
91 .name = "send 2, recv 1, buffer 5",
92 .send = 2,
93 .recv = 1,
94 .predata = 0,
95 .bufsize = sizeof (int) * 2 - 3,
96 .x_controllen = 17,
97 .x_cmsg_datalen = 5,
98 .x_flags = MSG_CTRUNC,
99 },
100 {
101 .name = "send 2, recv 1, buffer 6",
102 .send = 2,
103 .recv = 1,
104 .predata = 0,
105 .bufsize = sizeof (int) * 2 - 2,
106 .x_controllen = 18,
107 .x_cmsg_datalen = 6,
108 .x_flags = MSG_CTRUNC,
109 },
110 {
111 .name = "send 2, recv 1, buffer 7",
112 .send = 2,
113 .recv = 1,
114 .predata = 0,
115 .bufsize = sizeof (int) * 2 - 1,
116 .x_controllen = 19,
117 .x_cmsg_datalen = 7,
118 .x_flags = MSG_CTRUNC,
119 },
120
121 /* Tests where there is no room allowed for data */
122
123 {
124 .name = "send 2, recv 0, hdronly",
125 .send = 2,
126 .recv = 0,
127 .predata = 0,
128 .bufsize = 0,
129 .x_controllen = 12,
130 .x_cmsg_datalen = 0,
131 .x_flags = MSG_CTRUNC,
132 },
133
134 {
135 .name = "send 2, recv 0, hdr - 1",
136 .send = 2,
137 .recv = 0,
138 .predata = 0,
139 .bufsize = -1,
140 .x_controllen = 11,
141 .x_cmsg_datalen = 0,
142 .x_flags = MSG_CTRUNC,
143 },
144
145 {
146 .name = "send 2, recv 0, hdr - 5",
147 .send = 2,
148 .recv = 0,
149 .predata = 0,
150 .bufsize = -5,
151 .x_controllen = 7,
152 .x_cmsg_datalen = 0,
153 .x_flags = MSG_CTRUNC,
154 },
155
156 /* Tests where SCM_RIGHTS is not the first message */
157
158 {
159 .name = "send 1, recv 1, pre 8",
160 .send = 1,
161 .recv = 1,
162 .predata = 8,
163 .bufsize = 0,
164 .x_controllen = 36,
165 .x_cmsg_datalen = 4,
166 .x_flags = 0,
167 },
168 {
169 .name = "send 1, recv 1, pre 7",
170 .send = 1,
171 .recv = 1,
172 .predata = 7,
173 .bufsize = 0,
174 .x_controllen = 35,
175 .x_cmsg_datalen = 4,
176 .x_flags = 0,
177 },
178 {
179 .name = "send 1, recv 1, pre 6",
180 .send = 1,
181 .recv = 1,
182 .predata = 6,
183 .bufsize = 0,
184 .x_controllen = 34,
185 .x_cmsg_datalen = 4,
186 .x_flags = 0,
187 },
188 {
189 .name = "send 1, recv 1, pre 5",
190 .send = 1,
191 .recv = 1,
192 .predata = 5,
193 .bufsize = 0,
194 .x_controllen = 33,
195 .x_cmsg_datalen = 4,
196 .x_flags = 0,
197 },
198
199 {
200 .name = "send 2, recv 1, pre 8",
201 .send = 2,
202 .recv = 1,
203 .predata = 8,
204 .bufsize = 0,
205 .x_controllen = 36,
206 .x_cmsg_datalen = 8,
207 .x_flags = MSG_CTRUNC,
208 },
209 {
210 .name = "send 2, recv 1, pre 7",
211 .send = 2,
212 .recv = 1,
213 .predata = 7,
214 .bufsize = 0,
215 .x_controllen = 36,
216 .x_cmsg_datalen = 8,
217 .x_flags = MSG_CTRUNC,
218 },
219 {
220 .name = "send 2, recv 1, pre 6",
221 .send = 2,
222 .recv = 1,
223 .predata = 6,
224 .bufsize = 0,
225 .x_controllen = 36,
226 .x_cmsg_datalen = 8,
227 .x_flags = MSG_CTRUNC,
228 },
229 {
230 .name = "send 2, recv 1, pre 5",
231 .send = 2,
232 .recv = 1,
233 .predata = 5,
234 .bufsize = 0,
235 .x_controllen = 36,
236 .x_cmsg_datalen = 8,
237 .x_flags = MSG_CTRUNC,
238 },
239 {
240 .name = "send 2, recv 1, pre 4",
241 .send = 2,
242 .recv = 1,
243 .predata = 4,
244 .bufsize = 0,
245 .x_controllen = 32,
246 .x_cmsg_datalen = 8,
247 .x_flags = MSG_CTRUNC,
248 },
249 {
250 .name = "send 2, recv 1, pre 3",
251 .send = 2,
252 .recv = 1,
253 .predata = 3,
254 .bufsize = 0,
255 .x_controllen = 32,
256 .x_cmsg_datalen = 8,
257 .x_flags = MSG_CTRUNC,
258 },
259 {
260 .name = "send 2, recv 1, pre 2",
261 .send = 2,
262 .recv = 1,
263 .predata = 2,
264 .bufsize = 0,
265 .x_controllen = 32,
266 .x_cmsg_datalen = 8,
267 .x_flags = MSG_CTRUNC,
268 },
269 {
270 .name = "send 2, recv 1, pre 1",
271 .send = 2,
272 .recv = 1,
273 .predata = 1,
274 .bufsize = 0,
275 .x_controllen = 32,
276 .x_cmsg_datalen = 8,
277 .x_flags = MSG_CTRUNC,
278 },
279
280 {
281 .name = "send 2, recv 1, pre 8, buffer 5",
282 .send = 2,
283 .recv = 1,
284 .predata = 8,
285 .bufsize = sizeof (int) * 2 - 3,
286 .x_controllen = 37,
287 .x_cmsg_datalen = 8,
288 .x_flags = MSG_CTRUNC,
289 },
290 {
291 .name = "send 2, recv 1, pre 8, buffer 6",
292 .send = 2,
293 .recv = 1,
294 .predata = 8,
295 .bufsize = sizeof (int) * 2 - 2,
296 .x_controllen = 38,
297 .x_cmsg_datalen = 8,
298 .x_flags = MSG_CTRUNC,
299 },
300 {
301 .name = "send 2, recv 1, pre 8, buffer 7",
302 .send = 2,
303 .recv = 1,
304 .predata = 8,
305 .bufsize = sizeof (int) * 2 - 1,
306 .x_controllen = 39,
307 .x_cmsg_datalen = 8,
308 .x_flags = MSG_CTRUNC,
309 },
310 {
311 .name = "send 10, recv 1, pre 8",
312 .send = 10,
313 .recv = 1,
314 .predata = 8,
315 .bufsize = 0,
316 .x_controllen = 36,
317 .x_cmsg_datalen = 24,
318 .x_flags = MSG_CTRUNC,
319 },
320
321 /* End of tests */
322
323 {
324 .name = NULL
325 }
326 };
327
328 static int sock = -1, testfd = -1, cfd = -1;
329 static int fdcount;
330
331 static int
fdwalkcb(const prfdinfo_t * info,void * arg)332 fdwalkcb(const prfdinfo_t *info, void *arg)
333 {
334 if (!S_ISDIR(info->pr_mode) && info->pr_fd > 2 &&
335 info->pr_fd != sock && info->pr_fd != testfd &&
336 info->pr_fd != cfd) {
337 if (debug) {
338 fprintf(stderr, "%s: unexpected fd: %d\n",
339 (char *)arg, info->pr_fd);
340 }
341 fdcount++;
342 }
343
344 return (0);
345
346 }
347
348 static void
check_fds(char * tag)349 check_fds(char *tag)
350 {
351 fdcount = 0;
352 proc_fdwalk(getpid(), fdwalkcb, tag);
353 }
354
355 static void
send_and_wait(pid_t pid,sigset_t * set,int osig,int isig)356 send_and_wait(pid_t pid, sigset_t *set, int osig, int isig)
357 {
358 int sig;
359
360 if (osig > 0)
361 kill(pid, osig);
362
363 if (isig > 0) {
364 if (sigwait(set, &sig) != 0) {
365 err(EXIT_FAILURE,
366 "sigwait failed waiting for %d", isig);
367 }
368 if (sig == SIGINT) {
369 exit(1);
370 }
371 if (sig != isig) {
372 err(EXIT_FAILURE,
373 "sigwait returned unexpected signal %d", sig);
374 }
375 }
376 }
377
378 static void
sendtest(cmsg_test_t * t)379 sendtest(cmsg_test_t *t)
380 {
381 struct msghdr msg;
382 struct cmsghdr *cm;
383 struct iovec iov;
384 ssize_t nbytes;
385 char c = '*';
386 int i, *p;
387
388 bzero(&msg, sizeof (msg));
389
390 msg.msg_name = NULL;
391 msg.msg_namelen = 0;
392
393 iov.iov_base = &c;
394 iov.iov_len = sizeof (c);
395 msg.msg_iov = &iov;
396 msg.msg_iovlen = 1;
397
398 msg.msg_flags = 0;
399
400 msg.msg_controllen = CMSG_SPACE(sizeof (int) * t->send);
401
402 if (t->predata > 0) {
403 /* A dummy cmsg will be inserted at the head of the data */
404 msg.msg_controllen += CMSG_SPACE(t->predata);
405 }
406
407 msg.msg_control = alloca(msg.msg_controllen);
408 bzero(msg.msg_control, msg.msg_controllen);
409
410 cm = CMSG_FIRSTHDR(&msg);
411
412 if (t->predata > 0) {
413 /* Insert the dummy cmsg */
414 cm->cmsg_len = CMSG_LEN(t->predata);
415 cm->cmsg_level = SOL_SOCKET;
416 cm->cmsg_type = 0;
417 cm = CMSG_NXTHDR(&msg, cm);
418 }
419
420 cm->cmsg_len = CMSG_LEN(sizeof (int) * t->send);
421 cm->cmsg_level = SOL_SOCKET;
422 cm->cmsg_type = SCM_RIGHTS;
423
424 p = (int *)CMSG_DATA(cm);
425 for (i = 0; i < t->send; i++) {
426 int s = dup(testfd);
427 if (s == -1)
428 err(EXIT_FAILURE, "dup()");
429 *p++ = s;
430 }
431
432 if (debug)
433 printf("Sending: controllen=%u\n", msg.msg_controllen);
434
435 nbytes = sendmsg(cfd, &msg, 0);
436 if (nbytes == -1)
437 err(EXIT_FAILURE, "sendmsg()");
438
439 p = (int *)CMSG_DATA(cm);
440 for (i = 0; i < t->send; i++)
441 (void) close(*p++);
442 }
443
444 static int
server(const char * sockpath,pid_t pid)445 server(const char *sockpath, pid_t pid)
446 {
447 struct sockaddr_un addr;
448 sigset_t set;
449 cmsg_test_t *t;
450
451 sigemptyset(&set);
452 sigaddset(&set, SIGUSR2);
453 sigaddset(&set, SIGINT);
454
455 sock = socket(PF_LOCAL, SOCK_STREAM, 0);
456 if (sock == -1)
457 err(EXIT_FAILURE, "failed to create socket");
458 addr.sun_family = AF_UNIX;
459 strlcpy(addr.sun_path, sockpath, sizeof (addr.sun_path));
460 if (bind(sock, (struct sockaddr *)&addr, sizeof (addr)) == -1)
461 err(EXIT_FAILURE, "bind failed");
462 if (listen(sock, 0) == -1)
463 err(EXIT_FAILURE, "listen failed");
464
465 if ((testfd = open("/dev/null", O_RDONLY)) == -1)
466 err(EXIT_FAILURE, "/dev/null");
467
468 check_fds("server");
469
470 /* Signal the child to connect to the socket */
471 send_and_wait(pid, &set, SIGUSR1, SIGUSR2);
472
473 if ((cfd = accept(sock, NULL, 0)) == -1)
474 err(EXIT_FAILURE, "accept failed");
475
476 for (t = tests; t->name != NULL; t++) {
477 if (debug)
478 printf("\n>>> Starting test %s\n", t->name);
479
480 sendtest(t);
481 check_fds("server");
482
483 send_and_wait(pid, &set, SIGUSR1, SIGUSR2);
484 }
485
486 close(cfd);
487 close(testfd);
488 close(sock);
489
490 return (0);
491 }
492
493 static boolean_t pass;
494
495 static void
check(uint_t actual,uint_t expected,char * tag)496 check(uint_t actual, uint_t expected, char *tag)
497 {
498 if (actual != expected) {
499 fprintf(stderr, " !!!: "
500 "%1$s = %2$u(%2$#x) (expected %3$u(%3$#x))\n",
501 tag, actual, expected);
502 pass = _B_FALSE;
503 } else if (debug) {
504 fprintf(stderr, " : "
505 "%1$s = %2$u(%2$#x)\n",
506 tag, actual);
507 }
508 }
509
510 static boolean_t
recvtest(cmsg_test_t * t)511 recvtest(cmsg_test_t *t)
512 {
513 struct msghdr msg;
514 struct cmsghdr *cm;
515 struct iovec iov;
516 size_t bufsize;
517 ssize_t nbytes;
518 char c = '*';
519
520 bzero(&msg, sizeof (msg));
521
522 msg.msg_name = NULL;
523 msg.msg_namelen = 0;
524
525 iov.iov_base = &c;
526 iov.iov_len = sizeof (c);
527 msg.msg_iov = &iov;
528 msg.msg_iovlen = 1;
529
530 msg.msg_flags = 0;
531
532 /*
533 * If the test does not specify a receive buffer size, calculate one
534 * from the number of file descriptors to receive.
535 */
536 if (t->bufsize == 0) {
537 bufsize = sizeof (int) * t->recv;
538 bufsize = CMSG_SPACE(bufsize);
539 } else {
540 /*
541 * Use the specific buffer size provided but add in
542 * space for the header
543 */
544 bufsize = t->bufsize + CMSG_LEN(0);
545 }
546
547 if (t->predata > 0) {
548 /* A dummy cmsg will be found at the head of the data */
549 bufsize += CMSG_SPACE(t->predata);
550 }
551
552 msg.msg_controllen = bufsize;
553 msg.msg_control = alloca(bufsize);
554 bzero(msg.msg_control, msg.msg_controllen);
555
556 pass = _B_TRUE;
557
558 if (debug)
559 printf("Receiving: controllen=%u, \n", msg.msg_controllen);
560
561 nbytes = recvmsg(sock, &msg, 0);
562
563 if (nbytes == -1) {
564 pass = _B_FALSE;
565 fprintf(stderr, "recvmsg() failed: %s\n", strerror(errno));
566 goto out;
567 }
568
569 if (debug) {
570 printf("Received: controllen=%u, flags=%#x\n",
571 msg.msg_controllen, msg.msg_flags);
572 }
573
574 check(msg.msg_flags, t->x_flags, "msg_flags");
575 check(msg.msg_controllen, t->x_controllen, "msg_controllen");
576
577 for (cm = CMSG_FIRSTHDR(&msg); cm; cm = CMSG_NXTHDR(&msg, cm)) {
578 void *data, *end;
579
580 if (debug) {
581 printf(" >> : Got cmsg %x/%x - %u\n", cm->cmsg_level,
582 cm->cmsg_type, cm->cmsg_len);
583 }
584
585 if (cm->cmsg_type != SCM_RIGHTS) {
586 if (debug)
587 printf(" : skipping cmsg\n");
588 continue;
589 }
590
591 check(cm->cmsg_len - CMSG_LEN(0),
592 t->x_cmsg_datalen, "cmsg_len");
593
594 /* Close any received file descriptors */
595 data = CMSG_DATA(cm);
596
597 if ((msg.msg_flags & MSG_CTRUNC) &&
598 CMSG_NXTHDR(&msg, cm) == NULL) {
599 /*
600 * illumos did not previously adjust cmsg_len on
601 * truncation. This is the last cmsg, derive the
602 * length from msg_controllen
603 */
604 end = msg.msg_control + msg.msg_controllen;
605 } else {
606 end = data + cm->cmsg_len - CMSG_LEN(0);
607 }
608
609 while (data <= end - sizeof (int)) {
610 int *a = (int *)data;
611 if (debug)
612 printf(" : close(%d)\n", *a);
613 if (close(*a) == -1) {
614 pass = _B_FALSE;
615 fprintf(stderr, " !!!: "
616 "failed to close fd %d - %s\n", *a,
617 strerror(errno));
618 }
619 data += sizeof (int);
620 }
621 }
622
623 out:
624
625 check_fds("client");
626 check(fdcount, 0, "client descriptors");
627 printf(" + : %s %s\n", pass ? "PASS" : "FAIL", t->name);
628
629 return (pass);
630 }
631
632 static int
client(const char * sockpath,pid_t pid)633 client(const char *sockpath, pid_t pid)
634 {
635 struct sockaddr_un addr;
636 sigset_t set;
637 cmsg_test_t *t;
638 int ret = 0;
639
640 sigemptyset(&set);
641 sigaddset(&set, SIGUSR1);
642 sigaddset(&set, SIGINT);
643
644 send_and_wait(pid, &set, 0, SIGUSR1);
645
646 sock = socket(PF_LOCAL, SOCK_STREAM, 0);
647 if (sock == -1)
648 err(EXIT_FAILURE, "failed to create socket");
649 addr.sun_family = AF_UNIX;
650 strlcpy(addr.sun_path, sockpath, sizeof (addr.sun_path));
651 if (connect(sock, (struct sockaddr *)&addr, sizeof (addr)) == -1)
652 err(EXIT_FAILURE, "could not connect to server socket");
653
654 for (t = tests; t->name != NULL; t++) {
655 send_and_wait(pid, &set, SIGUSR2, SIGUSR1);
656 if (!recvtest(t))
657 ret = 1;
658 }
659
660 close(sock);
661
662 return (ret);
663 }
664
665 int
main(int argc,const char ** argv)666 main(int argc, const char **argv)
667 {
668 char sockpath[] = "/tmp/cmsg.testsock.XXXXXX";
669 pid_t pid, ppid;
670 sigset_t set;
671 int ret = 0;
672
673 /*
674 * The tests make assumptions about the number of open file descriptors
675 * present. In case we are invoked with more than just STDIN_FILENO,
676 * STDOUT_FILENO, and STDERR_FILENO open, close any other open
677 * descriptors that might exist. Otherwise their presence will violate
678 * the assumptions of the test and cause an erroneous failure.
679 */
680 closefrom(STDERR_FILENO + 1);
681
682 if (argc > 1 && strcmp(argv[1], "-d") == 0)
683 debug = _B_TRUE;
684
685 sigfillset(&set);
686 sigdelset(&set, SIGINT);
687 sigdelset(&set, SIGTSTP);
688 sigprocmask(SIG_BLOCK, &set, NULL);
689
690 if (mktemp(sockpath) == NULL)
691 err(EXIT_FAILURE, "Failed to make temporary socket path");
692
693 ppid = getpid();
694 pid = fork();
695 switch (pid) {
696 case -1:
697 err(EXIT_FAILURE, "fork failed");
698 case 0:
699 return (server(sockpath, ppid));
700 default:
701 break;
702 }
703
704 ret = client(sockpath, pid);
705 kill(pid, SIGINT);
706
707 unlink(sockpath);
708
709 return (ret);
710 }
711