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 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 349 check_fds(char *tag) 350 { 351 fdcount = 0; 352 proc_fdwalk(getpid(), fdwalkcb, tag); 353 } 354 355 static void 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 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 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 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 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 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 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