From: "Hallgrimur H. Gunnarsson" <halg@google.com>
To: Christian Brauner <brauner@kernel.org>,
Alexander Viro <viro@zeniv.linux.org.uk>,
Paul Moore <paul@paul-moore.com>,
Serge Hallyn <sergeh@kernel.org>, Kees Cook <kees@kernel.org>,
"David S . Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@google.com>,
Jakub Kicinski <kuba@kernel.org>,
Paolo Abeni <pabeni@redhat.com>
Cc: "Jan Kara" <jack@suse.cz>, "James Morris" <jmorris@namei.org>,
"Simon Horman" <horms@kernel.org>,
"Jonathan Corbet" <corbet@lwn.net>,
"Shuah Khan" <skhan@linuxfoundation.org>,
"Randy Dunlap" <rdunlap@infradead.org>,
"Jann Horn" <jannh@google.com>,
"Günther Noack" <gnoack@google.com>,
"KP Singh" <kpsingh@kernel.org>,
"Jonathan Calmels" <jcalmels@3xx0.net>,
"Casey Schaufler" <casey@schaufler-ca.com>,
linux-fsdevel@vger.kernel.org,
linux-security-module@vger.kernel.org, linux-api@vger.kernel.org,
containers@lists.linux.dev, netdev@vger.kernel.org,
linux-doc@vger.kernel.org, linux-kselftest@vger.kernel.org,
linux-kernel@vger.kernel.org,
"Hallgrimur H. Gunnarsson" <halg@google.com>
Subject: [PATCH 7/8] selftests/capabilities: add clone3() tests for CLONE_NEWUSER_BSET
Date: Wed, 28 Oct 2026 22:17:04 +0000 [thread overview]
Message-ID: <20261028221705.3915340-8-halg@google.com> (raw)
In-Reply-To: <20261028221705.3915340-1-halg@google.com>
Add tests to userns_bounding_set_test to verify per-namespace
capability bounding via CLONE_NEWUSER_BSET.
Signed-off-by: Hallgrimur H. Gunnarsson <halg@google.com>
---
.../capabilities/userns_bounding_set_test.c | 146 ++++++++++++++++++
.../selftests/clone3/clone3_selftests.h | 1 +
2 files changed, 147 insertions(+)
diff --git a/tools/testing/selftests/capabilities/userns_bounding_set_test.c b/tools/testing/selftests/capabilities/userns_bounding_set_test.c
index 798758e0705d..140c12e16e9e 100644
--- a/tools/testing/selftests/capabilities/userns_bounding_set_test.c
+++ b/tools/testing/selftests/capabilities/userns_bounding_set_test.c
@@ -417,4 +417,150 @@ TEST_F(userns_bset, parent_owner_cap_restricted)
EXPECT_EQ(EPERM, run_parent_owner_setns(restricted_mask));
}
+/*
+ * Verify that CLONE_NEWUSER_BSET restricts capabilities in the child user
+ * namespace even when the global sysctl allows all capabilities.
+ */
+TEST_F(userns_bset, clone3_bset_restriction)
+{
+ struct __clone_args args = {
+ .flags = CLONE_NEWUSER | CLONE_NEWUSER_BSET,
+ .exit_signal = SIGCHLD,
+ .cap_bset = cap_full_set & ~(1ULL << CAP_NET_ADMIN),
+ };
+ pid_t pid;
+ int status;
+
+ ASSERT_EQ(0, write_sysctl_caps(cap_full_set));
+
+ pid = sys_clone3(&args, sizeof(args));
+ ASSERT_GE(pid, 0);
+
+ if (pid == 0) {
+ if (!cap_raised(CAP_SYS_ADMIN))
+ _exit(1);
+ if (!cap_dropped(CAP_NET_ADMIN))
+ _exit(2);
+ _exit(0);
+ }
+
+ ASSERT_EQ(pid, waitpid(pid, &status, 0));
+ ASSERT_TRUE(WIFEXITED(status));
+ EXPECT_EQ(0, WEXITSTATUS(status));
+}
+
+/*
+ * Verify that passing cap_bset = 0 with CLONE_NEWUSER_BSET drops all
+ * capabilities in the new user namespace.
+ */
+TEST_F(userns_bset, clone3_bset_empty)
+{
+ struct __clone_args args = {
+ .flags = CLONE_NEWUSER | CLONE_NEWUSER_BSET,
+ .exit_signal = SIGCHLD,
+ .cap_bset = 0,
+ };
+ pid_t pid;
+ int status;
+
+ ASSERT_EQ(0, write_sysctl_caps(cap_full_set));
+
+ pid = sys_clone3(&args, sizeof(args));
+ ASSERT_GE(pid, 0);
+
+ if (pid == 0) {
+ if (!cap_dropped(CAP_SYS_ADMIN))
+ _exit(1);
+ if (!cap_dropped(CAP_NET_ADMIN))
+ _exit(2);
+ _exit(0);
+ }
+
+ ASSERT_EQ(pid, waitpid(pid, &status, 0));
+ ASSERT_TRUE(WIFEXITED(status));
+ EXPECT_EQ(0, WEXITSTATUS(status));
+}
+
+/*
+ * Verify that an unprivileged caller cannot use CLONE_NEWUSER_BSET to bypass
+ * the global sysctl ceiling.
+ */
+TEST_F(userns_bset, clone3_bset_cannot_bypass_sysctl)
+{
+ uint64_t restricted_mask = cap_full_set & ~(1ULL << CAP_NET_ADMIN);
+ pid_t pid;
+ int status;
+
+ ASSERT_EQ(0, write_sysctl_caps(restricted_mask));
+
+ pid = fork();
+ ASSERT_GE(pid, 0);
+
+ if (pid == 0) {
+ struct __clone_args args = {
+ .flags = CLONE_NEWUSER | CLONE_NEWUSER_BSET,
+ .exit_signal = SIGCHLD,
+ .cap_bset = cap_full_set,
+ };
+ int child_status;
+ pid_t child;
+
+ if (drop_privileges() != 0)
+ _exit(1);
+
+ child = sys_clone3(&args, sizeof(args));
+ if (child < 0)
+ _exit(2);
+
+ if (child == 0) {
+ if (!cap_raised(CAP_SYS_ADMIN))
+ _exit(3);
+ if (!cap_dropped(CAP_NET_ADMIN))
+ _exit(4);
+ _exit(0);
+ }
+
+ if (waitpid(child, &child_status, 0) != child ||
+ !WIFEXITED(child_status) || WEXITSTATUS(child_status) != 0)
+ _exit(5);
+
+ _exit(0);
+ }
+
+ ASSERT_EQ(pid, waitpid(pid, &status, 0));
+ ASSERT_TRUE(WIFEXITED(status));
+ EXPECT_EQ(0, WEXITSTATUS(status));
+}
+
+/*
+ * Verify clone3() argument validation for CLONE_NEWUSER_BSET.
+ */
+TEST_F(userns_bset, clone3_bset_invalid_args)
+{
+ struct __clone_args args;
+
+ /* CLONE_NEWUSER_BSET without CLONE_NEWUSER must fail */
+ memset(&args, 0, sizeof(args));
+ args.flags = CLONE_NEWUSER_BSET;
+ args.exit_signal = SIGCHLD;
+ args.cap_bset = cap_full_set;
+ EXPECT_EQ(-1, sys_clone3(&args, sizeof(args)));
+ EXPECT_EQ(EINVAL, errno);
+
+ /* Non-zero cap_bset without CLONE_NEWUSER_BSET must fail */
+ memset(&args, 0, sizeof(args));
+ args.flags = CLONE_NEWUSER;
+ args.exit_signal = SIGCHLD;
+ args.cap_bset = cap_full_set;
+ EXPECT_EQ(-1, sys_clone3(&args, sizeof(args)));
+ EXPECT_EQ(EINVAL, errno);
+
+ /* CLONE_NEWUSER_BSET with struct size < VER3 must fail */
+ memset(&args, 0, sizeof(args));
+ args.flags = CLONE_NEWUSER | CLONE_NEWUSER_BSET;
+ args.exit_signal = SIGCHLD;
+ EXPECT_EQ(-1, sys_clone3(&args, CLONE_ARGS_SIZE_VER2));
+ EXPECT_EQ(EINVAL, errno);
+}
+
TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/clone3/clone3_selftests.h b/tools/testing/selftests/clone3/clone3_selftests.h
index a0593e8950f0..2145d14956e5 100644
--- a/tools/testing/selftests/clone3/clone3_selftests.h
+++ b/tools/testing/selftests/clone3/clone3_selftests.h
@@ -31,6 +31,7 @@ struct __clone_args {
__aligned_u64 set_tid;
__aligned_u64 set_tid_size;
__aligned_u64 cgroup;
+ __aligned_u64 cap_bset;
};
static pid_t sys_clone3(struct __clone_args *args, size_t size)
--
2.56.0.313.g2479322e68-goog
next prev parent reply other threads:[~2026-10-28 22:18 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-28 22:16 [PATCH 0/8] userns: per-namespace capability bounding set and opt-in netns loopback Hallgrimur H. Gunnarsson
2026-10-28 22:16 ` [PATCH 1/8] net: introduce CLONE_NEWNET_LOOPBACK to auto-enable loopback Hallgrimur H. Gunnarsson
2026-10-29 22:18 ` sashiko-bot
2026-10-28 22:16 ` [PATCH 2/8] userns: introduce capability bounding set for user namespaces Hallgrimur H. Gunnarsson
2026-10-29 22:18 ` sashiko-bot
2026-10-28 22:17 ` [PATCH 3/8] userns: allow restricting capability bounding set via clone3() Hallgrimur H. Gunnarsson
2026-10-29 22:18 ` sashiko-bot
2026-10-28 22:17 ` [PATCH 4/8] nsfs: add NS_USER_GET_INFO ioctl to inspect user namespace capabilities Hallgrimur H. Gunnarsson
2026-10-29 22:18 ` sashiko-bot
2026-10-28 22:17 ` [PATCH 5/8] selftests/net: add test suite for CLONE_NEWNET_LOOPBACK Hallgrimur H. Gunnarsson
2026-10-29 22:18 ` sashiko-bot
2026-10-28 22:17 ` [PATCH 6/8] selftests/capabilities: tests for user namespace capability bounding set Hallgrimur H. Gunnarsson
2026-10-29 22:18 ` sashiko-bot
2026-10-28 22:17 ` Hallgrimur H. Gunnarsson [this message]
2026-10-29 22:18 ` [PATCH 7/8] selftests/capabilities: add clone3() tests for CLONE_NEWUSER_BSET sashiko-bot
2026-10-28 22:17 ` [PATCH 8/8] selftests/nsfs: add tests for NS_USER_GET_INFO ioctl Hallgrimur H. Gunnarsson
2026-10-29 22:18 ` sashiko-bot
2026-10-28 22:56 ` [PATCH 0/8] userns: per-namespace capability bounding set and opt-in netns loopback Andy Lutomirski
2026-10-29 10:44 ` Alexey Gladkov
2026-10-29 12:11 ` Hallgrimur H. Gunnarsson
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=20261028221705.3915340-8-halg@google.com \
--to=halg@google.com \
--cc=brauner@kernel.org \
--cc=casey@schaufler-ca.com \
--cc=containers@lists.linux.dev \
--cc=corbet@lwn.net \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=gnoack@google.com \
--cc=horms@kernel.org \
--cc=jack@suse.cz \
--cc=jannh@google.com \
--cc=jcalmels@3xx0.net \
--cc=jmorris@namei.org \
--cc=kees@kernel.org \
--cc=kpsingh@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-api@vger.kernel.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=paul@paul-moore.com \
--cc=rdunlap@infradead.org \
--cc=sergeh@kernel.org \
--cc=skhan@linuxfoundation.org \
--cc=viro@zeniv.linux.org.uk \
/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.