[PATCH v2 2/2] keys: Add KUnit coverage for KEYCTL_MOVE keep guard

Liu Mingyu <[email protected]> Sun, 31 May 2026 20:51:51 +0000
Newsgroups org.kernel.vger.keyrings,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
Add keyring KUnit tests that exercise successful key_move(), rejection
of moving a protected key out of a protected source keyring, and the
same-keyring no-op path.

Keep the test-only infrastructure separate from the KEYCTL_MOVE fix so
the fix can be backported without pulling in the KUnit module.

Signed-off-by: Mingyu Liu <[email protected]>
---
 security/keys/Kconfig        |  13 ++++
 security/keys/Makefile       |   1 +
 security/keys/keyring_test.c | 121 +++++++++++++++++++++++++++++++++++
 3 files changed, 135 insertions(+)
 create mode 100644 security/keys/keyring_test.c

diff --git a/security/keys/Kconfig b/security/keys/Kconfig
index 84f39e50ca36..acffb5f7385c 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -129,4 +129,17 @@ config KEY_NOTIFICATIONS
 	  This makes use of pipes to handle the notification buffer and
 	  provides KEYCTL_WATCH_KEY to enable/disable watches.
 
+config KEYS_KUNIT_TEST
+	tristate "KUnit tests for keyrings" if !KUNIT_ALL_TESTS
+	depends on KUNIT
+	default KUNIT_ALL_TESTS
+	help
+	  Build KUnit tests for keyring operations.
+	  These tests exercise keyring link and move behavior, including
+	  protection of KEY_FLAG_KEEP entries.
+	  They are intended for KUnit runs on developer kernels and are not
+	  needed for normal systems.
+
+	  If you are unsure how to answer this question, answer N.
+
 endif # KEYS
diff --git a/security/keys/Makefile b/security/keys/Makefile
index 5f40807f05b3..fa583a4ea945 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_SYSCTL) += sysctl.o
 obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
 obj-$(CONFIG_KEY_DH_OPERATIONS) += dh.o
 obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += keyctl_pkey.o
+obj-$(CONFIG_KEYS_KUNIT_TEST) += keyring_test.o
 
 #
 # Key types
diff --git a/security/keys/keyring_test.c b/security/keys/keyring_test.c
new file mode 100644
index 000000000000..0055b50224e9
--- /dev/null
+++ b/security/keys/keyring_test.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * KUnit tests for keyring operations.
+ */
+
+#include <keys/user-type.h>
+#include <kunit/test.h>
+#include <linux/cred.h>
+#include <linux/err.h>
+#include <linux/key.h>
+#include <linux/key-type.h>
+#include <linux/keyctl.h>
+#include <linux/module.h>
+#include <linux/uidgid.h>
+
+static void keyring_test_key_put(void *data)
+{
+	key_put(data);
+}
+
+static struct key *test_keyring_alloc(struct kunit *test, const char *desc,
+				      unsigned long flags)
+{
+	struct key *keyring;
+
+	keyring = keyring_alloc(desc, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
+				current_cred(), KEY_POS_ALL | KEY_USR_ALL,
+				KEY_ALLOC_NOT_IN_QUOTA | flags, NULL, NULL);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, keyring);
+	KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test,
+							keyring_test_key_put,
+							keyring), 0);
+
+	return keyring;
+}
+
+static struct key *test_user_key_alloc(struct kunit *test, const char *desc,
+				       struct key *keyring,
+				       unsigned long flags)
+{
+	static const char payload[] = "payload";
+	struct key *key;
+	int ret;
+
+	key = key_alloc(&key_type_user, desc, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
+			current_cred(), KEY_POS_ALL | KEY_USR_ALL,
+			KEY_ALLOC_NOT_IN_QUOTA | flags, NULL);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, key);
+	KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test,
+							keyring_test_key_put,
+							key), 0);
+
+	ret = key_instantiate_and_link(key, payload, sizeof(payload),
+				       keyring, NULL);
+	KUNIT_ASSERT_EQ(test, ret, 0);
+
+	return key;
+}
+
+static void keyring_move_user_key(struct kunit *test)
+{
+	struct key *from, *to, *key;
+	int ret;
+
+	from = test_keyring_alloc(test, "move-from", 0);
+	to = test_keyring_alloc(test, "move-to", 0);
+	key = test_user_key_alloc(test, "move-key", from, 0);
+
+	ret = key_move(key, from, to, 0);
+	KUNIT_EXPECT_EQ(test, ret, 0);
+
+	ret = key_move(key, to, from, 0);
+	KUNIT_EXPECT_EQ(test, ret, 0);
+}
+
+static void keyring_move_keep_key_fails(struct kunit *test)
+{
+	struct key *from, *to, *key;
+	int ret;
+
+	from = test_keyring_alloc(test, "keep-from", KEY_ALLOC_SET_KEEP);
+	to = test_keyring_alloc(test, "keep-to", 0);
+	key = test_user_key_alloc(test, "keep-key", from, 0);
+
+	KUNIT_ASSERT_TRUE(test, test_bit(KEY_FLAG_KEEP, &from->flags));
+	KUNIT_ASSERT_TRUE(test, test_bit(KEY_FLAG_KEEP, &key->flags));
+
+	ret = key_move(key, from, to, 0);
+	KUNIT_EXPECT_EQ(test, ret, -EPERM);
+
+	ret = key_move(key, to, from, 0);
+	KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+}
+
+static void keyring_move_keep_same_keyring(struct kunit *test)
+{
+	struct key *keyring, *key;
+	int ret;
+
+	keyring = test_keyring_alloc(test, "keep-same", KEY_ALLOC_SET_KEEP);
+	key = test_user_key_alloc(test, "keep-same-key", keyring, 0);
+
+	ret = key_move(key, keyring, keyring, 0);
+	KUNIT_EXPECT_EQ(test, ret, 0);
+}
+
+static struct kunit_case keyring_test_cases[] = {
+	KUNIT_CASE(keyring_move_user_key),
+	KUNIT_CASE(keyring_move_keep_key_fails),
+	KUNIT_CASE(keyring_move_keep_same_keyring),
+	{}
+};
+
+static struct kunit_suite keyring_test_suite = {
+	.name = "keyring",
+	.test_cases = keyring_test_cases,
+};
+
+kunit_test_suite(keyring_test_suite);
+
+MODULE_LICENSE("GPL");
-- 
2.51.2.windows.1