xref: /linux/tools/testing/selftests/net/af_unix/scm_rights.c (revision 1b98f357dadd6ea613a435fbaef1a5dd7b35fd21)
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 	struct {
275 		struct cmsghdr cmsghdr;
276 		int fd[2];
277 	} cmsg = {
278 		.cmsghdr = {
279 			.cmsg_len = CMSG_LEN(sizeof(cmsg.fd)),
280 			.cmsg_level = SOL_SOCKET,
281 			.cmsg_type = SCM_RIGHTS,
282 		},
283 		.fd = {
284 			self->fd[inflight * 2],
285 			self->fd[inflight * 2],
286 		},
287 	};
288 	struct iovec iov = {
289 		.iov_base = MSG,
290 		.iov_len = MSGLEN,
291 	};
292 	struct msghdr msg = {
293 		.msg_name = NULL,
294 		.msg_namelen = 0,
295 		.msg_iov = &iov,
296 		.msg_iovlen = 1,
297 		.msg_control = &cmsg,
298 		.msg_controllen = CMSG_SPACE(sizeof(cmsg.fd)),
299 	};
300 	int ret;
301 
302 	ret = sendmsg(self->fd[receiver * 2 + 1], &msg, variant->flags);
303 
304 	if (variant->disabled) {
305 		ASSERT_EQ(-1, ret);
306 		ASSERT_EQ(-EPERM, -errno);
307 	} else {
308 		ASSERT_EQ(MSGLEN, ret);
309 	}
310 }
311 
312 #define create_sockets(n)					\
313 	__create_sockets(_metadata, self, variant, n)
314 #define close_sockets(n)					\
315 	__close_sockets(_metadata, self, n)
316 #define send_fd(inflight, receiver)				\
317 	__send_fd(_metadata, self, variant, inflight, receiver)
318 
319 TEST_F(scm_rights, self_ref)
320 {
321 	create_sockets(2);
322 
323 	send_fd(0, 0);
324 
325 	send_fd(1, 1);
326 
327 	close_sockets(2);
328 }
329 
330 TEST_F(scm_rights, triangle)
331 {
332 	create_sockets(6);
333 
334 	send_fd(0, 1);
335 	send_fd(1, 2);
336 	send_fd(2, 0);
337 
338 	send_fd(3, 4);
339 	send_fd(4, 5);
340 	send_fd(5, 3);
341 
342 	close_sockets(6);
343 }
344 
345 TEST_F(scm_rights, cross_edge)
346 {
347 	create_sockets(8);
348 
349 	send_fd(0, 1);
350 	send_fd(1, 2);
351 	send_fd(2, 0);
352 	send_fd(1, 3);
353 	send_fd(3, 2);
354 
355 	send_fd(4, 5);
356 	send_fd(5, 6);
357 	send_fd(6, 4);
358 	send_fd(5, 7);
359 	send_fd(7, 6);
360 
361 	close_sockets(8);
362 }
363 
364 TEST_F(scm_rights, backtrack_from_scc)
365 {
366 	create_sockets(10);
367 
368 	send_fd(0, 1);
369 	send_fd(0, 4);
370 	send_fd(1, 2);
371 	send_fd(2, 3);
372 	send_fd(3, 1);
373 
374 	send_fd(5, 6);
375 	send_fd(5, 9);
376 	send_fd(6, 7);
377 	send_fd(7, 8);
378 	send_fd(8, 6);
379 
380 	close_sockets(10);
381 }
382 
383 TEST_HARNESS_MAIN
384