xref: /freebsd/tests/sys/kern/unix_connectat.c (revision 3baada83bf2deb48c7251f8de3f73e489338d4f1)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2026 John Ericson
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 /*
29  * Tests for connectat(2) naming a unix-domain peer by descriptor.
30  *
31  * A peer can be named three ways -- as the socket object itself, as a bound
32  * socket's filesystem node, or as an fdescfs /dev/fd node standing in for a
33  * socket descriptor -- and each of the two filesystem nodes can be reached
34  * either by an empty sun_path over a descriptor or by a pathname.  The socket
35  * object has no pathname form (a path that names a descriptor is the /dev/fd
36  * node, not the socket directly), giving five combinations, all of which must
37  * reach the same peer:
38  *
39  *                          | empty sun_path (fd)      | pathname
40  *   -----------------------+--------------------------+-----------------------
41  *   socket object          | fd is the socket         | (n/a: a path to a
42  *                          | -> stream, dgram, ...    |  descriptor is /dev/fd)
43  *   -----------------------+--------------------------+-----------------------
44  *   bound socket file      | O_PATH handle of the     | classic bind-path
45  *   (VSOCK vnode)          | socket's vnode           | lookup
46  *                          | -> empty_path_vnode      | -> path
47  *   -----------------------+--------------------------+-----------------------
48  *   fdescfs node of a      | O_PATH handle of the     | the "N" pathname,
49  *   socket descriptor      | fdescfs node             | absolute or relative
50  *   (VNON vnode)           | -> empty_path_devfd      | -> devfd,
51  *                          |                          |    devfd_relative
52  *
53  * An empty sun_path is signalled by sun_len == offsetof(.., sun_path).
54  *
55  * The fdescfs cases mount their own fdescfs instance rather than relying on
56  * the host's /dev/fd, so they require root; see mount_fdescfs() below.
57  */
58 
59 #include <sys/param.h>
60 #include <sys/capsicum.h>
61 #include <sys/mount.h>
62 #include <sys/socket.h>
63 #include <sys/stat.h>
64 #include <sys/un.h>
65 #include <errno.h>
66 #include <fcntl.h>
67 #include <mntopts.h>
68 #include <netinet/in.h>
69 #include <stdio.h>
70 #include <string.h>
71 #include <unistd.h>
72 
73 #include <atf-c.h>
74 
75 /* An AF_UNIX address with an empty path: "the fd is the peer". */
76 static const struct sockaddr_un empty_sun = {
77 	.sun_family = AF_UNIX,
78 	.sun_len = offsetof(struct sockaddr_un, sun_path),
79 };
80 
81 /* A nonempty address whose path starts with NUL, as Linux abstract names do. */
82 static const struct sockaddr_un nul_sun = {
83 	.sun_family = AF_UNIX,
84 	.sun_len = offsetof(struct sockaddr_un, sun_path) + 2,
85 	.sun_path = "\0x",
86 };
87 
88 /* Make a bound, listening stream socket. */
89 static int
mklistener(const char * path)90 mklistener(const char *path)
91 {
92 	struct sockaddr_un sun = { .sun_family = AF_UNIX };
93 	int l;
94 
95 	strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
96 	sun.sun_len = SUN_LEN(&sun);
97 	ATF_REQUIRE((l = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
98 	ATF_REQUIRE_MSG(bind(l, (struct sockaddr *)&sun, sun.sun_len) == 0,
99 	    "bind(%s): %s", path, strerror(errno));
100 	ATF_REQUIRE_MSG(listen(l, 1) == 0, "listen: %s", strerror(errno));
101 	return (l);
102 }
103 
104 static int
fdconnect(int fd,int s)105 fdconnect(int fd, int s)
106 {
107 	return (connectat(fd, s, (const struct sockaddr *)&empty_sun,
108 	    empty_sun.sun_len));
109 }
110 
111 /* connectat(2) to a pathname, relative to fd (AT_FDCWD for absolute). */
112 static int
pathconnect(int fd,int s,const char * path)113 pathconnect(int fd, int s, const char *path)
114 {
115 	struct sockaddr_un sun = { .sun_family = AF_UNIX };
116 
117 	strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
118 	sun.sun_len = SUN_LEN(&sun);
119 	return (connectat(fd, s, (const struct sockaddr *)&sun, sun.sun_len));
120 }
121 
122 /* Where the fdescfs cases mount fdescfs, inside the test's work directory. */
123 #define	FDDIR	"fd"
124 
125 /*
126  * Mount an fdescfs instance on FDDIR, enabling each mount option flag in the
127  * NULL-terminated 'opts' (NULL for a plain mount).  Mounting our own instance
128  * rather than relying on the host's /dev/fd keeps the fdescfs cases
129  * self-contained: they exercise real fdescfs lookups regardless of how the
130  * host is set up, and the mode-specific behaviour below is then well defined.
131  * Skips if the kernel has no fdescfs.
132  */
133 static void
mount_fdescfs(const char * const * opts)134 mount_fdescfs(const char * const *opts)
135 {
136 	struct iovec *iov;
137 	char errmsg[1024];
138 	int error, iovlen;
139 
140 	ATF_REQUIRE_MSG(mkdir(FDDIR, 0755) == 0 || errno == EEXIST,
141 	    "mkdir %s: %s", FDDIR, strerror(errno));
142 
143 	iov = NULL;
144 	iovlen = 0;
145 	build_iovec(&iov, &iovlen, __DECONST(char *, "fstype"),
146 	    __DECONST(char *, "fdescfs"), (size_t)-1);
147 	build_iovec(&iov, &iovlen, __DECONST(char *, "fspath"),
148 	    __DECONST(char *, FDDIR), (size_t)-1);
149 	for (; opts != NULL && *opts != NULL; opts++)
150 		build_iovec(&iov, &iovlen, __DECONST(char *, *opts), NULL,
151 		    (size_t)-1);
152 	build_iovec(&iov, &iovlen, __DECONST(char *, "errmsg"), errmsg,
153 	    sizeof(errmsg));
154 
155 	errmsg[0] = '\0';
156 	error = nmount(iov, iovlen, 0);
157 	if (error != 0 && errno == ENODEV)
158 		atf_tc_skip("no fdescfs support in the kernel");
159 	ATF_REQUIRE_MSG(error == 0, "mount fdescfs on %s: %s", FDDIR,
160 	    errmsg[0] != '\0' ? errmsg : strerror(errno));
161 
162 	free_iovec(&iov, &iovlen);
163 }
164 
165 /* Name descriptor 'fd' within the fdescfs mounted above. */
166 static void
fdpath(char * buf,size_t len,int fd)167 fdpath(char *buf, size_t len, int fd)
168 {
169 	int n;
170 
171 	n = snprintf(buf, len, FDDIR "/%d", fd);
172 	ATF_REQUIRE(n > 0 && (size_t)n < len);
173 }
174 
175 /*
176  * Boilerplate for a case that mounts fdescfs: mounting requires root, and the
177  * mount has to be undone even when the body fails, or the work directory
178  * cannot be removed.  Each body calls mount_fdescfs() itself, choosing the
179  * mount options it wants to exercise.
180  */
181 #define	FDESCFS_TC(name)						\
182 	ATF_TC_WITH_CLEANUP(name);					\
183 	ATF_TC_HEAD(name, tc)						\
184 	{								\
185 		atf_tc_set_md_var(tc, "require.user", "root");		\
186 	}								\
187 	ATF_TC_CLEANUP(name, tc)					\
188 	{								\
189 		(void)unmount(FDDIR, 0);				\
190 	}
191 
192 /* Connect to a listening stream socket by its fd; pass data. */
193 ATF_TC_WITHOUT_HEAD(stream);
ATF_TC_BODY(stream,tc)194 ATF_TC_BODY(stream, tc)
195 {
196 	char buf[8];
197 	int l, s, a;
198 
199 	l = mklistener("stream.sock");
200 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
201 	ATF_REQUIRE_EQ(0, fdconnect(l, s));
202 	ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
203 
204 	ATF_REQUIRE_EQ(5, write(s, "hello", 5));
205 	ATF_REQUIRE_EQ(5, read(a, buf, sizeof(buf)));
206 	ATF_REQUIRE_EQ(0, memcmp(buf, "hello", 5));
207 	ATF_REQUIRE_EQ(5, write(a, "world", 5));
208 	ATF_REQUIRE_EQ(5, read(s, buf, sizeof(buf)));
209 	ATF_REQUIRE_EQ(0, memcmp(buf, "world", 5));
210 
211 	ATF_REQUIRE_EQ(0, close(a));
212 	ATF_REQUIRE_EQ(0, close(s));
213 	ATF_REQUIRE_EQ(0, close(l));
214 }
215 
216 /* A bound listener's path is still reported to the connecting side. */
217 ATF_TC_WITHOUT_HEAD(stream_bound);
ATF_TC_BODY(stream_bound,tc)218 ATF_TC_BODY(stream_bound, tc)
219 {
220 	struct sockaddr_un sun;
221 	socklen_t len;
222 	int l, s;
223 
224 	l = mklistener("bound.sock");
225 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
226 	ATF_REQUIRE_EQ(0, fdconnect(l, s));
227 
228 	memset(&sun, 0, sizeof(sun));
229 	len = sizeof(sun);
230 	ATF_REQUIRE_EQ(0, getpeername(s, (struct sockaddr *)&sun, &len));
231 	ATF_REQUIRE_EQ(0, strcmp(sun.sun_path, "bound.sock"));
232 
233 	ATF_REQUIRE_EQ(0, close(s));
234 	ATF_REQUIRE_EQ(0, close(l));
235 }
236 
237 /*
238  * A socket may listen while unbound, and connectat(2) reaches it by
239  * descriptor: with no pathname there is nothing else that could name it.
240  * mklistener() cannot be used, as it binds first.
241  */
242 ATF_TC_WITHOUT_HEAD(listen_unbound);
ATF_TC_BODY(listen_unbound,tc)243 ATF_TC_BODY(listen_unbound, tc)
244 {
245 	char buf[8];
246 	int l, s, a;
247 
248 	ATF_REQUIRE((l = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
249 	ATF_REQUIRE_MSG(listen(l, 1) == 0, "listen: %s", strerror(errno));
250 
251 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
252 	ATF_REQUIRE_EQ(0, fdconnect(l, s));
253 	ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
254 
255 	/* A real connection, not just an accepted descriptor. */
256 	ATF_REQUIRE_EQ(5, write(s, "hello", 5));
257 	ATF_REQUIRE_EQ(5, read(a, buf, sizeof(buf)));
258 	ATF_REQUIRE_EQ(0, memcmp(buf, "hello", 5));
259 
260 	ATF_REQUIRE_EQ(0, close(a));
261 	ATF_REQUIRE_EQ(0, close(s));
262 	ATF_REQUIRE_EQ(0, close(l));
263 }
264 
265 /*
266  * A socket may be bound after it listens, so a listener can be published only
267  * once it is ready to accept, rather than leaving a window in which the socket
268  * file exists but connections to it are refused.  The late-bound name behaves
269  * like any other.  mklistener() cannot be used: it binds first.
270  */
271 ATF_TC_WITHOUT_HEAD(bind_after_listen);
ATF_TC_BODY(bind_after_listen,tc)272 ATF_TC_BODY(bind_after_listen, tc)
273 {
274 	struct sockaddr_un sun = { .sun_family = AF_UNIX };
275 	struct sockaddr_un peer;
276 	socklen_t len;
277 	int l, s, a;
278 
279 	ATF_REQUIRE((l = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
280 	ATF_REQUIRE_MSG(listen(l, 1) == 0, "listen: %s", strerror(errno));
281 
282 	strlcpy(sun.sun_path, "late.sock", sizeof(sun.sun_path));
283 	sun.sun_len = SUN_LEN(&sun);
284 	ATF_REQUIRE_MSG(bind(l, (struct sockaddr *)&sun, sun.sun_len) == 0,
285 	    "bind after listen: %s", strerror(errno));
286 
287 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
288 	ATF_REQUIRE_EQ(0, pathconnect(AT_FDCWD, s, "late.sock"));
289 	ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
290 
291 	/* The name bound after listen(2) is reported to the peer. */
292 	memset(&peer, 0, sizeof(peer));
293 	len = sizeof(peer);
294 	ATF_REQUIRE_EQ(0, getpeername(s, (struct sockaddr *)&peer, &len));
295 	ATF_REQUIRE_EQ(0, strcmp(peer.sun_path, "late.sock"));
296 
297 	ATF_REQUIRE_EQ(0, close(a));
298 	ATF_REQUIRE_EQ(0, close(s));
299 	ATF_REQUIRE_EQ(0, close(l));
300 }
301 
302 /*
303  * A socket whose connection has gone away may become a listener in its own
304  * right: unp_soisdisconnected() leaves only SS_ISDISCONNECTED set, which
305  * solisten_proto_check() does not reject, and unp_disconnect() has already
306  * cleared unp_conn.  Only the bind requirement stood in the way, and then only
307  * for the usual client socket, which has no name.
308  *
309  * Note: this case is here only to document the current behavior and to catch
310  * it changing in the future.  Such socket reuse is not covered by the
311  * specification, and is discouraged and should not be utilized in real-world
312  * programs.
313  */
314 ATF_TC_WITHOUT_HEAD(listen_after_disconnect);
ATF_TC_BODY(listen_after_disconnect,tc)315 ATF_TC_BODY(listen_after_disconnect, tc)
316 {
317 	int l, c, s, a;
318 
319 	/* Connect a pair, then drop the accepted end to disconnect 'c'. */
320 	ATF_REQUIRE((l = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
321 	ATF_REQUIRE_MSG(listen(l, 1) == 0, "listen: %s", strerror(errno));
322 	ATF_REQUIRE((c = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
323 	ATF_REQUIRE_EQ(0, fdconnect(l, c));
324 	ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
325 	ATF_REQUIRE_EQ(0, close(a));
326 	ATF_REQUIRE_EQ(0, close(l));
327 
328 	/* The survivor listens, and takes a connection of its own. */
329 	ATF_REQUIRE_MSG(listen(c, 1) == 0, "listen: %s", strerror(errno));
330 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
331 	ATF_REQUIRE_EQ(0, fdconnect(c, s));
332 	ATF_REQUIRE((a = accept(c, NULL, NULL)) >= 0);
333 
334 	ATF_REQUIRE_EQ(0, close(a));
335 	ATF_REQUIRE_EQ(0, close(s));
336 	ATF_REQUIRE_EQ(0, close(c));
337 }
338 
339 /* Connect a datagram socket to an unbound peer by its fd. */
340 ATF_TC_WITHOUT_HEAD(dgram);
ATF_TC_BODY(dgram,tc)341 ATF_TC_BODY(dgram, tc)
342 {
343 	char buf[8];
344 	int p, s;
345 
346 	ATF_REQUIRE((p = socket(PF_UNIX, SOCK_DGRAM, 0)) >= 0);
347 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_DGRAM, 0)) >= 0);
348 	ATF_REQUIRE_EQ(0, fdconnect(p, s));
349 	ATF_REQUIRE_EQ(5, send(s, "hello", 5, 0));
350 	ATF_REQUIRE_EQ(5, recv(p, buf, sizeof(buf), 0));
351 	ATF_REQUIRE_EQ(0, memcmp(buf, "hello", 5));
352 
353 	ATF_REQUIRE_EQ(0, close(s));
354 	ATF_REQUIRE_EQ(0, close(p));
355 }
356 
357 /*
358  * Matrix cell: empty path + a descriptor that names a bound socket's *vnode*
359  * (an O_PATH handle), not the socket object.  getsock() sees a non-socket and
360  * the connect falls back to an EMPTYPATH lookup that resolves the vnode.
361  */
362 ATF_TC_WITHOUT_HEAD(empty_path_vnode);
ATF_TC_BODY(empty_path_vnode,tc)363 ATF_TC_BODY(empty_path_vnode, tc)
364 {
365 	int l, s, a, pathfd;
366 
367 	l = mklistener("evnode.sock");
368 	ATF_REQUIRE_MSG((pathfd = open("evnode.sock", O_PATH)) >= 0,
369 	    "open(O_PATH): %s", strerror(errno));
370 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
371 	ATF_REQUIRE_EQ(0, fdconnect(pathfd, s));
372 	ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
373 
374 	ATF_REQUIRE_EQ(0, close(a));
375 	ATF_REQUIRE_EQ(0, close(s));
376 	ATF_REQUIRE_EQ(0, close(pathfd));
377 	ATF_REQUIRE_EQ(0, close(l));
378 }
379 
380 /*
381  * Matrix cell: non-empty path naming a bound socket's vnode -- the classic
382  * connect-by-pathname case, here spelled through connectat(2).
383  */
384 ATF_TC_WITHOUT_HEAD(path);
ATF_TC_BODY(path,tc)385 ATF_TC_BODY(path, tc)
386 {
387 	int l, s, a;
388 
389 	l = mklistener("path.sock");
390 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
391 	ATF_REQUIRE_EQ(0, pathconnect(AT_FDCWD, s, "path.sock"));
392 	ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
393 
394 	ATF_REQUIRE_EQ(0, close(a));
395 	ATF_REQUIRE_EQ(0, close(s));
396 	ATF_REQUIRE_EQ(0, close(l));
397 }
398 
399 /*
400  * Matrix cell: non-empty path that resolves to the socket *object* -- an
401  * fdescfs pathname naming the listener's descriptor.  This is plain
402  * connect(2), no empty path involved.
403  */
404 FDESCFS_TC(devfd);
ATF_TC_BODY(devfd,tc)405 ATF_TC_BODY(devfd, tc)
406 {
407 	char path[32];
408 	int l, s, a;
409 
410 	mount_fdescfs(NULL);
411 	l = mklistener("devfd.sock");
412 	fdpath(path, sizeof(path), l);
413 
414 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
415 	ATF_REQUIRE_EQ(0, pathconnect(AT_FDCWD, s, path));
416 	ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
417 
418 	ATF_REQUIRE_EQ(0, close(a));
419 	ATF_REQUIRE_EQ(0, close(s));
420 	ATF_REQUIRE_EQ(0, close(l));
421 }
422 
423 /*
424  * Matrix cell variant of `devfd`: the same socket-object lookup, but reached
425  * through connectat(2)'s dirfd-relative resolution.  A directory descriptor
426  * for the fdescfs mount serves as the base, and the peer is named by the
427  * *relative* path "N" -- the listener's descriptor number.  NDINIT_ATRIGHTS
428  * anchors namei() at the dirfd, and fdescfs resolves that descriptor to the
429  * socket unp_connectat() connects to.
430  */
431 FDESCFS_TC(devfd_relative);
ATF_TC_BODY(devfd_relative,tc)432 ATF_TC_BODY(devfd_relative, tc)
433 {
434 	char path[32];
435 	int l, s, a, dirfd;
436 
437 	mount_fdescfs(NULL);
438 	l = mklistener("devfd_rel.sock");
439 	ATF_REQUIRE_MSG((dirfd = open(FDDIR, O_DIRECTORY)) >= 0,
440 	    "open(%s, O_DIRECTORY): %s", FDDIR, strerror(errno));
441 
442 	/* Name the listener by its fd number, relative to the fdescfs dir. */
443 	ATF_REQUIRE(snprintf(path, sizeof(path), "%d", l) > 0);
444 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
445 	ATF_REQUIRE_EQ(0, pathconnect(dirfd, s, path));
446 	ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
447 
448 	ATF_REQUIRE_EQ(0, close(a));
449 	ATF_REQUIRE_EQ(0, close(s));
450 	ATF_REQUIRE_EQ(0, close(dirfd));
451 	ATF_REQUIRE_EQ(0, close(l));
452 }
453 
454 /*
455  * Matrix cell: empty path + an O_PATH handle to an fdescfs node.  getsock()
456  * sees a non-socket, the EMPTYPATH lookup resolves the synthetic fdescfs node,
457  * and opening that node yields the underlying descriptor -- the same socket.
458  * Reaches the fdescfs node by descriptor rather than by pathname.
459  */
460 FDESCFS_TC(empty_path_devfd);
ATF_TC_BODY(empty_path_devfd,tc)461 ATF_TC_BODY(empty_path_devfd, tc)
462 {
463 	char path[32];
464 	int l, s, a, pathfd;
465 
466 	mount_fdescfs(NULL);
467 	l = mklistener("edevfd.sock");
468 	fdpath(path, sizeof(path), l);
469 	ATF_REQUIRE_MSG((pathfd = open(path, O_PATH)) >= 0,
470 	    "open(%s, O_PATH): %s", path, strerror(errno));
471 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
472 	ATF_REQUIRE_EQ(0, fdconnect(pathfd, s));
473 	ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
474 
475 	ATF_REQUIRE_EQ(0, close(a));
476 	ATF_REQUIRE_EQ(0, close(s));
477 	ATF_REQUIRE_EQ(0, close(pathfd));
478 	ATF_REQUIRE_EQ(0, close(l));
479 }
480 
481 /*
482  * An fdescfs pathname is resolved a single level: the descriptor it names must
483  * be the peer socket itself.  A node naming an O_PATH handle instead -- of the
484  * socket's *file* (VNON -> VSOCK), or of another fdescfs node (VNON -> VNON)
485  * -- is not chased another level, and the connect fails with ENOTSOCK.
486  *
487  * The descriptor is rejected by getsock(), before the vnode behind it is ever
488  * examined, so both indirections fail the same way.
489  */
490 FDESCFS_TC(devfd_indirect);
ATF_TC_BODY(devfd_indirect,tc)491 ATF_TC_BODY(devfd_indirect, tc)
492 {
493 	char path[32], node[32];
494 	int l, s, pathfd, devfdfd;
495 
496 	mount_fdescfs(NULL);
497 	l = mklistener("devfd_ind.sock");
498 	fdpath(node, sizeof(node), l);
499 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
500 
501 	/* A node naming an O_PATH handle of the socket's file. */
502 	ATF_REQUIRE_MSG((pathfd = open("devfd_ind.sock", O_PATH)) >= 0,
503 	    "open(O_PATH): %s", strerror(errno));
504 	fdpath(path, sizeof(path), pathfd);
505 	ATF_REQUIRE_ERRNO(ENOTSOCK, pathconnect(AT_FDCWD, s, path) == -1);
506 	ATF_REQUIRE_EQ(0, close(pathfd));
507 
508 	/* A node naming an O_PATH handle of another such node. */
509 	ATF_REQUIRE_MSG((devfdfd = open(node, O_PATH)) >= 0,
510 	    "open(%s, O_PATH): %s", node, strerror(errno));
511 	fdpath(path, sizeof(path), devfdfd);
512 	ATF_REQUIRE_ERRNO(ENOTSOCK, pathconnect(AT_FDCWD, s, path) == -1);
513 	ATF_REQUIRE_EQ(0, close(devfdfd));
514 
515 	ATF_REQUIRE_EQ(0, close(s));
516 	ATF_REQUIRE_EQ(0, close(l));
517 }
518 
519 /*
520  * The same indirection under a "nodup" mount, which is where the single-level
521  * rule shows its seam: for a descriptor naming a vnode, fdescfs itself
522  * dereferences to that vnode rather than presenting a synthetic node, so the
523  * O_PATH handle of the socket's file resolves to the bound socket and the
524  * connect succeeds.  Whether the O_PATH is followed is the mount's business;
525  * resolving no more than one descriptor is ours.
526  */
527 FDESCFS_TC(devfd_indirect_nodup);
ATF_TC_BODY(devfd_indirect_nodup,tc)528 ATF_TC_BODY(devfd_indirect_nodup, tc)
529 {
530 	static const char * const opts[] = { "nodup", NULL };
531 	char path[32];
532 	int l, s, a, pathfd;
533 
534 	mount_fdescfs(opts);
535 	l = mklistener("devfd_nodup.sock");
536 
537 	ATF_REQUIRE_MSG((pathfd = open("devfd_nodup.sock", O_PATH)) >= 0,
538 	    "open(O_PATH): %s", strerror(errno));
539 	fdpath(path, sizeof(path), pathfd);
540 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
541 	ATF_REQUIRE_EQ(0, pathconnect(AT_FDCWD, s, path));
542 	ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
543 
544 	ATF_REQUIRE_EQ(0, close(a));
545 	ATF_REQUIRE_EQ(0, close(s));
546 	ATF_REQUIRE_EQ(0, close(pathfd));
547 	ATF_REQUIRE_EQ(0, close(l));
548 }
549 
550 /*
551  * The mount modes differ in how fdescfs presents a descriptor node, which
552  * decides whether the node can name a peer at all:
553  *
554  *	(plain)			VNON node, dup semantics	connects
555  *	nodup			VNON node, since a socket is	connects
556  *				not a vnode descriptor
557  *	linrdlnk		VNON node, readlink for the	connects
558  *				Linux ABI
559  *	rdlnk			VLNK node, followed by namei	fails
560  *
561  * Only rdlnk makes the node a real symlink, and namei() then follows it;
562  * fdesc_readlink() has no path to offer for a socket, so the lookup ends on
563  * its "anon_inode:[unknown]" placeholder instead of the peer.  nodup composes
564  * with either readlink mode without changing this: it only redirects
565  * descriptors that name a vnode, which a socket descriptor does not.
566  */
567 /*
568  * Mount fdescfs with 'opts' and connect to a listener through its node.
569  * 'error' is 0 if the connect must reach the peer, otherwise the errno it
570  * must fail with.
571  */
572 static void
devfd_mode(const char * const * opts,int error)573 devfd_mode(const char * const *opts, int error)
574 {
575 	char path[32];
576 	int l, s, a, ret;
577 
578 	mount_fdescfs(opts);
579 	l = mklistener("mode.sock");
580 	fdpath(path, sizeof(path), l);
581 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
582 
583 	ret = pathconnect(AT_FDCWD, s, path);
584 	if (error == 0) {
585 		ATF_REQUIRE_MSG(ret == 0, "connect: %s", strerror(errno));
586 		ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
587 		ATF_REQUIRE_EQ(0, close(a));
588 	} else {
589 		ATF_REQUIRE_MSG(ret == -1 && errno == error,
590 		    "expected %s, got %s", strerror(error),
591 		    ret == 0 ? "success" : strerror(errno));
592 	}
593 
594 	ATF_REQUIRE_EQ(0, close(s));
595 	ATF_REQUIRE_EQ(0, close(l));
596 }
597 
598 /* Dup semantics, the plain mount: the node names the descriptor. */
599 FDESCFS_TC(devfd_mode_plain);
ATF_TC_BODY(devfd_mode_plain,tc)600 ATF_TC_BODY(devfd_mode_plain, tc)
601 {
602 	static const char * const opts[] = { NULL };
603 
604 	devfd_mode(opts, 0);
605 }
606 
607 /* nodup only redirects descriptors that name a vnode, which a socket is not. */
608 FDESCFS_TC(devfd_mode_nodup);
ATF_TC_BODY(devfd_mode_nodup,tc)609 ATF_TC_BODY(devfd_mode_nodup, tc)
610 {
611 	static const char * const opts[] = { "nodup", NULL };
612 
613 	devfd_mode(opts, 0);
614 }
615 
616 /* linrdlnk only adds readlink for the Linux ABI; the node stays VNON. */
617 FDESCFS_TC(devfd_mode_linrdlnk);
ATF_TC_BODY(devfd_mode_linrdlnk,tc)618 ATF_TC_BODY(devfd_mode_linrdlnk, tc)
619 {
620 	static const char * const opts[] = { "linrdlnk", NULL };
621 
622 	devfd_mode(opts, 0);
623 }
624 
625 FDESCFS_TC(devfd_mode_nodup_linrdlnk);
ATF_TC_BODY(devfd_mode_nodup_linrdlnk,tc)626 ATF_TC_BODY(devfd_mode_nodup_linrdlnk, tc)
627 {
628 	static const char * const opts[] = { "nodup", "linrdlnk", NULL };
629 
630 	devfd_mode(opts, 0);
631 }
632 
633 /*
634  * rdlnk makes the node a real symlink, which namei() follows.
635  * fdesc_readlink() has no path to offer for a socket, so the lookup ends on
636  * its "anon_inode:[unknown]" placeholder rather than the peer.
637  */
638 FDESCFS_TC(devfd_mode_rdlnk);
ATF_TC_BODY(devfd_mode_rdlnk,tc)639 ATF_TC_BODY(devfd_mode_rdlnk, tc)
640 {
641 	static const char * const opts[] = { "rdlnk", NULL };
642 
643 	devfd_mode(opts, ENOENT);
644 }
645 
646 FDESCFS_TC(devfd_mode_nodup_rdlnk);
ATF_TC_BODY(devfd_mode_nodup_rdlnk,tc)647 ATF_TC_BODY(devfd_mode_nodup_rdlnk, tc)
648 {
649 	static const char * const opts[] = { "nodup", "rdlnk", NULL };
650 
651 	devfd_mode(opts, ENOENT);
652 }
653 
654 /* An empty path is only meaningful with a real descriptor. */
655 ATF_TC_WITHOUT_HEAD(empty_path_at_fdcwd);
ATF_TC_BODY(empty_path_at_fdcwd,tc)656 ATF_TC_BODY(empty_path_at_fdcwd, tc)
657 {
658 	int s;
659 
660 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
661 	ATF_REQUIRE_ERRNO(EINVAL, connect(s,
662 	    (const struct sockaddr *)&empty_sun, empty_sun.sun_len) == -1);
663 	ATF_REQUIRE_ERRNO(EINVAL, fdconnect(AT_FDCWD, s) == -1);
664 	ATF_REQUIRE_EQ(0, close(s));
665 }
666 
667 /*
668  * A NUL-leading path with a nonzero length is not the empty-path
669  * extension: connect(2) and connectat(2) with AT_FDCWD must perform a
670  * pathname lookup and fail with ENOENT, not treat AT_FDCWD as a peer
671  * descriptor and fail with EBADF.
672  *
673  * Such addresses occur in the wild: they name Linux abstract namespace
674  * sockets, and the linuxulator passes them through with the leading NUL
675  * intact.  libxcb tries the abstract X11 socket first and falls back to
676  * the pathname socket only on ENOENT or ECONNREFUSED, so when connect(2)
677  * briefly returned EBADF here, every Linux X11 client on the linuxulator
678  * failed at startup with "Missing X server or $DISPLAY".
679  */
680 ATF_TC_WITHOUT_HEAD(nul_path_at_fdcwd);
ATF_TC_BODY(nul_path_at_fdcwd,tc)681 ATF_TC_BODY(nul_path_at_fdcwd, tc)
682 {
683 	int s;
684 
685 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
686 	ATF_REQUIRE_ERRNO(ENOENT, connect(s,
687 	    (const struct sockaddr *)&nul_sun, nul_sun.sun_len) == -1);
688 	ATF_REQUIRE_ERRNO(ENOENT, connectat(AT_FDCWD, s,
689 	    (const struct sockaddr *)&nul_sun, nul_sun.sun_len) == -1);
690 	ATF_REQUIRE_EQ(0, close(s));
691 }
692 
693 /* Error matrix for unsuitable descriptors and peers. */
694 ATF_TC_WITHOUT_HEAD(bad_peers);
ATF_TC_BODY(bad_peers,tc)695 ATF_TC_BODY(bad_peers, tc)
696 {
697 	int s, d, fd;
698 
699 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
700 	ATF_REQUIRE((d = socket(PF_UNIX, SOCK_DGRAM, 0)) >= 0);
701 
702 	/* Non-socket descriptor. */
703 	ATF_REQUIRE((fd = open(".", O_RDONLY)) >= 0);
704 	ATF_REQUIRE_ERRNO(ENOTSOCK, fdconnect(fd, s) == -1);
705 	ATF_REQUIRE_EQ(0, close(fd));
706 
707 	/* Socket from another domain. */
708 	ATF_REQUIRE((fd = socket(PF_INET, SOCK_STREAM, 0)) >= 0);
709 	ATF_REQUIRE_ERRNO(EPROTOTYPE, fdconnect(fd, s) == -1);
710 	ATF_REQUIRE_EQ(0, close(fd));
711 
712 	/* Type mismatch between the two unix sockets. */
713 	fd = mklistener("mismatch.sock");
714 	ATF_REQUIRE_ERRNO(EPROTOTYPE, fdconnect(fd, d) == -1);
715 
716 	ATF_REQUIRE_EQ(0, close(fd));
717 
718 	/* Stream peer that is not listening: 's' never called listen(2). */
719 	ATF_REQUIRE((fd = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
720 	ATF_REQUIRE_ERRNO(ECONNREFUSED, fdconnect(s, fd) == -1);
721 
722 	ATF_REQUIRE_EQ(0, close(fd));
723 	ATF_REQUIRE_EQ(0, close(d));
724 	ATF_REQUIRE_EQ(0, close(s));
725 }
726 
727 /*
728  * A descriptor limited to CAP_CONNECTAT is a pure connect-to-me token:
729  * it can be connected to, but not listened on, accepted from, or read.
730  */
731 ATF_TC_WITHOUT_HEAD(cap_connectat);
ATF_TC_BODY(cap_connectat,tc)732 ATF_TC_BODY(cap_connectat, tc)
733 {
734 	cap_rights_t rights;
735 	char buf[8];
736 	int l, s, token, a;
737 
738 	l = mklistener("cap.sock");
739 	ATF_REQUIRE((token = dup(l)) >= 0);
740 	ATF_REQUIRE_EQ(0, cap_rights_limit(token,
741 	    cap_rights_init(&rights, CAP_CONNECTAT)));
742 
743 	ATF_REQUIRE_ERRNO(ENOTCAPABLE, listen(token, 1) == -1);
744 	ATF_REQUIRE_ERRNO(ENOTCAPABLE, accept(token, NULL, NULL) == -1);
745 	ATF_REQUIRE_ERRNO(ENOTCAPABLE, read(token, buf, sizeof(buf)) == -1);
746 
747 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
748 	ATF_REQUIRE_EQ(0, fdconnect(token, s));
749 	ATF_REQUIRE((a = accept(l, NULL, NULL)) >= 0);
750 
751 	ATF_REQUIRE_EQ(0, close(a));
752 	ATF_REQUIRE_EQ(0, close(s));
753 	ATF_REQUIRE_EQ(0, close(token));
754 	ATF_REQUIRE_EQ(0, close(l));
755 }
756 
757 /* Without CAP_CONNECTAT, the descriptor cannot be a connect target. */
758 ATF_TC_WITHOUT_HEAD(cap_connectat_denied);
ATF_TC_BODY(cap_connectat_denied,tc)759 ATF_TC_BODY(cap_connectat_denied, tc)
760 {
761 	cap_rights_t rights;
762 	int l, s, token;
763 
764 	l = mklistener("capdeny.sock");
765 	ATF_REQUIRE((token = dup(l)) >= 0);
766 	ATF_REQUIRE_EQ(0, cap_rights_limit(token,
767 	    cap_rights_init(&rights, CAP_READ, CAP_WRITE)));
768 
769 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
770 	ATF_REQUIRE_ERRNO(ENOTCAPABLE, fdconnect(token, s) == -1);
771 
772 	ATF_REQUIRE_EQ(0, close(s));
773 	ATF_REQUIRE_EQ(0, close(token));
774 	ATF_REQUIRE_EQ(0, close(l));
775 }
776 
ATF_TP_ADD_TCS(tp)777 ATF_TP_ADD_TCS(tp)
778 {
779 	ATF_TP_ADD_TC(tp, stream);
780 	ATF_TP_ADD_TC(tp, stream_bound);
781 	ATF_TP_ADD_TC(tp, listen_unbound);
782 	ATF_TP_ADD_TC(tp, bind_after_listen);
783 	ATF_TP_ADD_TC(tp, listen_after_disconnect);
784 	ATF_TP_ADD_TC(tp, dgram);
785 	ATF_TP_ADD_TC(tp, empty_path_vnode);
786 	ATF_TP_ADD_TC(tp, path);
787 	ATF_TP_ADD_TC(tp, devfd);
788 	ATF_TP_ADD_TC(tp, devfd_relative);
789 	ATF_TP_ADD_TC(tp, empty_path_devfd);
790 	ATF_TP_ADD_TC(tp, devfd_indirect);
791 	ATF_TP_ADD_TC(tp, devfd_indirect_nodup);
792 	ATF_TP_ADD_TC(tp, devfd_mode_plain);
793 	ATF_TP_ADD_TC(tp, devfd_mode_nodup);
794 	ATF_TP_ADD_TC(tp, devfd_mode_linrdlnk);
795 	ATF_TP_ADD_TC(tp, devfd_mode_nodup_linrdlnk);
796 	ATF_TP_ADD_TC(tp, devfd_mode_rdlnk);
797 	ATF_TP_ADD_TC(tp, devfd_mode_nodup_rdlnk);
798 	ATF_TP_ADD_TC(tp, empty_path_at_fdcwd);
799 	ATF_TP_ADD_TC(tp, nul_path_at_fdcwd);
800 	ATF_TP_ADD_TC(tp, bad_peers);
801 	ATF_TP_ADD_TC(tp, cap_connectat);
802 	ATF_TP_ADD_TC(tp, cap_connectat_denied);
803 
804 	return (atf_no_error());
805 }
806