selftests/landlock: Add test for TCP fast open

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>
Link: https://patch.msgid.link/20260701214628.33319-2-matthieu@buffet.re
Cc: stable@vger.kernel.org
[mic: Fix formatting]
Signed-off-by: Mickaël Salaün <mic@digikod.net>
This commit is contained in:
Matthieu Buffet
2026-07-01 23:46:28 +02:00
committed by Mickaël Salaün
parent 33cb713db0
commit f4b30e0b1d

View File

@@ -1281,6 +1281,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) {
EXPECT_EQ(-ENOTCONN, ret);
} else if (self->srv0.protocol.type == SOCK_DGRAM) {
EXPECT_EQ(-EDESTADDRREQ, ret);
} else {
EXPECT_EQ(-EINVAL, ret);
}
/* Fast Open to a denied address. */
ret = sendto_variant(connect_fd, &self->srv0, "A", 1,
MSG_FASTOPEN);
if (restricted) {
EXPECT_EQ(-EACCES, ret);
} else if (self->srv0.protocol.domain == AF_UNIX &&
self->srv0.protocol.type == SOCK_STREAM) {
EXPECT_EQ(-EOPNOTSUPP, ret);
} else {
EXPECT_EQ(0, ret);
}
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));
}
TEST_F(protocol, sendmsg_stream)
{
int srv0_fd, tmp_fd, client_fd, res;