From: "Mickaël Salaün" <mic@digikod.net>
To: Matthieu Buffet <matthieu@buffet.re>
Cc: "Bryam Vargas" <hexlabsecurity@proton.me>,
"Günther Noack" <gnoack@google.com>,
linux-security-module@vger.kernel.org,
"Mikhail Ivanov" <ivanov.mikhail1@huawei-partners.com>,
"Paul Moore" <paul@paul-moore.com>,
"Eric Dumazet" <edumazet@google.com>,
"Neal Cardwell" <ncardwell@google.com>,
linux-kernel@vger.kernel.org, netdev@vger.kernel.org
Subject: Re: [RFC PATCH 2/2] selftests/landlock: Add test for TCP fast open
Date: Fri, 26 Jun 2026 23:19:58 +0200 [thread overview]
Message-ID: <20260626.Ahk8pe9boong@digikod.net> (raw)
In-Reply-To: <20260617180526.15627-3-matthieu@buffet.re>
On Wed, Jun 17, 2026 at 08:05:24PM +0200, Matthieu Buffet wrote:
> Enforce that TCP Fast Open is controlled by
> LANDLOCK_ACCESS_NET_CONNECT_TCP. Semantics of connect() and
> sendmsg(MSG_FASTOPEN) should be identical from Landlock's perspective.
> Also enforce error code consistency, since UDP sockets ignore
> the MSG_FASTOPEN flag while Unix sockets reject it.
>
> Signed-off-by: Matthieu Buffet <matthieu@buffet.re>
> ---
> tools/testing/selftests/landlock/net_test.c | 155 ++++++++++++++++++++
> 1 file changed, 155 insertions(+)
>
> diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
> index 0c256e7c8675..177ed28e70f6 100644
> --- a/tools/testing/selftests/landlock/net_test.c
> +++ b/tools/testing/selftests/landlock/net_test.c
> @@ -258,6 +258,64 @@ static int connect_variant(const int sock_fd,
> return connect_variant_addrlen(sock_fd, srv, get_addrlen(srv, false));
> }
>
> +static int sendto_variant_addrlen(const int sock_fd,
> + const struct service_fixture *const srv,
> + const socklen_t addrlen, void *buf,
> + size_t len, size_t flags)
> +{
> + const struct sockaddr *dst = NULL;
> + ssize_t ret;
> +
> + /*
> + * We never want our processes to be killed by SIGPIPE: we check return
> + * codes and errno, so that we have actual error messages.
> + */
There are some extra spaces above.
> + flags |= MSG_NOSIGNAL;
> +
> + if (srv != NULL) {
Just `if (srv) {`
> + switch (srv->protocol.domain) {
> + case AF_UNSPEC:
> + case AF_INET:
> + dst = (const struct sockaddr *)&srv->ipv4_addr;
> + break;
> +
> + case AF_INET6:
> + dst = (const struct sockaddr *)&srv->ipv6_addr;
> + break;
> +
> + case AF_UNIX:
> + dst = (const struct sockaddr *)&srv->unix_addr;
> + break;
> +
> + default:
> + errno = EAFNOSUPPORT;
> + return -errno;
> + }
> + }
> +
> + ret = sendto(sock_fd, buf, len, flags, dst, addrlen);
> + if (ret < 0)
> + return -errno;
> +
> + /* errno is not set in cases of partial writes. */
> + if (ret != len)
> + return -EINTR;
> +
> + return 0;
> +}
> +
> +static int sendto_variant(const int sock_fd,
> + const struct service_fixture *const srv, void *buf,
> + size_t len, size_t flags)
> +{
> + socklen_t addrlen = 0;
> +
> + if (srv != NULL)
ditto
> + addrlen = get_addrlen(srv, false);
> +
> + return sendto_variant_addrlen(sock_fd, srv, addrlen, buf, len, flags);
> +}
> +
> FIXTURE(protocol)
> {
> struct service_fixture srv0, srv1, srv2, unspec_any0, unspec_srv0;
> @@ -950,6 +1008,103 @@ TEST_F(protocol, connect_unspec)
> EXPECT_EQ(0, close(bind_fd));
> }
>
> +TEST_F(protocol, tcp_fastopen)
> +{
> + const bool restricted = variant->sandbox == TCP_SANDBOX &&
> + variant->prot.type == SOCK_STREAM &&
> + (variant->prot.protocol == IPPROTO_TCP ||
> + variant->prot.protocol == IPPROTO_IP) &&
> + (variant->prot.domain == AF_INET ||
> + variant->prot.domain == AF_INET6);
> + const struct landlock_ruleset_attr ruleset_attr = {
> + .handled_access_net = LANDLOCK_ACCESS_NET_CONNECT_TCP,
> + };
> + int bind_fd, client_fd, status;
> + char buf;
> + pid_t child;
> +
> + bind_fd = socket_variant(&self->srv0);
> + ASSERT_LE(0, bind_fd);
> + EXPECT_EQ(0, bind_variant(bind_fd, &self->srv0));
> + if (self->srv0.protocol.type == SOCK_STREAM)
> + EXPECT_EQ(0, listen(bind_fd, backlog));
> +
> + child = fork();
> + ASSERT_LE(0, child);
> + if (child == 0) {
> + int connect_fd, ret;
> +
> + /* Closes listening socket for the child. */
> + EXPECT_EQ(0, close(bind_fd));
> +
> + connect_fd = socket_variant(&self->srv0);
> + ASSERT_LE(0, connect_fd);
> +
> + if (variant->sandbox == TCP_SANDBOX) {
> + const int ruleset_fd = landlock_create_ruleset(
> + &ruleset_attr, sizeof(ruleset_attr), 0);
> + ASSERT_LE(0, ruleset_fd);
> +
> + enforce_ruleset(_metadata, ruleset_fd);
> + EXPECT_EQ(0, close(ruleset_fd));
> + }
> +
> + /* Fast Open with no address. */
> + ret = sendto_variant(connect_fd, NULL, NULL, 0, MSG_FASTOPEN);
> + if (self->srv0.protocol.domain == AF_UNIX) {
> + ASSERT_EQ(-ENOTCONN, ret);
> + } else if (self->srv0.protocol.type == SOCK_DGRAM) {
> + ASSERT_EQ(-EDESTADDRREQ, ret);
> + } else {
> + ASSERT_EQ(-EINVAL, ret);
> + }
> +
> + /* Fast Open to a denied address. */
> + ret = sendto_variant(connect_fd, &self->srv0, "A", 1,
> + MSG_FASTOPEN);
> + if (restricted) {
> + ASSERT_EQ(-EACCES, ret);
> + } else if (self->srv0.protocol.domain == AF_UNIX &&
> + self->srv0.protocol.type == SOCK_STREAM) {
> + ASSERT_EQ(-EOPNOTSUPP, ret);
> + } else {
> + ASSERT_EQ(0, ret);
> + }
All these ret checks should be done with EXPECT_EQ because they don't
block the test itself and we can get more info by checking more after
that.
> +
> + EXPECT_EQ(0, close(connect_fd));
> + _exit(_metadata->exit_code);
> + return;
> + }
> +
> + client_fd = bind_fd;
> + if (!restricted && self->srv0.protocol.type == SOCK_STREAM &&
> + self->srv0.protocol.domain != AF_UNIX) {
> + client_fd = accept(bind_fd, NULL, 0);
> + ASSERT_LE(0, client_fd);
> + }
> +
> + if (restricted) {
> + EXPECT_EQ(-1, read(client_fd, &buf, 1));
> + EXPECT_EQ(ENOTCONN, errno);
> + } else if (self->srv0.protocol.domain == AF_UNIX &&
> + self->srv0.protocol.type == SOCK_STREAM) {
> + EXPECT_EQ(-1, read(client_fd, &buf, 1));
> + EXPECT_EQ(EINVAL, errno);
> + } else {
> + EXPECT_EQ(1, read(client_fd, &buf, 1));
> + EXPECT_EQ('A', buf);
> + }
> +
> + EXPECT_EQ(child, waitpid(child, &status, 0));
> + EXPECT_EQ(1, WIFEXITED(status));
> + EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
> +
> + if (client_fd != bind_fd)
> + EXPECT_LE(0, close(client_fd));
> +
> + EXPECT_EQ(0, close(bind_fd));
> +}
> +
> FIXTURE(ipv4)
> {
> struct service_fixture srv0, srv1;
> --
> 2.47.3
>
>
prev parent reply other threads:[~2026-06-26 21:20 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-16 20:16 Landlock: LANDLOCK_ACCESS_NET_CONNECT_TCP bypass via TCP Fast Open Bryam Vargas
2026-06-17 14:22 ` Mickaël Salaün
2026-06-17 18:05 ` Matthieu Buffet
2026-06-17 18:05 ` [RFC PATCH 1/2] landlock: fix TCP Fast Open connection bypass Matthieu Buffet
2026-06-18 1:25 ` Bryam Vargas
2026-06-19 0:34 ` Matthieu Buffet
2026-06-19 1:39 ` Bryam Vargas
2026-06-26 20:40 ` Mickaël Salaün
2026-07-01 21:42 ` Matthieu Buffet
2026-07-01 21:46 ` [PATCH v2 " Matthieu Buffet
2026-07-01 21:46 ` [PATCH v2 2/2] selftests/landlock: Add test for TCP fast open Matthieu Buffet
2026-07-02 14:59 ` [PATCH v2 1/2] landlock: fix TCP Fast Open connection bypass Mickaël Salaün
2026-06-17 18:05 ` [RFC PATCH 2/2] selftests/landlock: Add test for TCP fast open Matthieu Buffet
2026-06-26 21:19 ` Mickaël Salaün [this message]
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=20260626.Ahk8pe9boong@digikod.net \
--to=mic@digikod.net \
--cc=edumazet@google.com \
--cc=gnoack@google.com \
--cc=hexlabsecurity@proton.me \
--cc=ivanov.mikhail1@huawei-partners.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=matthieu@buffet.re \
--cc=ncardwell@google.com \
--cc=netdev@vger.kernel.org \
--cc=paul@paul-moore.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.