1152251Srwatson/*- 2152251Srwatson * Copyright (c) 2005 Robert N. M. Watson 3292814Sngie * Copyright (c) 2015 Mark Johnston 4152251Srwatson * All rights reserved. 5152251Srwatson * 6152251Srwatson * Redistribution and use in source and binary forms, with or without 7152251Srwatson * modification, are permitted provided that the following conditions 8152251Srwatson * are met: 9152251Srwatson * 1. Redistributions of source code must retain the above copyright 10152251Srwatson * notice, this list of conditions and the following disclaimer. 11152251Srwatson * 2. Redistributions in binary form must reproduce the above copyright 12152251Srwatson * notice, this list of conditions and the following disclaimer in the 13152251Srwatson * documentation and/or other materials provided with the distribution. 14152251Srwatson * 15152251Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16152251Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17152251Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18152251Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19152251Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20152251Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21152251Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22152251Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23152251Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24152251Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25152251Srwatson * SUCH DAMAGE. 26152251Srwatson * 27152251Srwatson * $FreeBSD: releng/10.3/tests/sys/kern/unix_passfd_test.c 293272 2016-01-06 20:29:15Z ngie $ 28152251Srwatson */ 29152251Srwatson 30152251Srwatson#include <sys/types.h> 31152251Srwatson#include <sys/socket.h> 32152251Srwatson#include <sys/stat.h> 33261550Sglebius#include <sys/sysctl.h> 34261550Sglebius#include <sys/un.h> 35152251Srwatson 36292914Sngie#include <errno.h> 37228371Sjhb#include <fcntl.h> 38152251Srwatson#include <limits.h> 39152251Srwatson#include <stdio.h> 40261550Sglebius#include <stdlib.h> 41152251Srwatson#include <string.h> 42152251Srwatson#include <unistd.h> 43152251Srwatson 44292914Sngie#include <atf-c.h> 45292914Sngie 46152251Srwatson/* 47152251Srwatson * UNIX domain sockets allow file descriptors to be passed via "ancillary 48152251Srwatson * data", or control messages. This regression test is intended to exercise 49152251Srwatson * this facility, both performing some basic tests that it operates, and also 50152251Srwatson * causing some kernel edge cases to execute, such as garbage collection when 51152251Srwatson * there are cyclic file descriptor references. Right now we test only with 52152251Srwatson * stream sockets, but ideally we'd also test with datagram sockets. 53152251Srwatson */ 54152251Srwatson 55152251Srwatsonstatic void 56292914Sngiedomainsocketpair(int *fdp) 57152251Srwatson{ 58152251Srwatson 59292914Sngie ATF_REQUIRE_MSG(socketpair(PF_UNIX, SOCK_STREAM, 0, fdp) != -1, 60292914Sngie "socketpair(PF_UNIX, SOCK_STREAM) failed: %s", strerror(errno)); 61152251Srwatson} 62152251Srwatson 63152251Srwatsonstatic void 64152251Srwatsonclosesocketpair(int *fdp) 65152251Srwatson{ 66152251Srwatson 67152251Srwatson close(fdp[0]); 68152251Srwatson close(fdp[1]); 69152251Srwatson} 70152251Srwatson 71152251Srwatsonstatic void 72292914Sngiedevnull(int *fdp) 73228371Sjhb{ 74228371Sjhb int fd; 75228371Sjhb 76228371Sjhb fd = open("/dev/null", O_RDONLY); 77292914Sngie ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 78228371Sjhb *fdp = fd; 79228371Sjhb} 80228371Sjhb 81228371Sjhbstatic void 82292914Sngietempfile(int *fdp) 83152251Srwatson{ 84152251Srwatson char path[PATH_MAX]; 85152251Srwatson int fd; 86152251Srwatson 87292914Sngie snprintf(path, PATH_MAX, "%s/unix_passfd.XXXXXXXXXXXXXXX", 88292914Sngie getenv("TMPDIR") == NULL ? "/tmp" : getenv("TMPDIR")); 89152251Srwatson fd = mkstemp(path); 90292914Sngie ATF_REQUIRE_MSG(fd != -1, "mkstemp(%s) failed", path); 91152251Srwatson (void)unlink(path); 92152251Srwatson *fdp = fd; 93152251Srwatson} 94152251Srwatson 95152251Srwatsonstatic void 96292914Sngiedofstat(int fd, struct stat *sb) 97152251Srwatson{ 98152251Srwatson 99292914Sngie ATF_REQUIRE_MSG(fstat(fd, sb) == 0, 100292914Sngie "fstat failed: %s", strerror(errno)); 101152251Srwatson} 102152251Srwatson 103152251Srwatsonstatic void 104292914Sngiesamefile(struct stat *sb1, struct stat *sb2) 105152251Srwatson{ 106152251Srwatson 107292914Sngie ATF_REQUIRE_MSG(sb1->st_dev == sb2->st_dev, "different device"); 108292914Sngie ATF_REQUIRE_MSG(sb1->st_ino == sb2->st_ino, "different inode"); 109152251Srwatson} 110152251Srwatson 111152251Srwatsonstatic void 112293272Sngiesendfd_payload(int sockfd, int send_fd, void *payload, size_t paylen) 113152251Srwatson{ 114152251Srwatson struct iovec iovec; 115228371Sjhb char message[CMSG_SPACE(sizeof(int))]; 116228371Sjhb struct cmsghdr *cmsghdr; 117152251Srwatson struct msghdr msghdr; 118152251Srwatson ssize_t len; 119152251Srwatson 120152251Srwatson bzero(&msghdr, sizeof(msghdr)); 121152251Srwatson bzero(&message, sizeof(message)); 122152251Srwatson 123228371Sjhb msghdr.msg_control = message; 124152251Srwatson msghdr.msg_controllen = sizeof(message); 125152251Srwatson 126261550Sglebius iovec.iov_base = payload; 127261550Sglebius iovec.iov_len = paylen; 128152251Srwatson 129152251Srwatson msghdr.msg_iov = &iovec; 130152251Srwatson msghdr.msg_iovlen = 1; 131152251Srwatson 132292914Sngie cmsghdr = (struct cmsghdr *)(void*)message; 133228371Sjhb cmsghdr->cmsg_len = CMSG_LEN(sizeof(int)); 134228371Sjhb cmsghdr->cmsg_level = SOL_SOCKET; 135228371Sjhb cmsghdr->cmsg_type = SCM_RIGHTS; 136293272Sngie memcpy(CMSG_DATA(cmsghdr), &send_fd, sizeof(int)); 137152251Srwatson 138152251Srwatson len = sendmsg(sockfd, &msghdr, 0); 139292914Sngie ATF_REQUIRE_MSG(len != -1, "sendmsg failed: %s", strerror(errno)); 140292914Sngie ATF_REQUIRE_MSG((size_t)len == paylen, 141292914Sngie "sendmsg: %zd messages sent; expected: %zu; %s", len, paylen, 142292914Sngie strerror(errno)); 143152251Srwatson} 144152251Srwatson 145152251Srwatsonstatic void 146293272Sngiesendfd(int sockfd, int send_fd) 147152251Srwatson{ 148292814Sngie char ch = 0; 149261550Sglebius 150293272Sngie return (sendfd_payload(sockfd, send_fd, &ch, sizeof(ch))); 151261550Sglebius} 152261550Sglebius 153261550Sglebiusstatic void 154293272Sngierecvfd_payload(int sockfd, int *recv_fd, void *buf, size_t buflen) 155261550Sglebius{ 156228371Sjhb struct cmsghdr *cmsghdr; 157261550Sglebius char message[CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + sizeof(int)]; 158152251Srwatson struct msghdr msghdr; 159152251Srwatson struct iovec iovec; 160152251Srwatson ssize_t len; 161152251Srwatson 162152251Srwatson bzero(&msghdr, sizeof(msghdr)); 163152251Srwatson 164228371Sjhb msghdr.msg_control = message; 165152251Srwatson msghdr.msg_controllen = sizeof(message); 166152251Srwatson 167261550Sglebius iovec.iov_base = buf; 168261550Sglebius iovec.iov_len = buflen; 169152251Srwatson 170152251Srwatson msghdr.msg_iov = &iovec; 171152251Srwatson msghdr.msg_iovlen = 1; 172152251Srwatson 173152251Srwatson len = recvmsg(sockfd, &msghdr, 0); 174292914Sngie ATF_REQUIRE_MSG(len != -1, "recvmsg failed: %s", strerror(errno)); 175292914Sngie ATF_REQUIRE_MSG((size_t)len == buflen, 176292914Sngie "recvmsg: %zd bytes received; expected %zd", len, buflen); 177261550Sglebius 178228371Sjhb cmsghdr = CMSG_FIRSTHDR(&msghdr); 179292914Sngie ATF_REQUIRE_MSG(cmsghdr != NULL, 180292914Sngie "recvmsg: did not receive control message"); 181293272Sngie *recv_fd = -1; 182261550Sglebius for (; cmsghdr != NULL; cmsghdr = CMSG_NXTHDR(&msghdr, cmsghdr)) { 183261550Sglebius if (cmsghdr->cmsg_level == SOL_SOCKET && 184261550Sglebius cmsghdr->cmsg_type == SCM_RIGHTS && 185261550Sglebius cmsghdr->cmsg_len == CMSG_LEN(sizeof(int))) { 186293272Sngie memcpy(recv_fd, CMSG_DATA(cmsghdr), sizeof(int)); 187293272Sngie ATF_REQUIRE(*recv_fd != -1); 188261550Sglebius } 189261550Sglebius } 190293272Sngie ATF_REQUIRE_MSG(*recv_fd != -1, 191292914Sngie "recvmsg: did not receive single-fd message"); 192152251Srwatson} 193152251Srwatson 194261550Sglebiusstatic void 195293272Sngierecvfd(int sockfd, int *recv_fd) 196261550Sglebius{ 197292814Sngie char ch = 0; 198261550Sglebius 199293272Sngie return (recvfd_payload(sockfd, recv_fd, &ch, sizeof(ch))); 200261550Sglebius} 201261550Sglebius 202292914Sngie/* 203292914Sngie * Put a temporary file into a UNIX domain socket, then take it out and make 204292914Sngie * sure it's the same file. First time around, don't close the reference 205292914Sngie * after sending. 206292914Sngie */ 207292914SngieATF_TC_WITHOUT_HEAD(simple_send_fd); 208292914SngieATF_TC_BODY(simple_send_fd, tc) 209152251Srwatson{ 210292914Sngie struct stat getfd_stat, putfd_stat; 211292914Sngie int fd[2], getfd, putfd; 212152251Srwatson 213292914Sngie domainsocketpair(fd); 214292914Sngie tempfile(&putfd); 215292914Sngie dofstat(putfd, &putfd_stat); 216292914Sngie sendfd(fd[0], putfd); 217292914Sngie recvfd(fd[1], &getfd); 218292914Sngie dofstat(getfd, &getfd_stat); 219292914Sngie samefile(&putfd_stat, &getfd_stat); 220292914Sngie close(putfd); 221292914Sngie close(getfd); 222152251Srwatson closesocketpair(fd); 223292914Sngie} 224152251Srwatson 225292914Sngie/* 226292914Sngie * Same as simple_send_fd, only close the file reference after sending, so that 227292914Sngie * the only reference is the descriptor in the UNIX domain socket buffer. 228292914Sngie */ 229292914SngieATF_TC_WITHOUT_HEAD(send_and_close); 230292914SngieATF_TC_BODY(send_and_close, tc) 231292914Sngie{ 232292914Sngie struct stat getfd_stat, putfd_stat; 233292914Sngie int fd[2], getfd, putfd; 234152251Srwatson 235292914Sngie domainsocketpair(fd); 236292914Sngie tempfile(&putfd); 237292914Sngie dofstat(putfd, &putfd_stat); 238292914Sngie sendfd(fd[0], putfd); 239292914Sngie close(putfd); 240292914Sngie recvfd(fd[1], &getfd); 241292914Sngie dofstat(getfd, &getfd_stat); 242292914Sngie samefile(&putfd_stat, &getfd_stat); 243292914Sngie close(getfd); 244152251Srwatson closesocketpair(fd); 245292914Sngie} 246152251Srwatson 247292914Sngie/* 248292914Sngie * Put a temporary file into a UNIX domain socket, then close both endpoints 249292914Sngie * causing garbage collection to kick off. 250292914Sngie */ 251292914SngieATF_TC_WITHOUT_HEAD(send_and_cancel); 252292914SngieATF_TC_BODY(send_and_cancel, tc) 253292914Sngie{ 254292914Sngie int fd[2], putfd; 255152251Srwatson 256292914Sngie domainsocketpair(fd); 257292914Sngie tempfile(&putfd); 258292914Sngie sendfd(fd[0], putfd); 259292914Sngie close(putfd); 260152251Srwatson closesocketpair(fd); 261292914Sngie} 262152251Srwatson 263292914Sngie/* 264292914Sngie * Send two files. Then receive them. Make sure they are returned in the 265292914Sngie * right order, and both get there. 266292914Sngie */ 267292914SngieATF_TC_WITHOUT_HEAD(two_files); 268292914SngieATF_TC_BODY(two_files, tc) 269292914Sngie{ 270292914Sngie struct stat getfd_1_stat, getfd_2_stat, putfd_1_stat, putfd_2_stat; 271292914Sngie int fd[2], getfd_1, getfd_2, putfd_1, putfd_2; 272152251Srwatson 273292914Sngie domainsocketpair(fd); 274292914Sngie tempfile(&putfd_1); 275292914Sngie tempfile(&putfd_2); 276292914Sngie dofstat(putfd_1, &putfd_1_stat); 277292914Sngie dofstat(putfd_2, &putfd_2_stat); 278292914Sngie sendfd(fd[0], putfd_1); 279292914Sngie sendfd(fd[0], putfd_2); 280152251Srwatson close(putfd_1); 281152251Srwatson close(putfd_2); 282292914Sngie recvfd(fd[1], &getfd_1); 283292914Sngie recvfd(fd[1], &getfd_2); 284292914Sngie dofstat(getfd_1, &getfd_1_stat); 285292914Sngie dofstat(getfd_2, &getfd_2_stat); 286292914Sngie samefile(&putfd_1_stat, &getfd_1_stat); 287292914Sngie samefile(&putfd_2_stat, &getfd_2_stat); 288152251Srwatson close(getfd_1); 289152251Srwatson close(getfd_2); 290152251Srwatson closesocketpair(fd); 291292914Sngie} 292152251Srwatson 293292914Sngie/* 294292914Sngie * Big bundling test. Send an endpoint of the UNIX domain socket over itself, 295292914Sngie * closing the door behind it. 296292914Sngie */ 297292914SngieATF_TC_WITHOUT_HEAD(bundle); 298292914SngieATF_TC_BODY(bundle, tc) 299292914Sngie{ 300292914Sngie int fd[2], getfd; 301152251Srwatson 302292914Sngie domainsocketpair(fd); 303152251Srwatson 304292914Sngie sendfd(fd[0], fd[0]); 305152251Srwatson close(fd[0]); 306292914Sngie recvfd(fd[1], &getfd); 307292914Sngie close(getfd); 308152251Srwatson close(fd[1]); 309292914Sngie} 310152251Srwatson 311292914Sngie/* 312292914Sngie * Big bundling test part two: Send an endpoint of the UNIX domain socket over 313292914Sngie * itself, close the door behind it, and never remove it from the other end. 314292914Sngie */ 315292914SngieATF_TC_WITHOUT_HEAD(bundle_cancel); 316292914SngieATF_TC_BODY(bundle_cancel, tc) 317292914Sngie{ 318292914Sngie int fd[2]; 319152251Srwatson 320292914Sngie domainsocketpair(fd); 321292914Sngie sendfd(fd[0], fd[0]); 322292914Sngie sendfd(fd[1], fd[0]); 323292914Sngie closesocketpair(fd); 324292914Sngie} 325152251Srwatson 326292914Sngie/* 327292914Sngie * Test for PR 151758: Send an character device over the UNIX domain socket 328292914Sngie * and then close both sockets to orphan the device. 329292914Sngie */ 330292914SngieATF_TC_WITHOUT_HEAD(devfs_orphan); 331292914SngieATF_TC_BODY(devfs_orphan, tc) 332292914Sngie{ 333292914Sngie int fd[2], putfd; 334152251Srwatson 335292914Sngie domainsocketpair(fd); 336292914Sngie devnull(&putfd); 337292914Sngie sendfd(fd[0], putfd); 338292914Sngie close(putfd); 339152251Srwatson closesocketpair(fd); 340292914Sngie} 341152251Srwatson 342292914Sngie#define LOCAL_SENDSPACE_SYSCTL "net.local.stream.sendspace" 343152251Srwatson 344292914Sngie/* 345292914Sngie * Test for PR 181741. Receiver sets LOCAL_CREDS, and kernel prepends a 346292914Sngie * control message to the data. Sender sends large payload. 347292914Sngie * Payload + SCM_RIGHTS + LOCAL_CREDS hit socket buffer limit, and receiver 348292914Sngie * receives truncated data. 349292914Sngie */ 350292914SngieATF_TC_WITHOUT_HEAD(rights_creds_payload); 351292914SngieATF_TC_BODY(rights_creds_payload, tc) 352292914Sngie{ 353292914Sngie const int on = 1; 354292914Sngie u_long sendspace; 355292914Sngie size_t len; 356292914Sngie void *buf; 357292914Sngie int fd[2], getfd, putfd, rc; 358228371Sjhb 359292914Sngie atf_tc_expect_fail("PR 181741: Packet loss when 'control' messages " 360292914Sngie "are present with large data"); 361228371Sjhb 362292914Sngie len = sizeof(sendspace); 363292914Sngie rc = sysctlbyname(LOCAL_SENDSPACE_SYSCTL, &sendspace, 364292914Sngie &len, NULL, 0); 365292914Sngie ATF_REQUIRE_MSG(rc != -1, 366292914Sngie "sysctl %s failed: %s", LOCAL_SENDSPACE_SYSCTL, strerror(errno)); 367228371Sjhb 368292914Sngie buf = calloc(1, sendspace); 369292914Sngie ATF_REQUIRE(buf != NULL); 370261550Sglebius 371292914Sngie domainsocketpair(fd); 372292914Sngie rc = setsockopt(fd[1], 0, LOCAL_CREDS, &on, sizeof(on)); 373292914Sngie ATF_REQUIRE_MSG(rc != -1, "setsockopt(LOCAL_CREDS) failed: %s", 374292914Sngie strerror(errno)); 375292914Sngie tempfile(&putfd); 376292914Sngie sendfd_payload(fd[0], putfd, buf, sendspace); 377292914Sngie recvfd_payload(fd[1], &getfd, buf, sendspace); 378292914Sngie close(putfd); 379292914Sngie close(getfd); 380292914Sngie closesocketpair(fd); 381292914Sngie} 382261550Sglebius 383292914SngieATF_TP_ADD_TCS(tp) 384292914Sngie{ 385261550Sglebius 386292914Sngie ATF_TP_ADD_TC(tp, simple_send_fd); 387292914Sngie ATF_TP_ADD_TC(tp, send_and_close); 388292914Sngie ATF_TP_ADD_TC(tp, send_and_cancel); 389292914Sngie ATF_TP_ADD_TC(tp, two_files); 390292914Sngie ATF_TP_ADD_TC(tp, bundle); 391292914Sngie ATF_TP_ADD_TC(tp, bundle_cancel); 392292914Sngie ATF_TP_ADD_TC(tp, devfs_orphan); 393292914Sngie ATF_TP_ADD_TC(tp, rights_creds_payload); 394261550Sglebius 395292914Sngie return (atf_no_error()); 396152251Srwatson} 397