File Index
Files and directories
Sign in
FILE

scm_rights.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/net/af_unix/scm_rights.c C
  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
 15FIXTURE(scm_rights)
 16{
 17	int fd[32];
 18};
 19
 20FIXTURE_VARIANT(scm_rights)
 21{
 22	char name[32];
 23	int type;
 24	int flags;
 25	bool test_listener;
 26	bool disabled;
 27};
 28
 29FIXTURE_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
 38FIXTURE_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
 47FIXTURE_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
 56FIXTURE_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
 65FIXTURE_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
 74FIXTURE_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
 83FIXTURE_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
 92FIXTURE_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
101FIXTURE_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
110FIXTURE_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
119static 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
152FIXTURE_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
166FIXTURE_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
179static 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
219static 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
240static 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
253static 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
267void __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
317TEST_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
328TEST_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
343TEST_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
362TEST_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
381TEST_HARNESS_MAIN
~/ Read-only file preview.