xref: /illumos-gate/usr/src/test/os-tests/tests/sockfs/rights.c (revision 480497bc2ff96b447dc09403a6c187a1593ac1ec)
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