From: "Mickaël Salaün" <mic@digikod.net>
To: "Günther Noack" <gnoack3000@gmail.com>
Cc: John Johansen <john.johansen@canonical.com>,
Justin Suess <utilityemal77@gmail.com>,
linux-security-module@vger.kernel.org,
Tingmao Wang <m@maowtm.org>,
Samasth Norway Ananda <samasth.norway.ananda@oracle.com>,
Matthieu Buffet <matthieu@buffet.re>,
Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>,
konstantin.meskhidze@huawei.com,
Demi Marie Obenour <demiobenour@gmail.com>,
Alyssa Ross <hi@alyssa.is>, Jann Horn <jannh@google.com>,
Tahera Fahimi <fahimitahera@gmail.com>
Subject: Re: [PATCH v4 4/6] landlock/selftests: Test named UNIX domain socket restrictions
Date: Mon, 9 Feb 2026 18:29:13 +0100 [thread overview]
Message-ID: <20260209.Noh6thae5cho@digikod.net> (raw)
In-Reply-To: <20260208231017.114343-5-gnoack3000@gmail.com>
On Mon, Feb 09, 2026 at 12:10:14AM +0100, Günther Noack wrote:
> * Exercise the access right for connect() and sendmsg() on named UNIX
> domain sockets, in various combinations of Landlock domains and
> socket types.
> * Extract common helpers from an existing IOCTL test that
> also uses pathname unix(7) sockets.
>
> The tested combinations are the cross product of these sets of fixture
> fields:
>
> * {{.handled=RESOLVE_UNIX},
> {.handled=RESOLVE_UNIX, .allowed=RESOLVE_UNIX}}
> * {{.sock_type=SOCK_STREAM},
> {.sock_type=SOCK_DGRAM},
> {.sock_type=SOCK_DGRAM, .use_sendto=true},
> {.sock_type=SOCK_SEQPACKET}}
> * {{.server_in_same_domain=false},
> {.server_in_same_domain=true}}
It would improve test clarity to follow the same approach as Tingmao to
check the scope, especially to use the scoped_base_variant.h:
https://lore.kernel.org/all/88de5bed60b06ba97088d87803f7bb3dbcc9a808.1767115163.git.m@maowtm.org/
Even if there is no more explicit scoped flag anymore, this test suite
is still relevant.
The fs_test.c part would then mostly check the
LANDLOCK_ACCESS_FS_RESOLVE_UNIX rules/exceptions.
>
> Some additional fixtures exercise scenarios with two nested domains.
>
> Cc: Justin Suess <utilityemal77@gmail.com>
> Cc: Mickaël Salaün <mic@digikod.net>
> Signed-off-by: Günther Noack <gnoack3000@gmail.com>
> ---
> tools/testing/selftests/landlock/fs_test.c | 381 ++++++++++++++++++++-
> 1 file changed, 365 insertions(+), 16 deletions(-)
>
> diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
> index b318627e7561..9d3f5dab4567 100644
> --- a/tools/testing/selftests/landlock/fs_test.c
> +++ b/tools/testing/selftests/landlock/fs_test.c
> @@ -4358,30 +4358,61 @@ TEST_F_FORK(layout1, named_pipe_ioctl)
> ASSERT_EQ(child_pid, waitpid(child_pid, NULL, 0));
> }
>
> +/*
> + * set_up_named_unix_server - Create a pathname unix socket
> + *
> + * If the socket type is not SOCK_DGRAM, also invoke listen(2).
> + *
> + * Return: The listening FD - it is the caller responsibility to close it.
> + */
> +static int set_up_named_unix_server(struct __test_metadata *const _metadata,
> + int type, const char *const path)
> +{
> + int fd;
> + struct sockaddr_un addr = {
> + .sun_family = AF_UNIX,
> + };
> +
> + fd = socket(AF_UNIX, type, 0);
> + ASSERT_LE(0, fd);
> +
> + strncpy(addr.sun_path, path, sizeof(addr.sun_path));
> + ASSERT_EQ(0, bind(fd, (struct sockaddr *)&addr, sizeof(addr)));
> +
> + if (type != SOCK_DGRAM)
> + ASSERT_EQ(0, listen(fd, 10 /* qlen */));
> + return fd;
> +}
> +
> +/*
> + * test_connect_named_unix - connect to the given named UNIX socket
> + *
> + * Return: The errno from connect(), or 0
> + */
> +static int test_connect_named_unix(int fd, const char *const path)
> +{
> + struct sockaddr_un addr = {
> + .sun_family = AF_UNIX,
> + };
> + strncpy(addr.sun_path, path, sizeof(addr.sun_path));
> +
> + if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
> + return errno;
> + return 0;
> +}
> +
> /* For named UNIX domain sockets, no IOCTL restrictions apply. */
> TEST_F_FORK(layout1, named_unix_domain_socket_ioctl)
> {
> const char *const path = file1_s1d1;
> int srv_fd, cli_fd, ruleset_fd;
> - struct sockaddr_un srv_un = {
> - .sun_family = AF_UNIX,
> - };
> - struct sockaddr_un cli_un = {
> - .sun_family = AF_UNIX,
> - };
> const struct landlock_ruleset_attr attr = {
> .handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV,
> };
>
> /* Sets up a server */
> ASSERT_EQ(0, unlink(path));
> - srv_fd = socket(AF_UNIX, SOCK_STREAM, 0);
> - ASSERT_LE(0, srv_fd);
> -
> - strncpy(srv_un.sun_path, path, sizeof(srv_un.sun_path));
> - ASSERT_EQ(0, bind(srv_fd, (struct sockaddr *)&srv_un, sizeof(srv_un)));
> -
> - ASSERT_EQ(0, listen(srv_fd, 10 /* qlen */));
> + srv_fd = set_up_named_unix_server(_metadata, SOCK_STREAM, path);
>
> /* Enables Landlock. */
> ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
> @@ -4393,9 +4424,7 @@ TEST_F_FORK(layout1, named_unix_domain_socket_ioctl)
> cli_fd = socket(AF_UNIX, SOCK_STREAM, 0);
> ASSERT_LE(0, cli_fd);
>
> - strncpy(cli_un.sun_path, path, sizeof(cli_un.sun_path));
> - ASSERT_EQ(0,
> - connect(cli_fd, (struct sockaddr *)&cli_un, sizeof(cli_un)));
> + ASSERT_EQ(0, test_connect_named_unix(cli_fd, path));
>
> /* FIONREAD and other IOCTLs should not be forbidden. */
> EXPECT_EQ(0, test_fionread_ioctl(cli_fd));
> @@ -4570,6 +4599,326 @@ TEST_F_FORK(ioctl, handle_file_access_file)
> ASSERT_EQ(0, close(file_fd));
> }
>
> +/* clang-format off */
> +FIXTURE(unix_socket) {};
> +
> +FIXTURE_SETUP(unix_socket) {};
> +
> +FIXTURE_TEARDOWN(unix_socket) {};
> +/* clang-format on */
> +
> +FIXTURE_VARIANT(unix_socket)
> +{
> + const __u64 handled;
> + const __u64 allowed;
> + const __u64 handled2;
> + const __u64 allowed2;
> + const int sock_type;
> + const int expected;
> + const bool use_sendto;
> + const bool server_in_same_domain;
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(unix_socket, stream_handled_not_allowed)
> +{
> + /* clang-format on */
> + .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed = 0,
> + .sock_type = SOCK_STREAM,
> + .expected = EACCES,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(unix_socket, stream_handled_and_allowed)
> +{
> + /* clang-format on */
> + .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .sock_type = SOCK_STREAM,
> + .expected = 0,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(unix_socket, dgram_handled_not_allowed)
> +{
> + /* clang-format on */
> + .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed = 0,
> + .sock_type = SOCK_DGRAM,
> + .expected = EACCES,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(unix_socket, dgram_handled_and_allowed)
> +{
> + /* clang-format on */
> + .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .sock_type = SOCK_DGRAM,
> + .expected = 0,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(unix_socket, dgram_sendto_handled_not_allowed)
> +{
> + /* clang-format on */
> + .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed = 0,
> + .sock_type = SOCK_DGRAM,
> + .use_sendto = true,
> + .expected = EACCES,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(unix_socket, dgram_sendto_handled_and_allowed)
> +{
> + /* clang-format on */
> + .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .sock_type = SOCK_DGRAM,
> + .use_sendto = true,
> + .expected = 0,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(unix_socket, seqpacket_handled_not_allowed)
> +{
> + /* clang-format on */
> + .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed = 0,
> + .sock_type = SOCK_SEQPACKET,
> + .expected = EACCES,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(unix_socket, seqpacket_handled_and_allowed)
> +{
> + /* clang-format on */
> + .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .sock_type = SOCK_SEQPACKET,
> + .expected = 0,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(unix_socket, stream_handled_not_allowed_and_same_scope)
> +{
> + /* clang-format on */
> + .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed = 0,
> + .sock_type = SOCK_STREAM,
> + .expected = 0,
> + .server_in_same_domain = true,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(unix_socket, stream_handled_and_allowed_and_same_scope)
> +{
> + /* clang-format on */
> + .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .sock_type = SOCK_STREAM,
> + .expected = 0,
> + .server_in_same_domain = true,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(unix_socket, dgram_handled_not_allowed_and_same_scope)
> +{
> + /* clang-format on */
> + .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed = 0,
> + .sock_type = SOCK_DGRAM,
> + .expected = 0,
> + .server_in_same_domain = true,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(unix_socket, dgram_handled_and_allowed_and_same_scope)
> +{
> + /* clang-format on */
> + .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .sock_type = SOCK_DGRAM,
> + .expected = 0,
> + .server_in_same_domain = true,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(unix_socket, dgram_sendto_handled_not_allowed_and_same_scope)
> +{
> + /* clang-format on */
> + .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed = 0,
> + .sock_type = SOCK_DGRAM,
> + .use_sendto = true,
> + .expected = 0,
> + .server_in_same_domain = true,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(unix_socket, dgram_sendto_handled_and_allowed_and_same_scope)
> +{
> + /* clang-format on */
> + .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .sock_type = SOCK_DGRAM,
> + .use_sendto = true,
> + .expected = 0,
> + .server_in_same_domain = true,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(unix_socket, seqpacket_handled_not_allowed_and_same_scope)
> +{
> + /* clang-format on */
> + .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed = 0,
> + .sock_type = SOCK_SEQPACKET,
> + .expected = 0,
> + .server_in_same_domain = true,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(unix_socket, seqpacket_handled_and_allowed_and_same_scope)
> +{
> + /* clang-format on */
> + .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .sock_type = SOCK_SEQPACKET,
> + .expected = 0,
> + .server_in_same_domain = true,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(unix_socket, stream_nested_domains_scope_path)
> +{
> + /* clang-format on */
> + .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed = 0,
> + .server_in_same_domain = true,
> + .handled2 = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed2 = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .sock_type = SOCK_STREAM,
> + .expected = 0,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(unix_socket, stream_nested_domains_path_scope)
> +{
> + /* clang-format on */
> + .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .server_in_same_domain = true,
> + .handled2 = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed2 = 0,
> + .sock_type = SOCK_STREAM,
> + .expected = EACCES,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(unix_socket, stream_nested_domains_scope_scope)
> +{
> + /* clang-format on */
> + .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed = 0,
> + .server_in_same_domain = true,
> + .handled2 = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed2 = 0,
> + .sock_type = SOCK_STREAM,
> + .expected = EACCES,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(unix_socket, stream_nested_domains_path_path)
> +{
> + /* clang-format on */
> + .handled = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .server_in_same_domain = true,
> + .handled2 = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .allowed2 = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> + .sock_type = SOCK_STREAM,
> + .expected = 0,
> +};
> +
> +/*
> + * test_sendto_named_unix - sendto to the given named UNIX socket
> + *
> + * sendto() is equivalent to sendmsg() in this respect.
> + *
> + * Return: The errno from sendto(), or 0
> + */
> +static int test_sendto_named_unix(int fd, const char *const path)
> +{
> + static const char buf[] = "dummy";
> + struct sockaddr_un addr = {
> + .sun_family = AF_UNIX,
> + };
> + strncpy(addr.sun_path, path, sizeof(addr.sun_path));
> +
> + if (sendto(fd, buf, sizeof(buf), 0, (struct sockaddr *)&addr,
> + sizeof(addr)) == -1)
> + return errno;
> + return 0;
> +}
> +
> +TEST_F_FORK(unix_socket, test)
> +{
> + const char *const path = "sock";
> + int cli_fd, srv_fd, ruleset_fd, res;
> + struct rule rules[] = {
> + {
> + .path = ".",
> + .access = variant->allowed,
> + },
> + {},
> + };
> +
> + /* Sets up a server (in the case where the server is in the parent domain) */
> + if (!variant->server_in_same_domain)
> + srv_fd = set_up_named_unix_server(_metadata, variant->sock_type,
> + path);
> +
> + /* Enables Landlock. */
> + ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
> + ASSERT_LE(0, ruleset_fd);
> + enforce_ruleset(_metadata, ruleset_fd);
> + ASSERT_EQ(0, close(ruleset_fd));
> +
> + /* Sets up a server (in the case where the server is in the same domain) */
> + if (variant->server_in_same_domain)
> + srv_fd = set_up_named_unix_server(_metadata, variant->sock_type,
> + path);
> +
> + if (variant->handled2) {
> + /* Enables Landlock another time, if needed. */
> + rules[0].access = variant->allowed2;
> + ruleset_fd =
> + create_ruleset(_metadata, variant->handled2, rules);
> + ASSERT_LE(0, ruleset_fd);
> + enforce_ruleset(_metadata, ruleset_fd);
> + ASSERT_EQ(0, close(ruleset_fd));
> + }
> +
> + /* Sets up a client connection to it */
> + cli_fd = socket(AF_UNIX, variant->sock_type, 0);
> + ASSERT_LE(0, cli_fd);
> +
> + /* Connecting or sendto to the Unix socket is denied. */
> + if (variant->use_sendto)
> + res = test_sendto_named_unix(cli_fd, path);
> + else
> + res = test_connect_named_unix(cli_fd, path);
> + EXPECT_EQ(variant->expected, res);
> +
> + /* Clean up. */
> + EXPECT_EQ(0, close(cli_fd));
> + EXPECT_EQ(0, close(srv_fd));
> + EXPECT_EQ(0, unlink(path));
> +}
> +
> /* clang-format off */
> FIXTURE(layout1_bind) {};
> /* clang-format on */
> --
> 2.52.0
>
>
next prev parent reply other threads:[~2026-02-09 17:29 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-08 23:10 [PATCH v4 0/6] landlock: UNIX connect() control by pathname and scope Günther Noack
2026-02-08 23:10 ` [PATCH v4 1/6] lsm: Add LSM hook security_unix_find Günther Noack
2026-02-09 17:51 ` Mickaël Salaün
2026-02-09 18:33 ` Tingmao Wang
2026-02-09 19:53 ` Tingmao Wang
2026-02-10 13:02 ` Justin Suess
2026-02-08 23:10 ` [PATCH v4 2/6] landlock: Control pathname UNIX domain socket resolution by path Günther Noack
2026-02-09 10:21 ` Günther Noack
2026-02-09 13:11 ` Justin Suess
2026-02-10 23:04 ` Günther Noack
2026-02-09 17:28 ` Mickaël Salaün
2026-02-10 23:09 ` Günther Noack
2026-02-09 18:03 ` Mickaël Salaün
2026-02-08 23:10 ` [PATCH v4 3/6] samples/landlock: Add support for named UNIX domain socket restrictions Günther Noack
2026-02-08 23:10 ` [PATCH v4 4/6] landlock/selftests: Test " Günther Noack
2026-02-09 17:29 ` Mickaël Salaün [this message]
2026-02-15 3:01 ` Tingmao Wang
2026-02-08 23:10 ` [PATCH v4 5/6] landlock: Document FS access right for pathname UNIX sockets Günther Noack
2026-02-08 23:10 ` [PATCH v4 6/6] landlock: Document design rationale for scoped access rights Günther Noack
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260209.Noh6thae5cho@digikod.net \
--to=mic@digikod.net \
--cc=demiobenour@gmail.com \
--cc=fahimitahera@gmail.com \
--cc=gnoack3000@gmail.com \
--cc=hi@alyssa.is \
--cc=ivanov.mikhail1@huawei-partners.com \
--cc=jannh@google.com \
--cc=john.johansen@canonical.com \
--cc=konstantin.meskhidze@huawei.com \
--cc=linux-security-module@vger.kernel.org \
--cc=m@maowtm.org \
--cc=matthieu@buffet.re \
--cc=samasth.norway.ananda@oracle.com \
--cc=utilityemal77@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.