[PATCH 6/6] selftests/landlock: Add pathname socket variants for more tests

Tingmao Wang m at maowtm.org
Sun Dec 28 12:45:45 UTC 2025


While this produces a lot of change, it does allow us to "simultaneously"
test both abstract and pathname UNIX sockets with reletively little code
duplication, since they are really similar.

Tests touched: scoped_vs_unscoped, outside_socket,
various_address_sockets, datagram_sockets, self_connect.

Signed-off-by: Tingmao Wang <m at maowtm.org>
---
 .../selftests/landlock/scoped_unix_test.c     | 599 ++++++++++++------
 1 file changed, 395 insertions(+), 204 deletions(-)

diff --git a/tools/testing/selftests/landlock/scoped_unix_test.c b/tools/testing/selftests/landlock/scoped_unix_test.c
index 669418c97509..6d1541f77dbe 100644
--- a/tools/testing/selftests/landlock/scoped_unix_test.c
+++ b/tools/testing/selftests/landlock/scoped_unix_test.c
@@ -536,8 +536,12 @@ TEST_F(scoped_audit, connect_to_child)
 
 FIXTURE(scoped_vs_unscoped)
 {
-	struct service_fixture parent_stream_address, parent_dgram_address,
-		child_stream_address, child_dgram_address;
+	struct service_fixture parent_stream_address_abstract,
+		parent_dgram_address_abstract, child_stream_address_abstract,
+		child_dgram_address_abstract;
+	struct service_fixture parent_stream_address_pathname,
+		parent_dgram_address_pathname, child_stream_address_pathname,
+		child_dgram_address_pathname;
 };
 
 #include "scoped_multiple_domain_variants.h"
@@ -546,35 +550,75 @@ FIXTURE_SETUP(scoped_vs_unscoped)
 {
 	drop_caps(_metadata);
 
-	memset(&self->parent_stream_address, 0,
-	       sizeof(self->parent_stream_address));
-	set_unix_address(&self->parent_stream_address, 0, true);
-	memset(&self->parent_dgram_address, 0,
-	       sizeof(self->parent_dgram_address));
-	set_unix_address(&self->parent_dgram_address, 1, true);
-	memset(&self->child_stream_address, 0,
-	       sizeof(self->child_stream_address));
-	set_unix_address(&self->child_stream_address, 2, true);
-	memset(&self->child_dgram_address, 0,
-	       sizeof(self->child_dgram_address));
-	set_unix_address(&self->child_dgram_address, 3, true);
+	ASSERT_EQ(0, mkdir(PATHNAME_UNIX_SOCK_DIR, 0700));
+
+	/* Abstract addresses. */
+	memset(&self->parent_stream_address_abstract, 0,
+	       sizeof(self->parent_stream_address_abstract));
+	set_unix_address(&self->parent_stream_address_abstract, 0, true);
+	memset(&self->parent_dgram_address_abstract, 0,
+	       sizeof(self->parent_dgram_address_abstract));
+	set_unix_address(&self->parent_dgram_address_abstract, 1, true);
+	memset(&self->child_stream_address_abstract, 0,
+	       sizeof(self->child_stream_address_abstract));
+	set_unix_address(&self->child_stream_address_abstract, 2, true);
+	memset(&self->child_dgram_address_abstract, 0,
+	       sizeof(self->child_dgram_address_abstract));
+	set_unix_address(&self->child_dgram_address_abstract, 3, true);
+
+	/* Pathname addresses. */
+	memset(&self->parent_stream_address_pathname, 0,
+	       sizeof(self->parent_stream_address_pathname));
+	set_unix_address(&self->parent_stream_address_pathname, 4, false);
+	memset(&self->parent_dgram_address_pathname, 0,
+	       sizeof(self->parent_dgram_address_pathname));
+	set_unix_address(&self->parent_dgram_address_pathname, 5, false);
+	memset(&self->child_stream_address_pathname, 0,
+	       sizeof(self->child_stream_address_pathname));
+	set_unix_address(&self->child_stream_address_pathname, 6, false);
+	memset(&self->child_dgram_address_pathname, 0,
+	       sizeof(self->child_dgram_address_pathname));
+	set_unix_address(&self->child_dgram_address_pathname, 7, false);
 }
 
 FIXTURE_TEARDOWN(scoped_vs_unscoped)
 {
+	EXPECT_EQ(0, remove_path(self->parent_stream_address_pathname.unix_addr.sun_path));
+	EXPECT_EQ(0, remove_path(self->parent_dgram_address_pathname.unix_addr.sun_path));
+	EXPECT_EQ(0, remove_path(self->child_stream_address_pathname.unix_addr.sun_path));
+	EXPECT_EQ(0, remove_path(self->child_dgram_address_pathname.unix_addr.sun_path));
+	EXPECT_EQ(0, rmdir(PATHNAME_UNIX_SOCK_DIR));
 }
 
 /*
  * Test unix_stream_connect and unix_may_send for parent, child and
  * grand child processes when they can have scoped or non-scoped domains.
  */
-TEST_F(scoped_vs_unscoped, unix_scoping)
+static void test_scoped_vs_unscoped(
+	struct __test_metadata *const _metadata,
+	FIXTURE_DATA(scoped_vs_unscoped) * self,
+	const FIXTURE_VARIANT(scoped_vs_unscoped) * variant,
+	const bool abstract)
 {
 	pid_t child;
 	int status;
 	bool can_connect_to_parent, can_connect_to_child;
 	int pipe_parent[2];
 	int stream_server_parent, dgram_server_parent;
+	const __u16 scope = abstract ? LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET :
+				       LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET;
+	const struct service_fixture *parent_stream_address =
+		abstract ? &self->parent_stream_address_abstract :
+			   &self->parent_stream_address_pathname;
+	const struct service_fixture *parent_dgram_address =
+		abstract ? &self->parent_dgram_address_abstract :
+			   &self->parent_dgram_address_pathname;
+	const struct service_fixture *child_stream_address =
+		abstract ? &self->child_stream_address_abstract :
+			   &self->child_stream_address_pathname;
+	const struct service_fixture *child_dgram_address =
+		abstract ? &self->child_dgram_address_abstract :
+			   &self->child_dgram_address_pathname;
 
 	can_connect_to_child = (variant->domain_grand_child != SCOPE_SANDBOX);
 	can_connect_to_parent = (can_connect_to_child &&
@@ -585,8 +629,7 @@ TEST_F(scoped_vs_unscoped, unix_scoping)
 	if (variant->domain_all == OTHER_SANDBOX)
 		create_fs_domain(_metadata);
 	else if (variant->domain_all == SCOPE_SANDBOX)
-		create_scoped_domain(_metadata,
-				     LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);
+		create_scoped_domain(_metadata, scope);
 
 	child = fork();
 	ASSERT_LE(0, child);
@@ -600,8 +643,7 @@ TEST_F(scoped_vs_unscoped, unix_scoping)
 		if (variant->domain_children == OTHER_SANDBOX)
 			create_fs_domain(_metadata);
 		else if (variant->domain_children == SCOPE_SANDBOX)
-			create_scoped_domain(
-				_metadata, LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);
+			create_scoped_domain(_metadata, scope);
 
 		grand_child = fork();
 		ASSERT_LE(0, grand_child);
@@ -616,9 +658,7 @@ TEST_F(scoped_vs_unscoped, unix_scoping)
 			if (variant->domain_grand_child == OTHER_SANDBOX)
 				create_fs_domain(_metadata);
 			else if (variant->domain_grand_child == SCOPE_SANDBOX)
-				create_scoped_domain(
-					_metadata,
-					LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);
+				create_scoped_domain(_metadata, scope);
 
 			stream_client = socket(AF_UNIX, SOCK_STREAM, 0);
 			ASSERT_LE(0, stream_client);
@@ -626,15 +666,13 @@ TEST_F(scoped_vs_unscoped, unix_scoping)
 			ASSERT_LE(0, dgram_client);
 
 			ASSERT_EQ(1, read(pipe_child[0], &buf, 1));
-			stream_err = connect(
-				stream_client,
-				&self->child_stream_address.unix_addr,
-				self->child_stream_address.unix_addr_len);
+			stream_err = connect(stream_client,
+					     &child_stream_address->unix_addr,
+					     child_stream_address->unix_addr_len);
 			stream_errno = errno;
-			dgram_err = connect(
-				dgram_client,
-				&self->child_dgram_address.unix_addr,
-				self->child_dgram_address.unix_addr_len);
+			dgram_err = connect(dgram_client,
+					    &child_dgram_address->unix_addr,
+					    child_dgram_address->unix_addr_len);
 			dgram_errno = errno;
 			if (can_connect_to_child) {
 				EXPECT_EQ(0, stream_err);
@@ -653,14 +691,12 @@ TEST_F(scoped_vs_unscoped, unix_scoping)
 
 			ASSERT_EQ(1, read(pipe_parent[0], &buf, 1));
 			stream_err = connect(
-				stream_client,
-				&self->parent_stream_address.unix_addr,
-				self->parent_stream_address.unix_addr_len);
+				stream_client, &parent_stream_address->unix_addr,
+				parent_stream_address->unix_addr_len);
 			stream_errno = errno;
 			dgram_err = connect(
-				dgram_client,
-				&self->parent_dgram_address.unix_addr,
-				self->parent_dgram_address.unix_addr_len);
+				dgram_client, &parent_dgram_address->unix_addr,
+				parent_dgram_address->unix_addr_len);
 			dgram_errno = errno;
 			if (can_connect_to_parent) {
 				EXPECT_EQ(0, stream_err);
@@ -681,8 +717,7 @@ TEST_F(scoped_vs_unscoped, unix_scoping)
 		if (variant->domain_child == OTHER_SANDBOX)
 			create_fs_domain(_metadata);
 		else if (variant->domain_child == SCOPE_SANDBOX)
-			create_scoped_domain(
-				_metadata, LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);
+			create_scoped_domain(_metadata, scope);
 
 		stream_server_child = socket(AF_UNIX, SOCK_STREAM, 0);
 		ASSERT_LE(0, stream_server_child);
@@ -690,11 +725,11 @@ TEST_F(scoped_vs_unscoped, unix_scoping)
 		ASSERT_LE(0, dgram_server_child);
 
 		ASSERT_EQ(0, bind(stream_server_child,
-				  &self->child_stream_address.unix_addr,
-				  self->child_stream_address.unix_addr_len));
-		ASSERT_EQ(0, bind(dgram_server_child,
-				  &self->child_dgram_address.unix_addr,
-				  self->child_dgram_address.unix_addr_len));
+				  &child_stream_address->unix_addr,
+				  child_stream_address->unix_addr_len));
+		ASSERT_EQ(0,
+			  bind(dgram_server_child, &child_dgram_address->unix_addr,
+			       child_dgram_address->unix_addr_len));
 		ASSERT_EQ(0, listen(stream_server_child, backlog));
 
 		ASSERT_EQ(1, write(pipe_child[1], ".", 1));
@@ -708,19 +743,16 @@ TEST_F(scoped_vs_unscoped, unix_scoping)
 	if (variant->domain_parent == OTHER_SANDBOX)
 		create_fs_domain(_metadata);
 	else if (variant->domain_parent == SCOPE_SANDBOX)
-		create_scoped_domain(_metadata,
-				     LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);
+		create_scoped_domain(_metadata, scope);
 
 	stream_server_parent = socket(AF_UNIX, SOCK_STREAM, 0);
 	ASSERT_LE(0, stream_server_parent);
 	dgram_server_parent = socket(AF_UNIX, SOCK_DGRAM, 0);
 	ASSERT_LE(0, dgram_server_parent);
-	ASSERT_EQ(0, bind(stream_server_parent,
-			  &self->parent_stream_address.unix_addr,
-			  self->parent_stream_address.unix_addr_len));
-	ASSERT_EQ(0, bind(dgram_server_parent,
-			  &self->parent_dgram_address.unix_addr,
-			  self->parent_dgram_address.unix_addr_len));
+	ASSERT_EQ(0, bind(stream_server_parent, &parent_stream_address->unix_addr,
+			  parent_stream_address->unix_addr_len));
+	ASSERT_EQ(0, bind(dgram_server_parent, &parent_dgram_address->unix_addr,
+			  parent_dgram_address->unix_addr_len));
 
 	ASSERT_EQ(0, listen(stream_server_parent, backlog));
 
@@ -734,57 +766,119 @@ TEST_F(scoped_vs_unscoped, unix_scoping)
 		_metadata->exit_code = KSFT_FAIL;
 }
 
+TEST_F(scoped_vs_unscoped, unix_scoping_abstract)
+{
+	test_scoped_vs_unscoped(_metadata, self, variant, true);
+}
+
+TEST_F(scoped_vs_unscoped, unix_scoping_pathname)
+{
+	test_scoped_vs_unscoped(_metadata, self, variant, false);
+}
+
 FIXTURE(outside_socket)
 {
-	struct service_fixture address, transit_address;
+	struct service_fixture address_abstract, transit_address_abstract;
+	struct service_fixture address_pathname, transit_address_pathname;
 };
 
 FIXTURE_VARIANT(outside_socket)
 {
 	const bool child_socket;
 	const int type;
+	const bool abstract;
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(outside_socket, abstract_allow_dgram_child) {
+	/* clang-format on */
+	.child_socket = true,
+	.type = SOCK_DGRAM,
+	.abstract = true,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(outside_socket, abstract_deny_dgram_server) {
+	/* clang-format on */
+	.child_socket = false,
+	.type = SOCK_DGRAM,
+	.abstract = true,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(outside_socket, abstract_allow_stream_child) {
+	/* clang-format on */
+	.child_socket = true,
+	.type = SOCK_STREAM,
+	.abstract = true,
 };
 
 /* clang-format off */
-FIXTURE_VARIANT_ADD(outside_socket, allow_dgram_child) {
+FIXTURE_VARIANT_ADD(outside_socket, abstract_deny_stream_server) {
+	/* clang-format on */
+	.child_socket = false,
+	.type = SOCK_STREAM,
+	.abstract = true,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(outside_socket, pathname_allow_dgram_child) {
 	/* clang-format on */
 	.child_socket = true,
 	.type = SOCK_DGRAM,
+	.abstract = false,
 };
 
 /* clang-format off */
-FIXTURE_VARIANT_ADD(outside_socket, deny_dgram_server) {
+FIXTURE_VARIANT_ADD(outside_socket, pathname_deny_dgram_server) {
 	/* clang-format on */
 	.child_socket = false,
 	.type = SOCK_DGRAM,
+	.abstract = false,
 };
 
 /* clang-format off */
-FIXTURE_VARIANT_ADD(outside_socket, allow_stream_child) {
+FIXTURE_VARIANT_ADD(outside_socket, pathname_allow_stream_child) {
 	/* clang-format on */
 	.child_socket = true,
 	.type = SOCK_STREAM,
+	.abstract = false,
 };
 
 /* clang-format off */
-FIXTURE_VARIANT_ADD(outside_socket, deny_stream_server) {
+FIXTURE_VARIANT_ADD(outside_socket, pathname_deny_stream_server) {
 	/* clang-format on */
 	.child_socket = false,
 	.type = SOCK_STREAM,
+	.abstract = false,
 };
 
 FIXTURE_SETUP(outside_socket)
 {
 	drop_caps(_metadata);
 
-	memset(&self->transit_address, 0, sizeof(self->transit_address));
-	set_unix_address(&self->transit_address, 0, true);
-	memset(&self->address, 0, sizeof(self->address));
-	set_unix_address(&self->address, 1, true);
+	ASSERT_EQ(0, mkdir(PATHNAME_UNIX_SOCK_DIR, 0700));
+
+	/* Abstract addresses. */
+	memset(&self->transit_address_abstract, 0,
+	       sizeof(self->transit_address_abstract));
+	set_unix_address(&self->transit_address_abstract, 0, true);
+	memset(&self->address_abstract, 0, sizeof(self->address_abstract));
+	set_unix_address(&self->address_abstract, 1, true);
+
+	/* Pathname addresses. */
+	memset(&self->transit_address_pathname, 0,
+	       sizeof(self->transit_address_pathname));
+	set_unix_address(&self->transit_address_pathname, 2, false);
+	memset(&self->address_pathname, 0, sizeof(self->address_pathname));
+	set_unix_address(&self->address_pathname, 3, false);
 }
 
 FIXTURE_TEARDOWN(outside_socket)
 {
+	EXPECT_EQ(0, remove_path(self->transit_address_pathname.unix_addr.sun_path));
+	EXPECT_EQ(0, remove_path(self->address_pathname.unix_addr.sun_path));
+	EXPECT_EQ(0, rmdir(PATHNAME_UNIX_SOCK_DIR));
 }
 
 /*
@@ -798,6 +892,15 @@ TEST_F(outside_socket, socket_with_different_domain)
 	int pipe_child[2], pipe_parent[2];
 	char buf_parent;
 	int server_socket;
+	const __u16 scope = variant->abstract ?
+				    LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET :
+				    LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET;
+	const struct service_fixture *transit_address =
+		variant->abstract ? &self->transit_address_abstract :
+				    &self->transit_address_pathname;
+	const struct service_fixture *address =
+		variant->abstract ? &self->address_abstract :
+				    &self->address_pathname;
 
 	ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC));
 	ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC));
@@ -812,8 +915,7 @@ TEST_F(outside_socket, socket_with_different_domain)
 		EXPECT_EQ(0, close(pipe_child[0]));
 
 		/* Client always has a domain. */
-		create_scoped_domain(_metadata,
-				     LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);
+		create_scoped_domain(_metadata, scope);
 
 		if (variant->child_socket) {
 			int data_socket, passed_socket, stream_server;
@@ -823,8 +925,8 @@ TEST_F(outside_socket, socket_with_different_domain)
 			stream_server = socket(AF_UNIX, SOCK_STREAM, 0);
 			ASSERT_LE(0, stream_server);
 			ASSERT_EQ(0, bind(stream_server,
-					  &self->transit_address.unix_addr,
-					  self->transit_address.unix_addr_len));
+					  &transit_address->unix_addr,
+					  transit_address->unix_addr_len));
 			ASSERT_EQ(0, listen(stream_server, backlog));
 			ASSERT_EQ(1, write(pipe_child[1], ".", 1));
 			data_socket = accept(stream_server, NULL, NULL);
@@ -839,8 +941,8 @@ TEST_F(outside_socket, socket_with_different_domain)
 
 		/* Waits for parent signal for connection. */
 		ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
-		err = connect(client_socket, &self->address.unix_addr,
-			      self->address.unix_addr_len);
+		err = connect(client_socket, &address->unix_addr,
+			      address->unix_addr_len);
 		if (variant->child_socket) {
 			EXPECT_EQ(0, err);
 		} else {
@@ -859,9 +961,8 @@ TEST_F(outside_socket, socket_with_different_domain)
 
 		ASSERT_LE(0, client_child);
 		ASSERT_EQ(1, read(pipe_child[0], &buf_parent, 1));
-		ASSERT_EQ(0, connect(client_child,
-				     &self->transit_address.unix_addr,
-				     self->transit_address.unix_addr_len));
+		ASSERT_EQ(0, connect(client_child, &transit_address->unix_addr,
+				     transit_address->unix_addr_len));
 		server_socket = recv_fd(client_child);
 		EXPECT_EQ(0, close(client_child));
 	} else {
@@ -870,10 +971,10 @@ TEST_F(outside_socket, socket_with_different_domain)
 	ASSERT_LE(0, server_socket);
 
 	/* Server always has a domain. */
-	create_scoped_domain(_metadata, LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);
+	create_scoped_domain(_metadata, scope);
 
-	ASSERT_EQ(0, bind(server_socket, &self->address.unix_addr,
-			  self->address.unix_addr_len));
+	ASSERT_EQ(0,
+		  bind(server_socket, &address->unix_addr, address->unix_addr_len));
 	if (variant->type == SOCK_STREAM)
 		ASSERT_EQ(0, listen(server_socket, backlog));
 
@@ -888,52 +989,85 @@ TEST_F(outside_socket, socket_with_different_domain)
 		_metadata->exit_code = KSFT_FAIL;
 }
 
-static const char stream_path[] = TMP_DIR "/stream.sock";
-static const char dgram_path[] = TMP_DIR "/dgram.sock";
-
 /* clang-format off */
-FIXTURE(various_address_sockets) {};
+FIXTURE(various_address_sockets) {
+	struct service_fixture stream_pathname_addr, dgram_pathname_addr;
+	struct service_fixture stream_abstract_addr, dgram_abstract_addr;
+};
 /* clang-format on */
 
-FIXTURE_VARIANT(various_address_sockets)
-{
-	const int domain;
+/*
+ * Test all 4 combinations of abstract and pathname socket scope bits,
+ * plus a case with no Landlock domain at all.
+ */
+/* clang-format off */
+FIXTURE_VARIANT(various_address_sockets) {
+	/* clang-format on */
+	const __u16 scope_bits;
+	const bool no_sandbox;
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(various_address_sockets, scope_abstract) {
+	/* clang-format on */
+	.scope_bits = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(various_address_sockets, scope_pathname) {
+	/* clang-format on */
+	.scope_bits = LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET,
 };
 
 /* clang-format off */
-FIXTURE_VARIANT_ADD(various_address_sockets, pathname_socket_scoped_domain) {
+FIXTURE_VARIANT_ADD(various_address_sockets, scope_both) {
 	/* clang-format on */
-	.domain = SCOPE_SANDBOX,
+	.scope_bits = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET |
+		      LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET,
 };
 
 /* clang-format off */
-FIXTURE_VARIANT_ADD(various_address_sockets, pathname_socket_other_domain) {
+FIXTURE_VARIANT_ADD(various_address_sockets, scope_none) {
 	/* clang-format on */
-	.domain = OTHER_SANDBOX,
+	.scope_bits = 0,
 };
 
 /* clang-format off */
-FIXTURE_VARIANT_ADD(various_address_sockets, pathname_socket_no_domain) {
+FIXTURE_VARIANT_ADD(various_address_sockets, no_domain) {
 	/* clang-format on */
-	.domain = NO_SANDBOX,
+	.no_sandbox = true,
 };
 
 FIXTURE_SETUP(various_address_sockets)
 {
 	drop_caps(_metadata);
 
-	umask(0077);
-	ASSERT_EQ(0, mkdir(TMP_DIR, 0700));
+	ASSERT_EQ(0, mkdir(PATHNAME_UNIX_SOCK_DIR, 0700));
+
+	memset(&self->stream_pathname_addr, 0, sizeof(self->stream_pathname_addr));
+	set_unix_address(&self->stream_pathname_addr, 0, false);
+	memset(&self->dgram_pathname_addr, 0, sizeof(self->dgram_pathname_addr));
+	set_unix_address(&self->dgram_pathname_addr, 1, false);
+
+	memset(&self->stream_abstract_addr, 0, sizeof(self->stream_abstract_addr));
+	set_unix_address(&self->stream_abstract_addr, 2, true);
+	memset(&self->dgram_abstract_addr, 0, sizeof(self->dgram_abstract_addr));
+	set_unix_address(&self->dgram_abstract_addr, 3, true);
 }
 
 FIXTURE_TEARDOWN(various_address_sockets)
 {
-	EXPECT_EQ(0, unlink(stream_path));
-	EXPECT_EQ(0, unlink(dgram_path));
-	EXPECT_EQ(0, rmdir(TMP_DIR));
+	EXPECT_EQ(0, remove_path(self->stream_pathname_addr.unix_addr.sun_path));
+	EXPECT_EQ(0, remove_path(self->dgram_pathname_addr.unix_addr.sun_path));
+	EXPECT_EQ(0, rmdir(PATHNAME_UNIX_SOCK_DIR));
 }
 
-TEST_F(various_address_sockets, scoped_pathname_sockets)
+/*
+ * Test interaction of various scope flags (controlled by variant->domain)
+ * with pathname and abstract sockets when connecting from a sandboxed
+ * child.
+ */
+TEST_F(various_address_sockets, scoped_sockets)
 {
 	pid_t child;
 	int status;
@@ -942,25 +1076,10 @@ TEST_F(various_address_sockets, scoped_pathname_sockets)
 	int unnamed_sockets[2];
 	int stream_pathname_socket, dgram_pathname_socket,
 		stream_abstract_socket, dgram_abstract_socket, data_socket;
-	struct service_fixture stream_abstract_addr, dgram_abstract_addr;
-	struct sockaddr_un stream_pathname_addr = {
-		.sun_family = AF_UNIX,
-	};
-	struct sockaddr_un dgram_pathname_addr = {
-		.sun_family = AF_UNIX,
-	};
-
-	/* Pathname address. */
-	snprintf(stream_pathname_addr.sun_path,
-		 sizeof(stream_pathname_addr.sun_path), "%s", stream_path);
-	snprintf(dgram_pathname_addr.sun_path,
-		 sizeof(dgram_pathname_addr.sun_path), "%s", dgram_path);
-
-	/* Abstract address. */
-	memset(&stream_abstract_addr, 0, sizeof(stream_abstract_addr));
-	set_unix_address(&stream_abstract_addr, 0, true);
-	memset(&dgram_abstract_addr, 0, sizeof(dgram_abstract_addr));
-	set_unix_address(&dgram_abstract_addr, 1, true);
+	bool pathname_restricted =
+		(variant->scope_bits & LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET);
+	bool abstract_restricted =
+		(variant->scope_bits & LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);
 
 	/* Unnamed address for datagram socket. */
 	ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_DGRAM, 0, unnamed_sockets));
@@ -975,82 +1094,103 @@ TEST_F(various_address_sockets, scoped_pathname_sockets)
 		EXPECT_EQ(0, close(pipe_parent[1]));
 		EXPECT_EQ(0, close(unnamed_sockets[1]));
 
-		if (variant->domain == SCOPE_SANDBOX)
-			create_scoped_domain(
-				_metadata, LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);
-		else if (variant->domain == OTHER_SANDBOX)
+		/* Create domain based on variant. */
+		if (variant->scope_bits)
+			create_scoped_domain(_metadata, variant->scope_bits);
+		else if (!variant->no_sandbox)
 			create_fs_domain(_metadata);
 
 		/* Waits for parent to listen. */
 		ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
 		EXPECT_EQ(0, close(pipe_parent[0]));
 
-		/* Checks that we can send data through a datagram socket. */
+		/* Checks that we can send data through a unnamed socket. */
 		ASSERT_EQ(1, write(unnamed_sockets[0], "a", 1));
 		EXPECT_EQ(0, close(unnamed_sockets[0]));
 
 		/* Connects with pathname sockets. */
 		stream_pathname_socket = socket(AF_UNIX, SOCK_STREAM, 0);
 		ASSERT_LE(0, stream_pathname_socket);
-		ASSERT_EQ(0,
-			  connect(stream_pathname_socket, &stream_pathname_addr,
-				  sizeof(stream_pathname_addr)));
-		ASSERT_EQ(1, write(stream_pathname_socket, "b", 1));
+		err = connect(stream_pathname_socket,
+			      &self->stream_pathname_addr.unix_addr,
+			      self->stream_pathname_addr.unix_addr_len);
+		if (pathname_restricted) {
+			EXPECT_EQ(-1, err);
+			EXPECT_EQ(EPERM, errno);
+		} else {
+			EXPECT_EQ(0, err);
+			ASSERT_EQ(1, write(stream_pathname_socket, "b", 1));
+		}
 		EXPECT_EQ(0, close(stream_pathname_socket));
 
-		/* Sends without connection. */
+		/* Sends without connection (pathname). */
 		dgram_pathname_socket = socket(AF_UNIX, SOCK_DGRAM, 0);
 		ASSERT_LE(0, dgram_pathname_socket);
 		err = sendto(dgram_pathname_socket, "c", 1, 0,
-			     &dgram_pathname_addr, sizeof(dgram_pathname_addr));
-		EXPECT_EQ(1, err);
+			     &self->dgram_pathname_addr.unix_addr,
+			     self->dgram_pathname_addr.unix_addr_len);
+		if (pathname_restricted) {
+			EXPECT_EQ(-1, err);
+			EXPECT_EQ(EPERM, errno);
+		} else {
+			EXPECT_EQ(1, err);
+		}
+
+		/* Sends with connection (pathname). */
+		err = connect(dgram_pathname_socket,
+			      &self->dgram_pathname_addr.unix_addr,
+			      self->dgram_pathname_addr.unix_addr_len);
+		if (pathname_restricted) {
+			EXPECT_EQ(-1, err);
+			EXPECT_EQ(EPERM, errno);
+		} else {
+			EXPECT_EQ(0, err);
+			ASSERT_EQ(1, write(dgram_pathname_socket, "d", 1));
+		}
 
-		/* Sends with connection. */
-		ASSERT_EQ(0,
-			  connect(dgram_pathname_socket, &dgram_pathname_addr,
-				  sizeof(dgram_pathname_addr)));
-		ASSERT_EQ(1, write(dgram_pathname_socket, "d", 1));
 		EXPECT_EQ(0, close(dgram_pathname_socket));
 
 		/* Connects with abstract sockets. */
 		stream_abstract_socket = socket(AF_UNIX, SOCK_STREAM, 0);
 		ASSERT_LE(0, stream_abstract_socket);
 		err = connect(stream_abstract_socket,
-			      &stream_abstract_addr.unix_addr,
-			      stream_abstract_addr.unix_addr_len);
-		if (variant->domain == SCOPE_SANDBOX) {
+			      &self->stream_abstract_addr.unix_addr,
+			      self->stream_abstract_addr.unix_addr_len);
+		if (abstract_restricted) {
 			EXPECT_EQ(-1, err);
 			EXPECT_EQ(EPERM, errno);
 		} else {
 			EXPECT_EQ(0, err);
 			ASSERT_EQ(1, write(stream_abstract_socket, "e", 1));
 		}
+
 		EXPECT_EQ(0, close(stream_abstract_socket));
 
-		/* Sends without connection. */
+		/* Sends without connection (abstract). */
 		dgram_abstract_socket = socket(AF_UNIX, SOCK_DGRAM, 0);
 		ASSERT_LE(0, dgram_abstract_socket);
 		err = sendto(dgram_abstract_socket, "f", 1, 0,
-			     &dgram_abstract_addr.unix_addr,
-			     dgram_abstract_addr.unix_addr_len);
-		if (variant->domain == SCOPE_SANDBOX) {
+			     &self->dgram_abstract_addr.unix_addr,
+			     self->dgram_abstract_addr.unix_addr_len);
+		if (abstract_restricted) {
 			EXPECT_EQ(-1, err);
 			EXPECT_EQ(EPERM, errno);
 		} else {
 			EXPECT_EQ(1, err);
 		}
 
-		/* Sends with connection. */
+		/* Sends with connection (abstract). */
 		err = connect(dgram_abstract_socket,
-			      &dgram_abstract_addr.unix_addr,
-			      dgram_abstract_addr.unix_addr_len);
-		if (variant->domain == SCOPE_SANDBOX) {
+			      &self->dgram_abstract_addr.unix_addr,
+			      self->dgram_abstract_addr.unix_addr_len);
+		if (abstract_restricted) {
 			EXPECT_EQ(-1, err);
 			EXPECT_EQ(EPERM, errno);
 		} else {
 			EXPECT_EQ(0, err);
 			ASSERT_EQ(1, write(dgram_abstract_socket, "g", 1));
 		}
+
 		EXPECT_EQ(0, close(dgram_abstract_socket));
 
 		_exit(_metadata->exit_code);
@@ -1062,27 +1202,30 @@ TEST_F(various_address_sockets, scoped_pathname_sockets)
 	/* Sets up pathname servers. */
 	stream_pathname_socket = socket(AF_UNIX, SOCK_STREAM, 0);
 	ASSERT_LE(0, stream_pathname_socket);
-	ASSERT_EQ(0, bind(stream_pathname_socket, &stream_pathname_addr,
-			  sizeof(stream_pathname_addr)));
+	ASSERT_EQ(0, bind(stream_pathname_socket,
+			  &self->stream_pathname_addr.unix_addr,
+			  self->stream_pathname_addr.unix_addr_len));
 	ASSERT_EQ(0, listen(stream_pathname_socket, backlog));
 
 	dgram_pathname_socket = socket(AF_UNIX, SOCK_DGRAM, 0);
 	ASSERT_LE(0, dgram_pathname_socket);
-	ASSERT_EQ(0, bind(dgram_pathname_socket, &dgram_pathname_addr,
-			  sizeof(dgram_pathname_addr)));
+	ASSERT_EQ(0, bind(dgram_pathname_socket,
+			  &self->dgram_pathname_addr.unix_addr,
+			  self->dgram_pathname_addr.unix_addr_len));
 
 	/* Sets up abstract servers. */
 	stream_abstract_socket = socket(AF_UNIX, SOCK_STREAM, 0);
 	ASSERT_LE(0, stream_abstract_socket);
-	ASSERT_EQ(0,
-		  bind(stream_abstract_socket, &stream_abstract_addr.unix_addr,
-		       stream_abstract_addr.unix_addr_len));
+	ASSERT_EQ(0, bind(stream_abstract_socket,
+			  &self->stream_abstract_addr.unix_addr,
+			  self->stream_abstract_addr.unix_addr_len));
+	ASSERT_EQ(0, listen(stream_abstract_socket, backlog));
 
 	dgram_abstract_socket = socket(AF_UNIX, SOCK_DGRAM, 0);
 	ASSERT_LE(0, dgram_abstract_socket);
-	ASSERT_EQ(0, bind(dgram_abstract_socket, &dgram_abstract_addr.unix_addr,
-			  dgram_abstract_addr.unix_addr_len));
-	ASSERT_EQ(0, listen(stream_abstract_socket, backlog));
+	ASSERT_EQ(0, bind(dgram_abstract_socket,
+			  &self->dgram_abstract_addr.unix_addr,
+			  self->dgram_abstract_addr.unix_addr_len));
 
 	ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
 	EXPECT_EQ(0, close(pipe_parent[1]));
@@ -1092,24 +1235,31 @@ TEST_F(various_address_sockets, scoped_pathname_sockets)
 	ASSERT_EQ('a', buf_parent);
 	EXPECT_LE(0, close(unnamed_sockets[1]));
 
-	/* Reads from pathname sockets. */
-	data_socket = accept(stream_pathname_socket, NULL, NULL);
-	ASSERT_LE(0, data_socket);
-	ASSERT_EQ(1, read(data_socket, &buf_parent, sizeof(buf_parent)));
-	ASSERT_EQ('b', buf_parent);
-	EXPECT_EQ(0, close(data_socket));
-	EXPECT_EQ(0, close(stream_pathname_socket));
+	if (!pathname_restricted) {
+		/*
+		 * Reads from pathname sockets if we expect child to be able to
+		 * send.
+		 */
+		data_socket = accept(stream_pathname_socket, NULL, NULL);
+		ASSERT_LE(0, data_socket);
+		ASSERT_EQ(1,
+			  read(data_socket, &buf_parent, sizeof(buf_parent)));
+		ASSERT_EQ('b', buf_parent);
+		EXPECT_EQ(0, close(data_socket));
 
-	ASSERT_EQ(1,
-		  read(dgram_pathname_socket, &buf_parent, sizeof(buf_parent)));
-	ASSERT_EQ('c', buf_parent);
-	ASSERT_EQ(1,
-		  read(dgram_pathname_socket, &buf_parent, sizeof(buf_parent)));
-	ASSERT_EQ('d', buf_parent);
-	EXPECT_EQ(0, close(dgram_pathname_socket));
+		ASSERT_EQ(1, read(dgram_pathname_socket, &buf_parent,
+				  sizeof(buf_parent)));
+		ASSERT_EQ('c', buf_parent);
+		ASSERT_EQ(1, read(dgram_pathname_socket, &buf_parent,
+				  sizeof(buf_parent)));
+		ASSERT_EQ('d', buf_parent);
+	}
 
-	if (variant->domain != SCOPE_SANDBOX) {
-		/* Reads from abstract sockets if allowed to send. */
+	if (!abstract_restricted) {
+		/*
+		 * Reads from abstract sockets if we expect child to be able to
+		 * send.
+		 */
 		data_socket = accept(stream_abstract_socket, NULL, NULL);
 		ASSERT_LE(0, data_socket);
 		ASSERT_EQ(1,
@@ -1125,30 +1275,73 @@ TEST_F(various_address_sockets, scoped_pathname_sockets)
 		ASSERT_EQ('g', buf_parent);
 	}
 
-	/* Waits for all abstract socket tests. */
+	/* Waits for child to complete, and only close the socket afterwards. */
 	ASSERT_EQ(child, waitpid(child, &status, 0));
 	EXPECT_EQ(0, close(stream_abstract_socket));
 	EXPECT_EQ(0, close(dgram_abstract_socket));
+	EXPECT_EQ(0, close(stream_pathname_socket));
+	EXPECT_EQ(0, close(dgram_pathname_socket));
 
 	if (WIFSIGNALED(status) || !WIFEXITED(status) ||
 	    WEXITSTATUS(status) != EXIT_SUCCESS)
 		_metadata->exit_code = KSFT_FAIL;
 }
 
-TEST(datagram_sockets)
+/* Fixture for datagram_sockets and self_connect tests */
+FIXTURE(socket_type_test)
 {
 	struct service_fixture connected_addr, non_connected_addr;
+};
+
+FIXTURE_VARIANT(socket_type_test)
+{
+	const bool abstract;
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(socket_type_test, abstract) {
+	/* clang-format on */
+	.abstract = true,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(socket_type_test, pathname) {
+	/* clang-format on */
+	.abstract = false,
+};
+
+FIXTURE_SETUP(socket_type_test)
+{
+	drop_caps(_metadata);
+
+	if (!variant->abstract)
+		ASSERT_EQ(0, mkdir(PATHNAME_UNIX_SOCK_DIR, 0700));
+
+	memset(&self->connected_addr, 0, sizeof(self->connected_addr));
+	set_unix_address(&self->connected_addr, 0, variant->abstract);
+	memset(&self->non_connected_addr, 0, sizeof(self->non_connected_addr));
+	set_unix_address(&self->non_connected_addr, 1, variant->abstract);
+}
+
+FIXTURE_TEARDOWN(socket_type_test)
+{
+	if (!variant->abstract) {
+		EXPECT_EQ(0, remove_path(self->connected_addr.unix_addr.sun_path));
+		EXPECT_EQ(0, remove_path(self->non_connected_addr.unix_addr.sun_path));
+		EXPECT_EQ(0, rmdir(PATHNAME_UNIX_SOCK_DIR));
+	}
+}
+
+TEST_F(socket_type_test, datagram_sockets)
+{
 	int server_conn_socket, server_unconn_socket;
 	int pipe_parent[2], pipe_child[2];
 	int status;
 	char buf;
 	pid_t child;
-
-	drop_caps(_metadata);
-	memset(&connected_addr, 0, sizeof(connected_addr));
-	set_unix_address(&connected_addr, 0, true);
-	memset(&non_connected_addr, 0, sizeof(non_connected_addr));
-	set_unix_address(&non_connected_addr, 1, true);
+	const __u16 scope = variant->abstract ?
+				    LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET :
+				    LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET;
 
 	ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC));
 	ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC));
@@ -1169,8 +1362,9 @@ TEST(datagram_sockets)
 		/* Waits for parent to listen. */
 		ASSERT_EQ(1, read(pipe_parent[0], &buf, 1));
 		ASSERT_EQ(0,
-			  connect(client_conn_socket, &connected_addr.unix_addr,
-				  connected_addr.unix_addr_len));
+			  connect(client_conn_socket,
+				  &self->connected_addr.unix_addr,
+				  self->connected_addr.unix_addr_len));
 
 		/*
 		 * Both connected and non-connected sockets can send data when
@@ -1178,13 +1372,12 @@ TEST(datagram_sockets)
 		 */
 		ASSERT_EQ(1, send(client_conn_socket, ".", 1, 0));
 		ASSERT_EQ(1, sendto(client_unconn_socket, ".", 1, 0,
-				    &non_connected_addr.unix_addr,
-				    non_connected_addr.unix_addr_len));
+				    &self->non_connected_addr.unix_addr,
+				    self->non_connected_addr.unix_addr_len));
 		ASSERT_EQ(1, write(pipe_child[1], ".", 1));
 
 		/* Scopes the domain. */
-		create_scoped_domain(_metadata,
-				     LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);
+		create_scoped_domain(_metadata, scope);
 
 		/*
 		 * Connected socket sends data to the receiver, but the
@@ -1192,8 +1385,8 @@ TEST(datagram_sockets)
 		 */
 		ASSERT_EQ(1, send(client_conn_socket, ".", 1, 0));
 		ASSERT_EQ(-1, sendto(client_unconn_socket, ".", 1, 0,
-				     &non_connected_addr.unix_addr,
-				     non_connected_addr.unix_addr_len));
+				     &self->non_connected_addr.unix_addr,
+				     self->non_connected_addr.unix_addr_len));
 		ASSERT_EQ(EPERM, errno);
 		ASSERT_EQ(1, write(pipe_child[1], ".", 1));
 
@@ -1210,10 +1403,11 @@ TEST(datagram_sockets)
 	ASSERT_LE(0, server_conn_socket);
 	ASSERT_LE(0, server_unconn_socket);
 
-	ASSERT_EQ(0, bind(server_conn_socket, &connected_addr.unix_addr,
-			  connected_addr.unix_addr_len));
-	ASSERT_EQ(0, bind(server_unconn_socket, &non_connected_addr.unix_addr,
-			  non_connected_addr.unix_addr_len));
+	ASSERT_EQ(0, bind(server_conn_socket, &self->connected_addr.unix_addr,
+			  self->connected_addr.unix_addr_len));
+	ASSERT_EQ(0, bind(server_unconn_socket,
+			  &self->non_connected_addr.unix_addr,
+			  self->non_connected_addr.unix_addr_len));
 	ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
 
 	/* Waits for child to test. */
@@ -1238,52 +1432,49 @@ TEST(datagram_sockets)
 		_metadata->exit_code = KSFT_FAIL;
 }
 
-TEST(self_connect)
+TEST_F(socket_type_test, self_connect)
 {
-	struct service_fixture connected_addr, non_connected_addr;
 	int connected_socket, non_connected_socket, status;
 	pid_t child;
-
-	drop_caps(_metadata);
-	memset(&connected_addr, 0, sizeof(connected_addr));
-	set_unix_address(&connected_addr, 0, true);
-	memset(&non_connected_addr, 0, sizeof(non_connected_addr));
-	set_unix_address(&non_connected_addr, 1, true);
+	const __u16 scope = variant->abstract ?
+				    LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET :
+				    LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET;
 
 	connected_socket = socket(AF_UNIX, SOCK_DGRAM, 0);
 	non_connected_socket = socket(AF_UNIX, SOCK_DGRAM, 0);
 	ASSERT_LE(0, connected_socket);
 	ASSERT_LE(0, non_connected_socket);
 
-	ASSERT_EQ(0, bind(connected_socket, &connected_addr.unix_addr,
-			  connected_addr.unix_addr_len));
-	ASSERT_EQ(0, bind(non_connected_socket, &non_connected_addr.unix_addr,
-			  non_connected_addr.unix_addr_len));
+	ASSERT_EQ(0, bind(connected_socket, &self->connected_addr.unix_addr,
+			  self->connected_addr.unix_addr_len));
+	ASSERT_EQ(0, bind(non_connected_socket,
+			  &self->non_connected_addr.unix_addr,
+			  self->non_connected_addr.unix_addr_len));
 
 	child = fork();
 	ASSERT_LE(0, child);
 	if (child == 0) {
 		/* Child's domain is scoped. */
-		create_scoped_domain(_metadata,
-				     LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);
+		create_scoped_domain(_metadata, scope);
 
 		/*
 		 * The child inherits the sockets, and cannot connect or
 		 * send data to them.
 		 */
 		ASSERT_EQ(-1,
-			  connect(connected_socket, &connected_addr.unix_addr,
-				  connected_addr.unix_addr_len));
+			  connect(connected_socket,
+				  &self->connected_addr.unix_addr,
+				  self->connected_addr.unix_addr_len));
 		ASSERT_EQ(EPERM, errno);
 
 		ASSERT_EQ(-1, sendto(connected_socket, ".", 1, 0,
-				     &connected_addr.unix_addr,
-				     connected_addr.unix_addr_len));
+				     &self->connected_addr.unix_addr,
+				     self->connected_addr.unix_addr_len));
 		ASSERT_EQ(EPERM, errno);
 
 		ASSERT_EQ(-1, sendto(non_connected_socket, ".", 1, 0,
-				     &non_connected_addr.unix_addr,
-				     non_connected_addr.unix_addr_len));
+				     &self->non_connected_addr.unix_addr,
+				     self->non_connected_addr.unix_addr_len));
 		ASSERT_EQ(EPERM, errno);
 
 		EXPECT_EQ(0, close(connected_socket));
-- 
2.52.0




More information about the Linux-security-module-archive mailing list