Add basic tests for the kpkeys_hardened_pgtables feature: try to perform a direct write to current->{cred,real_cred} and ensure it fails. Also check that prepare_creds, protect_creds, prepare_protected_creds behave as expected. Signed-off-by: Kevin Brodsky --- mm/Makefile | 1 + mm/tests/kpkeys_hardened_cred_kunit.c | 79 +++++++++++++++++++++++++++ security/Kconfig.hardening | 11 ++++ 3 files changed, 91 insertions(+) create mode 100644 mm/tests/kpkeys_hardened_cred_kunit.c diff --git a/mm/Makefile b/mm/Makefile index b1e6cf7f753c..c79af57c0aa5 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -149,3 +149,4 @@ obj-$(CONFIG_TMPFS_QUOTA) += shmem_quota.o obj-$(CONFIG_PT_RECLAIM) += pt_reclaim.o obj-$(CONFIG_KPKEYS_HARDENED_PGTABLES) += kpkeys_hardened_pgtables.o obj-$(CONFIG_KPKEYS_HARDENED_PGTABLES_KUNIT_TEST) += tests/kpkeys_hardened_pgtables_kunit.o +obj-$(CONFIG_KPKEYS_HARDENED_CRED_KUNIT_TEST) += tests/kpkeys_hardened_cred_kunit.o diff --git a/mm/tests/kpkeys_hardened_cred_kunit.c b/mm/tests/kpkeys_hardened_cred_kunit.c new file mode 100644 index 000000000000..ed07469b504c --- /dev/null +++ b/mm/tests/kpkeys_hardened_cred_kunit.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include + +static int increment_cred_uid_nofault(struct cred *cred) +{ + uid_t val = __kuid_val(cred->uid) + 1; + + return copy_to_kernel_nofault(&cred->uid, &val, sizeof(cred->uid)); +} + +static void write_current_creds(struct kunit *test) +{ + int ret; + + if (!arch_kpkeys_enabled()) + kunit_skip(test, "kpkeys are not supported"); + + ret = increment_cred_uid_nofault((struct cred *)current->cred); + KUNIT_EXPECT_EQ_MSG(test, ret, -EFAULT, + "Write to current->cred wasn't prevented"); + + ret = increment_cred_uid_nofault((struct cred *)current->real_cred); + KUNIT_EXPECT_EQ_MSG(test, ret, -EFAULT, + "Write to current->real_cred wasn't prevented"); +} + +static void write_new_creds(struct kunit *test) +{ + struct cred *cred, *protected_cred; + int ret; + + if (!arch_kpkeys_enabled()) + kunit_skip(test, "kpkeys are not supported"); + + /* prepare_creds() + protect_creds() */ + cred = prepare_creds(); + KUNIT_ASSERT_NOT_NULL(test, cred); + + ret = increment_cred_uid_nofault(cred); + KUNIT_EXPECT_EQ_MSG(test, ret, 0, + "Failed to write to unprotected creds"); + + protected_cred = protect_creds(cred); + KUNIT_EXPECT_PTR_NE_MSG(test, cred, protected_cred, + "protect_creds() failed to move creds to protected memory"); + + ret = increment_cred_uid_nofault(protected_cred); + KUNIT_EXPECT_EQ_MSG(test, ret, -EFAULT, + "Write to protected_cred wasn't prevented"); + + put_cred(protected_cred); + + /* prepare_protected_creds() */ + protected_cred = prepare_protected_creds(); + + ret = increment_cred_uid_nofault(protected_cred); + KUNIT_EXPECT_EQ_MSG(test, ret, -EFAULT, + "Write to protected_cred wasn't prevented"); + + put_cred(protected_cred); + +} + +static struct kunit_case kpkeys_hardened_cred_test_cases[] = { + KUNIT_CASE(write_current_creds), + KUNIT_CASE(write_new_creds), + {} +}; + +static struct kunit_suite kpkeys_hardened_cred_test_suite = { + .name = "Hardened credentials using kpkeys", + .test_cases = kpkeys_hardened_cred_test_cases, +}; +kunit_test_suite(kpkeys_hardened_cred_test_suite); + +MODULE_DESCRIPTION("Tests for the kpkeys_hardened_cred feature"); +MODULE_LICENSE("GPL"); diff --git a/security/Kconfig.hardening b/security/Kconfig.hardening index cb494448c7ae..7ceb1e6846f2 100644 --- a/security/Kconfig.hardening +++ b/security/Kconfig.hardening @@ -302,6 +302,17 @@ config KPKEYS_HARDENED_CRED This option has no effect if the system does not support kernel pkeys. +config KPKEYS_HARDENED_CRED_KUNIT_TEST + tristate "KUnit tests for kpkeys_hardened_cred" if !KUNIT_ALL_TESTS + depends on KPKEYS_HARDENED_CRED + depends on KUNIT + default KUNIT_ALL_TESTS + help + Enable this option to check that the kpkeys_hardened_cred feature + functions as intended, i.e. prevents arbitrary writes to live credentials. + + If unsure, say N. + endmenu config CC_HAS_RANDSTRUCT -- 2.47.0