308 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			308 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| /* Copyright Amazon.com Inc. or its affiliates. */
 | |
| #define _GNU_SOURCE
 | |
| #include <sched.h>
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <string.h>
 | |
| #include <unistd.h>
 | |
| #include <sys/types.h>
 | |
| #include <sys/socket.h>
 | |
| #include <sys/un.h>
 | |
| 
 | |
| #include "../../kselftest_harness.h"
 | |
| 
 | |
| FIXTURE(scm_rights)
 | |
| {
 | |
| 	int fd[32];
 | |
| };
 | |
| 
 | |
| FIXTURE_VARIANT(scm_rights)
 | |
| {
 | |
| 	char name[32];
 | |
| 	int type;
 | |
| 	int flags;
 | |
| 	bool test_listener;
 | |
| };
 | |
| 
 | |
| FIXTURE_VARIANT_ADD(scm_rights, dgram)
 | |
| {
 | |
| 	.name = "UNIX ",
 | |
| 	.type = SOCK_DGRAM,
 | |
| 	.flags = 0,
 | |
| 	.test_listener = false,
 | |
| };
 | |
| 
 | |
| FIXTURE_VARIANT_ADD(scm_rights, stream)
 | |
| {
 | |
| 	.name = "UNIX-STREAM ",
 | |
| 	.type = SOCK_STREAM,
 | |
| 	.flags = 0,
 | |
| 	.test_listener = false,
 | |
| };
 | |
| 
 | |
| FIXTURE_VARIANT_ADD(scm_rights, stream_oob)
 | |
| {
 | |
| 	.name = "UNIX-STREAM ",
 | |
| 	.type = SOCK_STREAM,
 | |
| 	.flags = MSG_OOB,
 | |
| 	.test_listener = false,
 | |
| };
 | |
| 
 | |
| FIXTURE_VARIANT_ADD(scm_rights, stream_listener)
 | |
| {
 | |
| 	.name = "UNIX-STREAM ",
 | |
| 	.type = SOCK_STREAM,
 | |
| 	.flags = 0,
 | |
| 	.test_listener = true,
 | |
| };
 | |
| 
 | |
| FIXTURE_VARIANT_ADD(scm_rights, stream_listener_oob)
 | |
| {
 | |
| 	.name = "UNIX-STREAM ",
 | |
| 	.type = SOCK_STREAM,
 | |
| 	.flags = MSG_OOB,
 | |
| 	.test_listener = true,
 | |
| };
 | |
| 
 | |
| static int count_sockets(struct __test_metadata *_metadata,
 | |
| 			 const FIXTURE_VARIANT(scm_rights) *variant)
 | |
| {
 | |
| 	int sockets = -1, len, ret;
 | |
| 	char *line = NULL;
 | |
| 	size_t unused;
 | |
| 	FILE *f;
 | |
| 
 | |
| 	f = fopen("/proc/net/protocols", "r");
 | |
| 	ASSERT_NE(NULL, f);
 | |
| 
 | |
| 	len = strlen(variant->name);
 | |
| 
 | |
| 	while (getline(&line, &unused, f) != -1) {
 | |
| 		int unused2;
 | |
| 
 | |
| 		if (strncmp(line, variant->name, len))
 | |
| 			continue;
 | |
| 
 | |
| 		ret = sscanf(line + len, "%d %d", &unused2, &sockets);
 | |
| 		ASSERT_EQ(2, ret);
 | |
| 
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	free(line);
 | |
| 
 | |
| 	ret = fclose(f);
 | |
| 	ASSERT_EQ(0, ret);
 | |
| 
 | |
| 	return sockets;
 | |
| }
 | |
| 
 | |
| FIXTURE_SETUP(scm_rights)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = unshare(CLONE_NEWNET);
 | |
| 	ASSERT_EQ(0, ret);
 | |
| 
 | |
| 	ret = count_sockets(_metadata, variant);
 | |
| 	ASSERT_EQ(0, ret);
 | |
| }
 | |
| 
 | |
| FIXTURE_TEARDOWN(scm_rights)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	sleep(1);
 | |
| 
 | |
| 	ret = count_sockets(_metadata, variant);
 | |
| 	ASSERT_EQ(0, ret);
 | |
| }
 | |
| 
 | |
| static void create_listeners(struct __test_metadata *_metadata,
 | |
| 			     FIXTURE_DATA(scm_rights) *self,
 | |
| 			     int n)
 | |
| {
 | |
| 	struct sockaddr_un addr = {
 | |
| 		.sun_family = AF_UNIX,
 | |
| 	};
 | |
| 	socklen_t addrlen;
 | |
| 	int i, ret;
 | |
| 
 | |
| 	for (i = 0; i < n * 2; i += 2) {
 | |
| 		self->fd[i] = socket(AF_UNIX, SOCK_STREAM, 0);
 | |
| 		ASSERT_LE(0, self->fd[i]);
 | |
| 
 | |
| 		addrlen = sizeof(addr.sun_family);
 | |
| 		ret = bind(self->fd[i], (struct sockaddr *)&addr, addrlen);
 | |
| 		ASSERT_EQ(0, ret);
 | |
| 
 | |
| 		ret = listen(self->fd[i], -1);
 | |
| 		ASSERT_EQ(0, ret);
 | |
| 
 | |
| 		addrlen = sizeof(addr);
 | |
| 		ret = getsockname(self->fd[i], (struct sockaddr *)&addr, &addrlen);
 | |
| 		ASSERT_EQ(0, ret);
 | |
| 
 | |
| 		self->fd[i + 1] = socket(AF_UNIX, SOCK_STREAM, 0);
 | |
| 		ASSERT_LE(0, self->fd[i + 1]);
 | |
| 
 | |
| 		ret = connect(self->fd[i + 1], (struct sockaddr *)&addr, addrlen);
 | |
| 		ASSERT_EQ(0, ret);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void create_socketpairs(struct __test_metadata *_metadata,
 | |
| 			       FIXTURE_DATA(scm_rights) *self,
 | |
| 			       const FIXTURE_VARIANT(scm_rights) *variant,
 | |
| 			       int n)
 | |
| {
 | |
| 	int i, ret;
 | |
| 
 | |
| 	ASSERT_GE(sizeof(self->fd) / sizeof(int), n);
 | |
| 
 | |
| 	for (i = 0; i < n * 2; i += 2) {
 | |
| 		ret = socketpair(AF_UNIX, variant->type, 0, self->fd + i);
 | |
| 		ASSERT_EQ(0, ret);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void __create_sockets(struct __test_metadata *_metadata,
 | |
| 			     FIXTURE_DATA(scm_rights) *self,
 | |
| 			     const FIXTURE_VARIANT(scm_rights) *variant,
 | |
| 			     int n)
 | |
| {
 | |
| 	ASSERT_LE(n * 2, sizeof(self->fd) / sizeof(self->fd[0]));
 | |
| 
 | |
| 	if (variant->test_listener)
 | |
| 		create_listeners(_metadata, self, n);
 | |
| 	else
 | |
| 		create_socketpairs(_metadata, self, variant, n);
 | |
| }
 | |
| 
 | |
| static void __close_sockets(struct __test_metadata *_metadata,
 | |
| 			    FIXTURE_DATA(scm_rights) *self,
 | |
| 			    int n)
 | |
| {
 | |
| 	int i, ret;
 | |
| 
 | |
| 	ASSERT_GE(sizeof(self->fd) / sizeof(int), n);
 | |
| 
 | |
| 	for (i = 0; i < n * 2; i++) {
 | |
| 		ret = close(self->fd[i]);
 | |
| 		ASSERT_EQ(0, ret);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void __send_fd(struct __test_metadata *_metadata,
 | |
| 	       const FIXTURE_DATA(scm_rights) *self,
 | |
| 	       const FIXTURE_VARIANT(scm_rights) *variant,
 | |
| 	       int inflight, int receiver)
 | |
| {
 | |
| #define MSG "x"
 | |
| #define MSGLEN 1
 | |
| 	struct {
 | |
| 		struct cmsghdr cmsghdr;
 | |
| 		int fd[2];
 | |
| 	} cmsg = {
 | |
| 		.cmsghdr = {
 | |
| 			.cmsg_len = CMSG_LEN(sizeof(cmsg.fd)),
 | |
| 			.cmsg_level = SOL_SOCKET,
 | |
| 			.cmsg_type = SCM_RIGHTS,
 | |
| 		},
 | |
| 		.fd = {
 | |
| 			self->fd[inflight * 2],
 | |
| 			self->fd[inflight * 2],
 | |
| 		},
 | |
| 	};
 | |
| 	struct iovec iov = {
 | |
| 		.iov_base = MSG,
 | |
| 		.iov_len = MSGLEN,
 | |
| 	};
 | |
| 	struct msghdr msg = {
 | |
| 		.msg_name = NULL,
 | |
| 		.msg_namelen = 0,
 | |
| 		.msg_iov = &iov,
 | |
| 		.msg_iovlen = 1,
 | |
| 		.msg_control = &cmsg,
 | |
| 		.msg_controllen = CMSG_SPACE(sizeof(cmsg.fd)),
 | |
| 	};
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = sendmsg(self->fd[receiver * 2 + 1], &msg, variant->flags);
 | |
| 	ASSERT_EQ(MSGLEN, ret);
 | |
| }
 | |
| 
 | |
| #define create_sockets(n)					\
 | |
| 	__create_sockets(_metadata, self, variant, n)
 | |
| #define close_sockets(n)					\
 | |
| 	__close_sockets(_metadata, self, n)
 | |
| #define send_fd(inflight, receiver)				\
 | |
| 	__send_fd(_metadata, self, variant, inflight, receiver)
 | |
| 
 | |
| TEST_F(scm_rights, self_ref)
 | |
| {
 | |
| 	create_sockets(2);
 | |
| 
 | |
| 	send_fd(0, 0);
 | |
| 
 | |
| 	send_fd(1, 1);
 | |
| 
 | |
| 	close_sockets(2);
 | |
| }
 | |
| 
 | |
| TEST_F(scm_rights, triangle)
 | |
| {
 | |
| 	create_sockets(6);
 | |
| 
 | |
| 	send_fd(0, 1);
 | |
| 	send_fd(1, 2);
 | |
| 	send_fd(2, 0);
 | |
| 
 | |
| 	send_fd(3, 4);
 | |
| 	send_fd(4, 5);
 | |
| 	send_fd(5, 3);
 | |
| 
 | |
| 	close_sockets(6);
 | |
| }
 | |
| 
 | |
| TEST_F(scm_rights, cross_edge)
 | |
| {
 | |
| 	create_sockets(8);
 | |
| 
 | |
| 	send_fd(0, 1);
 | |
| 	send_fd(1, 2);
 | |
| 	send_fd(2, 0);
 | |
| 	send_fd(1, 3);
 | |
| 	send_fd(3, 2);
 | |
| 
 | |
| 	send_fd(4, 5);
 | |
| 	send_fd(5, 6);
 | |
| 	send_fd(6, 4);
 | |
| 	send_fd(5, 7);
 | |
| 	send_fd(7, 6);
 | |
| 
 | |
| 	close_sockets(8);
 | |
| }
 | |
| 
 | |
| TEST_F(scm_rights, backtrack_from_scc)
 | |
| {
 | |
| 	create_sockets(10);
 | |
| 
 | |
| 	send_fd(0, 1);
 | |
| 	send_fd(0, 4);
 | |
| 	send_fd(1, 2);
 | |
| 	send_fd(2, 3);
 | |
| 	send_fd(3, 1);
 | |
| 
 | |
| 	send_fd(5, 6);
 | |
| 	send_fd(5, 9);
 | |
| 	send_fd(6, 7);
 | |
| 	send_fd(7, 8);
 | |
| 	send_fd(8, 6);
 | |
| 
 | |
| 	close_sockets(10);
 | |
| }
 | |
| 
 | |
| TEST_HARNESS_MAIN
 |