From: Kuniyuki Iwashima <kuniyu@amazon.com>
To: <aleksandr.mikhalitsyn@canonical.com>
Cc: <arnd@arndb.de>, <bluca@debian.org>, <brauner@kernel.org>,
<cgroups@vger.kernel.org>, <davem@davemloft.net>,
<edumazet@google.com>, <hannes@cmpxchg.org>, <kuba@kernel.org>,
<kuniyu@amazon.com>, <leon@kernel.org>,
<linux-kernel@vger.kernel.org>, <linux-kselftest@vger.kernel.org>,
<mkoutny@suse.com>, <mzxreary@0pointer.de>,
<netdev@vger.kernel.org>, <pabeni@redhat.com>, <shuah@kernel.org>,
<tj@kernel.org>, <willemb@google.com>
Subject: Re: [PATCH net-next 4/4] tools/testing/selftests/cgroup: add test for SO_PEERCGROUPID
Date: Tue, 11 Mar 2025 01:02:32 -0700 [thread overview]
Message-ID: <20250311080233.11136-1-kuniyu@amazon.com> (raw)
In-Reply-To: <20250309132821.103046-5-aleksandr.mikhalitsyn@canonical.com>
From: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@canonical.com>
Date: Sun, 9 Mar 2025 14:28:15 +0100
> +static void client(FIXTURE_DATA(so_peercgroupid) *self,
> + const FIXTURE_VARIANT(so_peercgroupid) *variant)
> +{
> + int cfd, err;
> + socklen_t len;
> + uint64_t peer_cgroup_id = 0, test_cgroup1_id = 0, test_cgroup2_id = 0;
> + char state;
> +
> + cfd = socket(AF_UNIX, variant->type, 0);
> + if (cfd < 0) {
> + log_err("socket");
> + child_die();
> + }
> +
> + if (variant->type == SOCK_DGRAM) {
> + fill_sockaddr(self->client_addr, variant->abstract);
> +
> + if (bind(cfd, (struct sockaddr *)&self->client_addr->listen_addr, self->client_addr->addrlen)) {
> + log_err("bind");
> + child_die();
> + }
> + }
> +
> + /* negative testcase: no peer for socket yet */
> + len = sizeof(peer_cgroup_id);
> + err = getsockopt(cfd, SOL_SOCKET, SO_PEERCGROUPID, &peer_cgroup_id, &len);
> + if (!err || (errno != ENODATA)) {
> + log_err("getsockopt must fail with errno == ENODATA when socket has no peer");
> + child_die();
> + }
> +
> + if (connect(cfd, (struct sockaddr *)&self->server_addr.listen_addr,
> + self->server_addr.addrlen) != 0) {
> + log_err("connect");
> + child_die();
> + }
> +
> + state = 'R';
> + write(self->sync_sk[1], &state, sizeof(state));
nit: This looks unnecessary ?
> +
> + read(self->sync_sk[1], &test_cgroup1_id, sizeof(uint64_t));
> + read(self->sync_sk[1], &test_cgroup2_id, sizeof(uint64_t));
> +
> + len = sizeof(peer_cgroup_id);
> + if (getsockopt(cfd, SOL_SOCKET, SO_PEERCGROUPID, &peer_cgroup_id, &len)) {
> + log_err("Failed to get SO_PEERCGROUPID");
> + child_die();
> + }
> +
> + /*
> + * There is a difference between connection-oriented sockets
> + * and connectionless ones from the perspective of SO_PEERCGROUPID.
> + *
> + * sk->sk_cgrp_data is getting filled when we allocate struct sock (see call to cgroup_sk_alloc()).
> + * For DGRAM socket, self->server socket is our peer and by the time when we allocate it,
> + * parent process sits in a test_cgroup1. Then it changes cgroup to test_cgroup2, but it does not
> + * affect anything.
> + * For STREAM/SEQPACKET socket, self->server is not our peer, but that one we get from accept()
> + * syscall. And by the time when we call accept(), parent process sits in test_cgroup2.
> + *
> + * Let's ensure that it works like that and if it get changed then we should detect it
> + * as it's a clear UAPI change.
> + */
> + if (variant->type == SOCK_DGRAM) {
> + /* cgroup id from SO_PEERCGROUPID should be equal to the test_cgroup1_id */
> + if (peer_cgroup_id != test_cgroup1_id) {
> + log_err("peer_cgroup_id != test_cgroup1_id: %" PRId64 " != %" PRId64, peer_cgroup_id, test_cgroup1_id);
> + child_die();
> + }
> + } else {
> + /* cgroup id from SO_PEERCGROUPID should be equal to the test_cgroup2_id */
> + if (peer_cgroup_id != test_cgroup2_id) {
> + log_err("peer_cgroup_id != test_cgroup2_id: %" PRId64 " != %" PRId64, peer_cgroup_id, test_cgroup2_id);
> + child_die();
> + }
> + }
> +}
> +
> +TEST_F(so_peercgroupid, test)
> +{
> + uint64_t test_cgroup1_id, test_cgroup2_id;
> + int err;
> + int pfd;
> + char state;
> + int child_status = 0;
> +
> + if (cg_find_unified_root(self->cgroup_root, sizeof(self->cgroup_root), NULL))
> + ksft_exit_skip("cgroup v2 isn't mounted\n");
> +
> + self->test_cgroup1 = cg_name(self->cgroup_root, "so_peercgroupid_cg1");
> + ASSERT_NE(NULL, self->test_cgroup1);
> +
> + self->test_cgroup2 = cg_name(self->cgroup_root, "so_peercgroupid_cg2");
> + ASSERT_NE(NULL, self->test_cgroup2);
> +
> + err = cg_create(self->test_cgroup1);
> + ASSERT_EQ(0, err);
> +
> + err = cg_create(self->test_cgroup2);
> + ASSERT_EQ(0, err);
> +
> + test_cgroup1_id = cg_get_id(self->test_cgroup1);
> + ASSERT_LT(0, test_cgroup1_id);
> +
> + test_cgroup2_id = cg_get_id(self->test_cgroup2);
> + ASSERT_LT(0, test_cgroup2_id);
> +
> + /* enter test_cgroup1 before allocating a socket */
> + err = cg_enter_current(self->test_cgroup1);
> + ASSERT_EQ(0, err);
> +
> + self->server = socket(AF_UNIX, variant->type, 0);
> + ASSERT_NE(-1, self->server);
> +
> + /* enter test_cgroup2 after allocating a socket */
> + err = cg_enter_current(self->test_cgroup2);
> + ASSERT_EQ(0, err);
> +
> + fill_sockaddr(&self->server_addr, variant->abstract);
> +
> + err = bind(self->server, (struct sockaddr *)&self->server_addr.listen_addr, self->server_addr.addrlen);
> + ASSERT_EQ(0, err);
> +
> + if (variant->type != SOCK_DGRAM) {
> + err = listen(self->server, 1);
> + ASSERT_EQ(0, err);
> + }
> +
> + err = socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0, self->sync_sk);
> + EXPECT_EQ(err, 0);
> +
> + self->client_pid = fork();
> + ASSERT_NE(-1, self->client_pid);
> + if (self->client_pid == 0) {
> + close(self->server);
> + close(self->sync_sk[0]);
> + client(self, variant);
> + exit(0);
> + }
> + close(self->sync_sk[1]);
> +
> + if (variant->type != SOCK_DGRAM) {
> + pfd = accept(self->server, NULL, NULL);
> + ASSERT_NE(-1, pfd);
nit: close(self->server) here ?
It's close()d anyway when the process exits.
> + } else {
> + pfd = self->server;
> + }
> +
> + /* wait until the child arrives at checkpoint */
> + read(self->sync_sk[0], &state, sizeof(state));
> + ASSERT_EQ(state, 'R');
The client will wait two write()s without this synchronisation.
> +
> + write(self->sync_sk[0], &test_cgroup1_id, sizeof(uint64_t));
> + write(self->sync_sk[0], &test_cgroup2_id, sizeof(uint64_t));
> +
> + close(pfd);
> + waitpid(self->client_pid, &child_status, 0);
> + ASSERT_EQ(0, WIFEXITED(child_status) ? WEXITSTATUS(child_status) : 1);
> +}
> +
> +TEST_HARNESS_MAIN
prev parent reply other threads:[~2025-03-11 8:02 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <20250309132821.103046-1-aleksandr.mikhalitsyn@canonical.com>
2025-03-09 13:28 ` [PATCH net-next 3/4] tools/testing/selftests/cgroup/cgroup_util: add cg_get_id helper Alexander Mikhalitsyn
2025-03-09 13:28 ` [PATCH net-next 4/4] tools/testing/selftests/cgroup: add test for SO_PEERCGROUPID Alexander Mikhalitsyn
2025-03-10 13:59 ` Willem de Bruijn
2025-03-11 8:02 ` Kuniyuki Iwashima [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=20250311080233.11136-1-kuniyu@amazon.com \
--to=kuniyu@amazon.com \
--cc=aleksandr.mikhalitsyn@canonical.com \
--cc=arnd@arndb.de \
--cc=bluca@debian.org \
--cc=brauner@kernel.org \
--cc=cgroups@vger.kernel.org \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=hannes@cmpxchg.org \
--cc=kuba@kernel.org \
--cc=leon@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=mkoutny@suse.com \
--cc=mzxreary@0pointer.de \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=shuah@kernel.org \
--cc=tj@kernel.org \
--cc=willemb@google.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox