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