xref: /linux/tools/testing/selftests/net/af_unix/scm_rights.c (revision d2b007374551ac09db16badde575cdd698f6fc92)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright Amazon.com Inc. or its affiliates. */
3 #define _GNU_SOURCE
4 #include <sched.h>
5 
6 #include <stdio.h>
7 #include <string.h>
8 #include <unistd.h>
9 #include <sys/types.h>
10 #include <sys/socket.h>
11 #include <sys/un.h>
12 
13 #include "../../kselftest_harness.h"
14 
15 FIXTURE(scm_rights)
16 {
17 	int fd[32];
18 };
19 
20 FIXTURE_VARIANT(scm_rights)
21 {
22 	char name[32];
23 	int type;
24 	int flags;
25 	bool test_listener;
26 	bool disabled;
27 };
28 
29 FIXTURE_VARIANT_ADD(scm_rights, dgram)
30 {
31 	.name = "UNIX ",
32 	.type = SOCK_DGRAM,
33 	.flags = 0,
34 	.test_listener = false,
35 	.disabled = false,
36 };
37 
38 FIXTURE_VARIANT_ADD(scm_rights, dgram_disabled)
39 {
40 	.name = "UNIX ",
41 	.type = SOCK_DGRAM,
42 	.flags = 0,
43 	.test_listener = false,
44 	.disabled = true,
45 };
46 
47 FIXTURE_VARIANT_ADD(scm_rights, stream)
48 {
49 	.name = "UNIX-STREAM ",
50 	.type = SOCK_STREAM,
51 	.flags = 0,
52 	.test_listener = false,
53 	.disabled = false,
54 };
55 
56 FIXTURE_VARIANT_ADD(scm_rights, stream_disabled)
57 {
58 	.name = "UNIX-STREAM ",
59 	.type = SOCK_STREAM,
60 	.flags = 0,
61 	.test_listener = false,
62 	.disabled = true,
63 };
64 
65 FIXTURE_VARIANT_ADD(scm_rights, stream_oob)
66 {
67 	.name = "UNIX-STREAM ",
68 	.type = SOCK_STREAM,
69 	.flags = MSG_OOB,
70 	.test_listener = false,
71 	.disabled = false,
72 };
73 
74 FIXTURE_VARIANT_ADD(scm_rights, stream_oob_disabled)
75 {
76 	.name = "UNIX-STREAM ",
77 	.type = SOCK_STREAM,
78 	.flags = MSG_OOB,
79 	.test_listener = false,
80 	.disabled = true,
81 };
82 
83 FIXTURE_VARIANT_ADD(scm_rights, stream_listener)
84 {
85 	.name = "UNIX-STREAM ",
86 	.type = SOCK_STREAM,
87 	.flags = 0,
88 	.test_listener = true,
89 	.disabled = false,
90 };
91 
92 FIXTURE_VARIANT_ADD(scm_rights, stream_listener_disabled)
93 {
94 	.name = "UNIX-STREAM ",
95 	.type = SOCK_STREAM,
96 	.flags = 0,
97 	.test_listener = true,
98 	.disabled = true,
99 };
100 
101 FIXTURE_VARIANT_ADD(scm_rights, stream_listener_oob)
102 {
103 	.name = "UNIX-STREAM ",
104 	.type = SOCK_STREAM,
105 	.flags = MSG_OOB,
106 	.test_listener = true,
107 	.disabled = false,
108 };
109 
110 FIXTURE_VARIANT_ADD(scm_rights, stream_listener_oob_disabled)
111 {
112 	.name = "UNIX-STREAM ",
113 	.type = SOCK_STREAM,
114 	.flags = MSG_OOB,
115 	.test_listener = true,
116 	.disabled = true,
117 };
118 
119 static int count_sockets(struct __test_metadata *_metadata,
120 			 const FIXTURE_VARIANT(scm_rights) *variant)
121 {
122 	int sockets = -1, len, ret;
123 	char *line = NULL;
124 	size_t unused;
125 	FILE *f;
126 
127 	f = fopen("/proc/net/protocols", "r");
128 	ASSERT_NE(NULL, f);
129 
130 	len = strlen(variant->name);
131 
132 	while (getline(&line, &unused, f) != -1) {
133 		int unused2;
134 
135 		if (strncmp(line, variant->name, len))
136 			continue;
137 
138 		ret = sscanf(line + len, "%d %d", &unused2, &sockets);
139 		ASSERT_EQ(2, ret);
140 
141 		break;
142 	}
143 
144 	free(line);
145 
146 	ret = fclose(f);
147 	ASSERT_EQ(0, ret);
148 
149 	return sockets;
150 }
151 
152 FIXTURE_SETUP(scm_rights)
153 {
154 	int ret;
155 
156 	ret = unshare(CLONE_NEWNET);
157 	ASSERT_EQ(0, ret);
158 
159 	if (variant->disabled)
160 		return;
161 
162 	ret = count_sockets(_metadata, variant);
163 	ASSERT_EQ(0, ret);
164 }
165 
166 FIXTURE_TEARDOWN(scm_rights)
167 {
168 	int ret;
169 
170 	if (variant->disabled)
171 		return;
172 
173 	sleep(1);
174 
175 	ret = count_sockets(_metadata, variant);
176 	ASSERT_EQ(0, ret);
177 }
178 
179 static void create_listeners(struct __test_metadata *_metadata,
180 			     FIXTURE_DATA(scm_rights) *self,
181 			     const FIXTURE_VARIANT(scm_rights) *variant,
182 			     int n)
183 {
184 	struct sockaddr_un addr = {
185 		.sun_family = AF_UNIX,
186 	};
187 	socklen_t addrlen;
188 	int i, ret;
189 
190 	for (i = 0; i < n * 2; i += 2) {
191 		self->fd[i] = socket(AF_UNIX, SOCK_STREAM, 0);
192 		ASSERT_LE(0, self->fd[i]);
193 
194 		addrlen = sizeof(addr.sun_family);
195 		ret = bind(self->fd[i], (struct sockaddr *)&addr, addrlen);
196 		ASSERT_EQ(0, ret);
197 
198 		ret = listen(self->fd[i], -1);
199 		ASSERT_EQ(0, ret);
200 
201 		if (variant->disabled) {
202 			ret = setsockopt(self->fd[i], SOL_SOCKET, SO_PASSRIGHTS,
203 					 &(int){0}, sizeof(int));
204 			ASSERT_EQ(0, ret);
205 		}
206 
207 		addrlen = sizeof(addr);
208 		ret = getsockname(self->fd[i], (struct sockaddr *)&addr, &addrlen);
209 		ASSERT_EQ(0, ret);
210 
211 		self->fd[i + 1] = socket(AF_UNIX, SOCK_STREAM, 0);
212 		ASSERT_LE(0, self->fd[i + 1]);
213 
214 		ret = connect(self->fd[i + 1], (struct sockaddr *)&addr, addrlen);
215 		ASSERT_EQ(0, ret);
216 	}
217 }
218 
219 static void create_socketpairs(struct __test_metadata *_metadata,
220 			       FIXTURE_DATA(scm_rights) *self,
221 			       const FIXTURE_VARIANT(scm_rights) *variant,
222 			       int n)
223 {
224 	int i, ret;
225 
226 	ASSERT_GE(sizeof(self->fd) / sizeof(int), n);
227 
228 	for (i = 0; i < n * 2; i += 2) {
229 		ret = socketpair(AF_UNIX, variant->type, 0, self->fd + i);
230 		ASSERT_EQ(0, ret);
231 
232 		if (variant->disabled) {
233 			ret = setsockopt(self->fd[i], SOL_SOCKET, SO_PASSRIGHTS,
234 					 &(int){0}, sizeof(int));
235 			ASSERT_EQ(0, ret);
236 		}
237 	}
238 }
239 
240 static void __create_sockets(struct __test_metadata *_metadata,
241 			     FIXTURE_DATA(scm_rights) *self,
242 			     const FIXTURE_VARIANT(scm_rights) *variant,
243 			     int n)
244 {
245 	ASSERT_LE(n * 2, sizeof(self->fd) / sizeof(self->fd[0]));
246 
247 	if (variant->test_listener)
248 		create_listeners(_metadata, self, variant, n);
249 	else
250 		create_socketpairs(_metadata, self, variant, n);
251 }
252 
253 static void __close_sockets(struct __test_metadata *_metadata,
254 			    FIXTURE_DATA(scm_rights) *self,
255 			    int n)
256 {
257 	int i, ret;
258 
259 	ASSERT_GE(sizeof(self->fd) / sizeof(int), n);
260 
261 	for (i = 0; i < n * 2; i++) {
262 		ret = close(self->fd[i]);
263 		ASSERT_EQ(0, ret);
264 	}
265 }
266 
267 void __send_fd(struct __test_metadata *_metadata,
268 	       const FIXTURE_DATA(scm_rights) *self,
269 	       const FIXTURE_VARIANT(scm_rights) *variant,
270 	       int inflight, int receiver)
271 {
272 #define MSG "x"
273 #define MSGLEN 1
274 	int fds[2] = {
275 		self->fd[inflight * 2],
276 		self->fd[inflight * 2],
277 	};
278 	char cmsg_buf[CMSG_SPACE(sizeof(fds))];
279 	struct iovec iov = {
280 		.iov_base = MSG,
281 		.iov_len = MSGLEN,
282 	};
283 	struct msghdr msg = {
284 		.msg_name = NULL,
285 		.msg_namelen = 0,
286 		.msg_iov = &iov,
287 		.msg_iovlen = 1,
288 		.msg_control = cmsg_buf,
289 		.msg_controllen = sizeof(cmsg_buf),
290 	};
291 	struct cmsghdr *cmsg;
292 	int ret;
293 
294 	cmsg = CMSG_FIRSTHDR(&msg);
295 	cmsg->cmsg_level = SOL_SOCKET;
296 	cmsg->cmsg_type = SCM_RIGHTS;
297 	cmsg->cmsg_len = CMSG_LEN(sizeof(fds));
298 	memcpy(CMSG_DATA(cmsg), fds, sizeof(fds));
299 
300 	ret = sendmsg(self->fd[receiver * 2 + 1], &msg, variant->flags);
301 
302 	if (variant->disabled) {
303 		ASSERT_EQ(-1, ret);
304 		ASSERT_EQ(-EPERM, -errno);
305 	} else {
306 		ASSERT_EQ(MSGLEN, ret);
307 	}
308 }
309 
310 #define create_sockets(n)					\
311 	__create_sockets(_metadata, self, variant, n)
312 #define close_sockets(n)					\
313 	__close_sockets(_metadata, self, n)
314 #define send_fd(inflight, receiver)				\
315 	__send_fd(_metadata, self, variant, inflight, receiver)
316 
317 TEST_F(scm_rights, self_ref)
318 {
319 	create_sockets(2);
320 
321 	send_fd(0, 0);
322 
323 	send_fd(1, 1);
324 
325 	close_sockets(2);
326 }
327 
328 TEST_F(scm_rights, triangle)
329 {
330 	create_sockets(6);
331 
332 	send_fd(0, 1);
333 	send_fd(1, 2);
334 	send_fd(2, 0);
335 
336 	send_fd(3, 4);
337 	send_fd(4, 5);
338 	send_fd(5, 3);
339 
340 	close_sockets(6);
341 }
342 
343 TEST_F(scm_rights, cross_edge)
344 {
345 	create_sockets(8);
346 
347 	send_fd(0, 1);
348 	send_fd(1, 2);
349 	send_fd(2, 0);
350 	send_fd(1, 3);
351 	send_fd(3, 2);
352 
353 	send_fd(4, 5);
354 	send_fd(5, 6);
355 	send_fd(6, 4);
356 	send_fd(5, 7);
357 	send_fd(7, 6);
358 
359 	close_sockets(8);
360 }
361 
362 TEST_F(scm_rights, backtrack_from_scc)
363 {
364 	create_sockets(10);
365 
366 	send_fd(0, 1);
367 	send_fd(0, 4);
368 	send_fd(1, 2);
369 	send_fd(2, 3);
370 	send_fd(3, 1);
371 
372 	send_fd(5, 6);
373 	send_fd(5, 9);
374 	send_fd(6, 7);
375 	send_fd(7, 8);
376 	send_fd(8, 6);
377 
378 	close_sockets(10);
379 }
380 
381 TEST_HARNESS_MAIN
382