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