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