* [PATCH] security: Add KUnit tests for rootid_owns_currentns()
@ 2025-11-10 14:37 Ryan Foster
2025-11-11 0:33 ` Serge E. Hallyn
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: Ryan Foster @ 2025-11-10 14:37 UTC (permalink / raw)
To: selinux; +Cc: linux-security-module, linux-kernel, serge, paul, Ryan Foster
The rootid_owns_currentns() function in security/commoncap.c is a
security-critical function used to determine if a root ID from a
parent namespace owns the current namespace. This function is used
in multiple security-critical paths:
1. cap_inode_getsecurity() - file capability retrieval
2. get_vfs_caps_from_disk() - file capability loading during exec
Despite its security-critical nature, this function had no test
coverage. This patch adds KUnit tests to verify the function's
behavior in various scenarios:
- Root ID in init namespace (positive case)
- Invalid vfsuid (negative case)
- Non-root UID (negative case)
The function is made testable by exporting it when
CONFIG_SECURITY_COMMONCAP_KUNIT_TEST is enabled, while maintaining
static visibility in production builds.
This follows the pattern established by other security subsystems
(IPE, Landlock) for KUnit testing.
Signed-off-by: Ryan Foster <foster.ryan.r@gmail.com>
---
security/Kconfig | 17 ++++++
security/Makefile | 1 +
security/commoncap.c | 5 ++
security/commoncap_test.c | 109 ++++++++++++++++++++++++++++++++++++++
4 files changed, 132 insertions(+)
create mode 100644 security/commoncap_test.c
diff --git a/security/Kconfig b/security/Kconfig
index 285f284dfcac..c7b3f42ef875 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -284,6 +284,23 @@ config LSM
If unsure, leave this as the default.
+config SECURITY_COMMONCAP_KUNIT_TEST
+ bool "Build KUnit tests for commoncap" if !KUNIT_ALL_TESTS
+ depends on KUNIT=y
+ default KUNIT_ALL_TESTS
+ help
+ This builds the commoncap KUnit tests.
+
+ KUnit tests run during boot and output the results to the debug log
+ in TAP format (https://testanything.org/). Only useful for kernel devs
+ running KUnit test harness and are not for inclusion into a
+ production build.
+
+ For more information on KUnit and unit tests in general please refer
+ to the KUnit documentation in Documentation/dev-tools/kunit/.
+
+ If unsure, say N.
+
source "security/Kconfig.hardening"
endmenu
diff --git a/security/Makefile b/security/Makefile
index 22ff4c8bd8ce..5b1285fde552 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_KEYS) += keys/
# always enable default capabilities
obj-y += commoncap.o
+obj-$(CONFIG_SECURITY_COMMONCAP_KUNIT_TEST) += commoncap_test.o
obj-$(CONFIG_SECURITY) += lsm_syscalls.o
obj-$(CONFIG_MMU) += min_addr.o
diff --git a/security/commoncap.c b/security/commoncap.c
index 6bd4adeb4795..4d0e014ce7cd 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -358,7 +358,12 @@ int cap_inode_killpriv(struct mnt_idmap *idmap, struct dentry *dentry)
return error;
}
+#ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST
+bool rootid_owns_currentns(vfsuid_t rootvfsuid);
+bool rootid_owns_currentns(vfsuid_t rootvfsuid)
+#else
static bool rootid_owns_currentns(vfsuid_t rootvfsuid)
+#endif
{
struct user_namespace *ns;
kuid_t kroot;
diff --git a/security/commoncap_test.c b/security/commoncap_test.c
new file mode 100644
index 000000000000..9757d433d314
--- /dev/null
+++ b/security/commoncap_test.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * KUnit tests for commoncap.c security functions
+ *
+ * Tests for security-critical functions in the capability subsystem,
+ * particularly namespace-related capability checks.
+ */
+
+#include <kunit/test.h>
+#include <linux/user_namespace.h>
+#include <linux/uidgid.h>
+#include <linux/module.h>
+
+/* We need to include the actual vfsuid_t definition but avoid the problematic
+ * inline functions in mnt_idmapping.h. Include just the type definitions.
+ */
+#include <linux/types.h>
+
+/* Forward declare the minimal types we need - match the actual kernel definitions */
+struct mnt_idmap;
+typedef struct {
+ uid_t val;
+} vfsuid_t;
+
+/* Minimal macros we need - match kernel definitions from mnt_idmapping.h */
+static inline uid_t __vfsuid_val(vfsuid_t uid)
+{
+ return uid.val;
+}
+
+#define VFSUIDT_INIT(val) ((vfsuid_t){ __kuid_val(val) })
+#define INVALID_VFSUID VFSUIDT_INIT(INVALID_UID)
+
+#ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST
+
+/* Forward declaration - function is exported when KUNIT_TEST is enabled */
+extern bool rootid_owns_currentns(vfsuid_t rootvfsuid);
+
+/**
+ * test_rootid_owns_currentns_init_ns - Test rootid_owns_currentns with init ns
+ *
+ * Verifies that a root ID in the init namespace correctly owns the current
+ * namespace when running in init_user_ns.
+ */
+static void test_rootid_owns_currentns_init_ns(struct kunit *test)
+{
+ vfsuid_t root_vfsuid;
+ kuid_t root_kuid;
+
+ /* Create a root UID in init namespace */
+ root_kuid = KUIDT_INIT(0);
+ root_vfsuid = VFSUIDT_INIT(root_kuid);
+
+ /* In init namespace, root should own current namespace */
+ KUNIT_EXPECT_TRUE(test, rootid_owns_currentns(root_vfsuid));
+}
+
+/**
+ * test_rootid_owns_currentns_invalid - Test rootid_owns_currentns with invalid vfsuid
+ *
+ * Verifies that an invalid vfsuid correctly returns false.
+ */
+static void test_rootid_owns_currentns_invalid(struct kunit *test)
+{
+ vfsuid_t invalid_vfsuid;
+
+ /* Use the predefined invalid vfsuid */
+ invalid_vfsuid = INVALID_VFSUID;
+
+ /* Invalid vfsuid should return false */
+ KUNIT_EXPECT_FALSE(test, rootid_owns_currentns(invalid_vfsuid));
+}
+
+/**
+ * test_rootid_owns_currentns_nonroot - Test rootid_owns_currentns with non-root UID
+ *
+ * Verifies that a non-root UID correctly returns false.
+ */
+static void test_rootid_owns_currentns_nonroot(struct kunit *test)
+{
+ vfsuid_t nonroot_vfsuid;
+ kuid_t nonroot_kuid;
+
+ /* Create a non-root UID */
+ nonroot_kuid = KUIDT_INIT(1000);
+ nonroot_vfsuid = VFSUIDT_INIT(nonroot_kuid);
+
+ /* Non-root UID should return false */
+ KUNIT_EXPECT_FALSE(test, rootid_owns_currentns(nonroot_vfsuid));
+}
+
+static struct kunit_case commoncap_test_cases[] = {
+ KUNIT_CASE(test_rootid_owns_currentns_init_ns),
+ KUNIT_CASE(test_rootid_owns_currentns_invalid),
+ KUNIT_CASE(test_rootid_owns_currentns_nonroot),
+ {}
+};
+
+static struct kunit_suite commoncap_test_suite = {
+ .name = "commoncap",
+ .test_cases = commoncap_test_cases,
+};
+
+kunit_test_suite(commoncap_test_suite);
+
+MODULE_LICENSE("GPL");
+
+#endif /* CONFIG_SECURITY_COMMONCAP_KUNIT_TEST */
+
--
2.43.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH] security: Add KUnit tests for rootid_owns_currentns()
2025-11-10 14:37 [PATCH] security: Add KUnit tests for rootid_owns_currentns() Ryan Foster
@ 2025-11-11 0:33 ` Serge E. Hallyn
2025-11-11 6:52 ` kernel test robot
` (2 subsequent siblings)
3 siblings, 0 replies; 8+ messages in thread
From: Serge E. Hallyn @ 2025-11-11 0:33 UTC (permalink / raw)
To: Ryan Foster; +Cc: selinux, linux-security-module, linux-kernel, serge, paul
On Mon, Nov 10, 2025 at 06:37:48AM -0800, Ryan Foster wrote:
> The rootid_owns_currentns() function in security/commoncap.c is a
> security-critical function used to determine if a root ID from a
> parent namespace owns the current namespace. This function is used
> in multiple security-critical paths:
>
> 1. cap_inode_getsecurity() - file capability retrieval
> 2. get_vfs_caps_from_disk() - file capability loading during exec
>
> Despite its security-critical nature, this function had no test
> coverage. This patch adds KUnit tests to verify the function's
> behavior in various scenarios:
>
> - Root ID in init namespace (positive case)
> - Invalid vfsuid (negative case)
> - Non-root UID (negative case)
I'd be more excited about this if it tested the actual
functionality.
The rootid_owns_currentns() could be split so that it calls
a (static if not testing) inline rootid_owns_userns(), and
then you test the latter. Then create a few user namespaces
with different values for the namespace's uid 0, and make
sure rootid_owns_userns(testns, testuid) behaves correctly.
Actually, this function should probably be renamed.
"rootid_owns_currentns()" is not correct. It should just be
"uid_owns_currentns()".
> The function is made testable by exporting it when
> CONFIG_SECURITY_COMMONCAP_KUNIT_TEST is enabled, while maintaining
> static visibility in production builds.
>
> This follows the pattern established by other security subsystems
> (IPE, Landlock) for KUnit testing.
>
> Signed-off-by: Ryan Foster <foster.ryan.r@gmail.com>
> ---
> security/Kconfig | 17 ++++++
> security/Makefile | 1 +
> security/commoncap.c | 5 ++
> security/commoncap_test.c | 109 ++++++++++++++++++++++++++++++++++++++
> 4 files changed, 132 insertions(+)
> create mode 100644 security/commoncap_test.c
>
> diff --git a/security/Kconfig b/security/Kconfig
> index 285f284dfcac..c7b3f42ef875 100644
> --- a/security/Kconfig
> +++ b/security/Kconfig
> @@ -284,6 +284,23 @@ config LSM
>
> If unsure, leave this as the default.
>
> +config SECURITY_COMMONCAP_KUNIT_TEST
> + bool "Build KUnit tests for commoncap" if !KUNIT_ALL_TESTS
> + depends on KUNIT=y
> + default KUNIT_ALL_TESTS
> + help
> + This builds the commoncap KUnit tests.
> +
> + KUnit tests run during boot and output the results to the debug log
> + in TAP format (https://testanything.org/). Only useful for kernel devs
> + running KUnit test harness and are not for inclusion into a
> + production build.
> +
> + For more information on KUnit and unit tests in general please refer
> + to the KUnit documentation in Documentation/dev-tools/kunit/.
> +
> + If unsure, say N.
> +
> source "security/Kconfig.hardening"
>
> endmenu
> diff --git a/security/Makefile b/security/Makefile
> index 22ff4c8bd8ce..5b1285fde552 100644
> --- a/security/Makefile
> +++ b/security/Makefile
> @@ -7,6 +7,7 @@ obj-$(CONFIG_KEYS) += keys/
>
> # always enable default capabilities
> obj-y += commoncap.o
> +obj-$(CONFIG_SECURITY_COMMONCAP_KUNIT_TEST) += commoncap_test.o
> obj-$(CONFIG_SECURITY) += lsm_syscalls.o
> obj-$(CONFIG_MMU) += min_addr.o
>
> diff --git a/security/commoncap.c b/security/commoncap.c
> index 6bd4adeb4795..4d0e014ce7cd 100644
> --- a/security/commoncap.c
> +++ b/security/commoncap.c
> @@ -358,7 +358,12 @@ int cap_inode_killpriv(struct mnt_idmap *idmap, struct dentry *dentry)
> return error;
> }
>
> +#ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST
> +bool rootid_owns_currentns(vfsuid_t rootvfsuid);
> +bool rootid_owns_currentns(vfsuid_t rootvfsuid)
> +#else
> static bool rootid_owns_currentns(vfsuid_t rootvfsuid)
> +#endif
> {
> struct user_namespace *ns;
> kuid_t kroot;
> diff --git a/security/commoncap_test.c b/security/commoncap_test.c
> new file mode 100644
> index 000000000000..9757d433d314
> --- /dev/null
> +++ b/security/commoncap_test.c
> @@ -0,0 +1,109 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * KUnit tests for commoncap.c security functions
> + *
> + * Tests for security-critical functions in the capability subsystem,
> + * particularly namespace-related capability checks.
> + */
> +
> +#include <kunit/test.h>
> +#include <linux/user_namespace.h>
> +#include <linux/uidgid.h>
> +#include <linux/module.h>
> +
> +/* We need to include the actual vfsuid_t definition but avoid the problematic
> + * inline functions in mnt_idmapping.h. Include just the type definitions.
> + */
> +#include <linux/types.h>
> +
> +/* Forward declare the minimal types we need - match the actual kernel definitions */
> +struct mnt_idmap;
> +typedef struct {
> + uid_t val;
> +} vfsuid_t;
> +
> +/* Minimal macros we need - match kernel definitions from mnt_idmapping.h */
> +static inline uid_t __vfsuid_val(vfsuid_t uid)
> +{
> + return uid.val;
> +}
> +
> +#define VFSUIDT_INIT(val) ((vfsuid_t){ __kuid_val(val) })
> +#define INVALID_VFSUID VFSUIDT_INIT(INVALID_UID)
> +
> +#ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST
> +
> +/* Forward declaration - function is exported when KUNIT_TEST is enabled */
> +extern bool rootid_owns_currentns(vfsuid_t rootvfsuid);
> +
> +/**
> + * test_rootid_owns_currentns_init_ns - Test rootid_owns_currentns with init ns
> + *
> + * Verifies that a root ID in the init namespace correctly owns the current
> + * namespace when running in init_user_ns.
> + */
> +static void test_rootid_owns_currentns_init_ns(struct kunit *test)
> +{
> + vfsuid_t root_vfsuid;
> + kuid_t root_kuid;
> +
> + /* Create a root UID in init namespace */
> + root_kuid = KUIDT_INIT(0);
> + root_vfsuid = VFSUIDT_INIT(root_kuid);
> +
> + /* In init namespace, root should own current namespace */
> + KUNIT_EXPECT_TRUE(test, rootid_owns_currentns(root_vfsuid));
> +}
> +
> +/**
> + * test_rootid_owns_currentns_invalid - Test rootid_owns_currentns with invalid vfsuid
> + *
> + * Verifies that an invalid vfsuid correctly returns false.
> + */
> +static void test_rootid_owns_currentns_invalid(struct kunit *test)
> +{
> + vfsuid_t invalid_vfsuid;
> +
> + /* Use the predefined invalid vfsuid */
> + invalid_vfsuid = INVALID_VFSUID;
> +
> + /* Invalid vfsuid should return false */
> + KUNIT_EXPECT_FALSE(test, rootid_owns_currentns(invalid_vfsuid));
> +}
> +
> +/**
> + * test_rootid_owns_currentns_nonroot - Test rootid_owns_currentns with non-root UID
> + *
> + * Verifies that a non-root UID correctly returns false.
> + */
> +static void test_rootid_owns_currentns_nonroot(struct kunit *test)
> +{
> + vfsuid_t nonroot_vfsuid;
> + kuid_t nonroot_kuid;
> +
> + /* Create a non-root UID */
> + nonroot_kuid = KUIDT_INIT(1000);
> + nonroot_vfsuid = VFSUIDT_INIT(nonroot_kuid);
> +
> + /* Non-root UID should return false */
> + KUNIT_EXPECT_FALSE(test, rootid_owns_currentns(nonroot_vfsuid));
> +}
> +
> +static struct kunit_case commoncap_test_cases[] = {
> + KUNIT_CASE(test_rootid_owns_currentns_init_ns),
> + KUNIT_CASE(test_rootid_owns_currentns_invalid),
> + KUNIT_CASE(test_rootid_owns_currentns_nonroot),
> + {}
> +};
> +
> +static struct kunit_suite commoncap_test_suite = {
> + .name = "commoncap",
> + .test_cases = commoncap_test_cases,
> +};
> +
> +kunit_test_suite(commoncap_test_suite);
> +
> +MODULE_LICENSE("GPL");
> +
> +#endif /* CONFIG_SECURITY_COMMONCAP_KUNIT_TEST */
> +
> --
> 2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] security: Add KUnit tests for rootid_owns_currentns()
2025-11-10 14:37 [PATCH] security: Add KUnit tests for rootid_owns_currentns() Ryan Foster
2025-11-11 0:33 ` Serge E. Hallyn
@ 2025-11-11 6:52 ` kernel test robot
2025-11-11 7:03 ` kernel test robot
2025-12-30 15:13 ` [PATCH v5] security: Add KUnit tests for kuid_root_in_ns and vfsuid_root_in_currentns Ryan Foster
3 siblings, 0 replies; 8+ messages in thread
From: kernel test robot @ 2025-11-11 6:52 UTC (permalink / raw)
To: Ryan Foster, selinux
Cc: oe-kbuild-all, linux-security-module, linux-kernel, serge, paul,
Ryan Foster
Hi Ryan,
kernel test robot noticed the following build errors:
[auto build test ERROR on pcmoore-selinux/next]
[also build test ERROR on linus/master v6.18-rc5 next-20251110]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Ryan-Foster/security-Add-KUnit-tests-for-rootid_owns_currentns/20251110-223824
base: https://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux.git next
patch link: https://lore.kernel.org/r/20251110143748.4144288-1-foster.ryan.r%40gmail.com
patch subject: [PATCH] security: Add KUnit tests for rootid_owns_currentns()
config: mips-randconfig-r053-20251111 (https://download.01.org/0day-ci/archive/20251111/202511111459.1ToyUP5p-lkp@intel.com/config)
compiler: mips-linux-gcc (GCC) 8.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251111/202511111459.1ToyUP5p-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202511111459.1ToyUP5p-lkp@intel.com/
All error/warnings (new ones prefixed by >>):
>> security/commoncap_test.c:23:3: error: conflicting types for 'vfsuid_t'
} vfsuid_t;
^~~~~~~~
In file included from include/linux/fs.h:45,
from arch/mips/include/asm/elf.h:12,
from include/linux/elf.h:6,
from include/linux/module.h:20,
from include/kunit/test.h:24,
from security/commoncap_test.c:9:
include/linux/mnt_idmapping.h:17:3: note: previous declaration of 'vfsuid_t' was here
} vfsuid_t;
^~~~~~~~
>> security/commoncap_test.c:26:21: error: conflicting types for '__vfsuid_val'
static inline uid_t __vfsuid_val(vfsuid_t uid)
^~~~~~~~~~~~
In file included from include/linux/fs.h:45,
from arch/mips/include/asm/elf.h:12,
from include/linux/elf.h:6,
from include/linux/module.h:20,
from include/kunit/test.h:24,
from security/commoncap_test.c:9:
include/linux/mnt_idmapping.h:34:21: note: previous definition of '__vfsuid_val' was here
static inline uid_t __vfsuid_val(vfsuid_t uid)
^~~~~~~~~~~~
>> security/commoncap_test.c:31: warning: "VFSUIDT_INIT" redefined
#define VFSUIDT_INIT(val) ((vfsuid_t){ __kuid_val(val) })
In file included from include/linux/fs.h:45,
from arch/mips/include/asm/elf.h:12,
from include/linux/elf.h:6,
from include/linux/module.h:20,
from include/kunit/test.h:24,
from security/commoncap_test.c:9:
include/linux/mnt_idmapping.h:109: note: this is the location of the previous definition
#define VFSUIDT_INIT(val) (vfsuid_t){ __kuid_val(val) }
vim +/vfsuid_t +23 security/commoncap_test.c
18
19 /* Forward declare the minimal types we need - match the actual kernel definitions */
20 struct mnt_idmap;
21 typedef struct {
22 uid_t val;
> 23 } vfsuid_t;
24
25 /* Minimal macros we need - match kernel definitions from mnt_idmapping.h */
> 26 static inline uid_t __vfsuid_val(vfsuid_t uid)
27 {
28 return uid.val;
29 }
30
> 31 #define VFSUIDT_INIT(val) ((vfsuid_t){ __kuid_val(val) })
32 #define INVALID_VFSUID VFSUIDT_INIT(INVALID_UID)
33
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] security: Add KUnit tests for rootid_owns_currentns()
2025-11-10 14:37 [PATCH] security: Add KUnit tests for rootid_owns_currentns() Ryan Foster
2025-11-11 0:33 ` Serge E. Hallyn
2025-11-11 6:52 ` kernel test robot
@ 2025-11-11 7:03 ` kernel test robot
2025-12-30 15:13 ` [PATCH v5] security: Add KUnit tests for kuid_root_in_ns and vfsuid_root_in_currentns Ryan Foster
3 siblings, 0 replies; 8+ messages in thread
From: kernel test robot @ 2025-11-11 7:03 UTC (permalink / raw)
To: Ryan Foster, selinux
Cc: oe-kbuild-all, linux-security-module, linux-kernel, serge, paul,
Ryan Foster
Hi Ryan,
kernel test robot noticed the following build warnings:
[auto build test WARNING on pcmoore-selinux/next]
[also build test WARNING on linus/master v6.18-rc5 next-20251110]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Ryan-Foster/security-Add-KUnit-tests-for-rootid_owns_currentns/20251110-223824
base: https://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux.git next
patch link: https://lore.kernel.org/r/20251110143748.4144288-1-foster.ryan.r%40gmail.com
patch subject: [PATCH] security: Add KUnit tests for rootid_owns_currentns()
config: x86_64-rhel-9.4-kunit (https://download.01.org/0day-ci/archive/20251111/202511111453.e1clsyyc-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251111/202511111453.e1clsyyc-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202511111453.e1clsyyc-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> Warning: security/commoncap_test.c:45 function parameter 'test' not described in 'test_rootid_owns_currentns_init_ns'
>> Warning: security/commoncap_test.c:63 function parameter 'test' not described in 'test_rootid_owns_currentns_invalid'
>> Warning: security/commoncap_test.c:79 function parameter 'test' not described in 'test_rootid_owns_currentns_nonroot'
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v5] security: Add KUnit tests for kuid_root_in_ns and vfsuid_root_in_currentns
2025-11-10 14:37 [PATCH] security: Add KUnit tests for rootid_owns_currentns() Ryan Foster
` (2 preceding siblings ...)
2025-11-11 7:03 ` kernel test robot
@ 2025-12-30 15:13 ` Ryan Foster
2026-01-06 21:07 ` Serge E. Hallyn
3 siblings, 1 reply; 8+ messages in thread
From: Ryan Foster @ 2025-12-30 15:13 UTC (permalink / raw)
To: foster.ryan.r; +Cc: linux-kernel, linux-security-module, paul, selinux, serge
Hi all,
Sorry for the spam, this aims to fix both issues. Attempted to reproduce
CI config build locally.
Thanks, Ryan
---
Add comprehensive KUnit tests for the namespace-related capability
functions that Serge Hallyn refactored in commit 9891d2f79a9f
("Clarify the rootid_owns_currentns").
The tests verify:
- Basic functionality: UID 0 in init namespace, invalid vfsuid,
non-zero UIDs
- Actual namespace traversal: Creating user namespaces with different
UID mappings where uid 0 maps to different kuids (e.g., 1000, 2000,
3000)
- Hierarchy traversal: Testing multiple nested namespaces to verify
correct namespace hierarchy traversal
This addresses the feedback to "test the actual functionality" by
creating real user namespaces with different values for the
namespace's uid 0, rather than just basic input validation.
The test file is included at the end of commoncap.c when
CONFIG_SECURITY_COMMONCAP_KUNIT_TEST is enabled, following the
standard kernel pattern (e.g., scsi_lib.c, ext4/mballoc.c). This
allows tests to access static functions in the same compilation unit
without modifying production code based on test configuration.
The tests require CONFIG_USER_NS to be enabled since they rely on user
namespace mapping functionality. The Kconfig dependency ensures the
tests only build when this requirement is met.
All 7 tests pass:
- test_vfsuid_root_in_currentns_init_ns
- test_vfsuid_root_in_currentns_invalid
- test_vfsuid_root_in_currentns_nonzero
- test_kuid_root_in_ns_init_ns_uid0
- test_kuid_root_in_ns_init_ns_nonzero
- test_kuid_root_in_ns_with_mapping
- test_kuid_root_in_ns_with_different_mappings
Updated MAINTAINER capabilities to include commoncap test
Signed-off-by: Ryan Foster <foster.ryan.r@gmail.com>
---
MAINTAINERS | 1 +
security/Kconfig | 17 +++
security/commoncap.c | 4 +
security/commoncap_test.c | 290 ++++++++++++++++++++++++++++++++++++++
4 files changed, 312 insertions(+)
create mode 100644 security/commoncap_test.c
diff --git a/MAINTAINERS b/MAINTAINERS
index c0030e126fc8..6f162c736dfb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5682,6 +5682,7 @@ F: include/trace/events/capability.h
F: include/uapi/linux/capability.h
F: kernel/capability.c
F: security/commoncap.c
+F: security/commoncap_test.c
CAPELLA MICROSYSTEMS LIGHT SENSOR DRIVER
M: Kevin Tsai <ktsai@capellamicro.com>
diff --git a/security/Kconfig b/security/Kconfig
index 285f284dfcac..6a4393fce9a1 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -284,6 +284,23 @@ config LSM
If unsure, leave this as the default.
+config SECURITY_COMMONCAP_KUNIT_TEST
+ bool "Build KUnit tests for commoncap" if !KUNIT_ALL_TESTS
+ depends on KUNIT=y && USER_NS
+ default KUNIT_ALL_TESTS
+ help
+ This builds the commoncap KUnit tests.
+
+ KUnit tests run during boot and output the results to the debug log
+ in TAP format (https://testanything.org/). Only useful for kernel devs
+ running KUnit test harness and are not for inclusion into a
+ production build.
+
+ For more information on KUnit and unit tests in general please refer
+ to the KUnit documentation in Documentation/dev-tools/kunit/.
+
+ If unsure, say N.
+
source "security/Kconfig.hardening"
endmenu
diff --git a/security/commoncap.c b/security/commoncap.c
index 8a23dfab7fac..3399535808fe 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -1521,3 +1521,7 @@ DEFINE_LSM(capability) = {
};
#endif /* CONFIG_SECURITY */
+
+#ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST
+#include "commoncap_test.c"
+#endif
diff --git a/security/commoncap_test.c b/security/commoncap_test.c
new file mode 100644
index 000000000000..1088364a54e6
--- /dev/null
+++ b/security/commoncap_test.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * KUnit tests for commoncap.c security functions
+ *
+ * Tests for security-critical functions in the capability subsystem,
+ * particularly namespace-related capability checks.
+ */
+
+#include <kunit/test.h>
+#include <linux/user_namespace.h>
+#include <linux/uidgid.h>
+#include <linux/cred.h>
+#include <linux/mnt_idmapping.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/refcount.h>
+
+#ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST
+
+/* Functions are static in commoncap.c, but we can call them since we're
+ * included in the same compilation unit when tests are enabled.
+ */
+
+/**
+ * test_vfsuid_root_in_currentns_init_ns - Test vfsuid_root_in_currentns with init ns
+ *
+ * Verifies that UID 0 in the init namespace correctly owns the current
+ * namespace when running in init_user_ns.
+ *
+ * @test: KUnit test context
+ */
+static void test_vfsuid_root_in_currentns_init_ns(struct kunit *test)
+{
+ vfsuid_t vfsuid;
+ kuid_t kuid;
+
+ /* Create UID 0 in init namespace */
+ kuid = KUIDT_INIT(0);
+ vfsuid = VFSUIDT_INIT(kuid);
+
+ /* In init namespace, UID 0 should own current namespace */
+ KUNIT_EXPECT_TRUE(test, vfsuid_root_in_currentns(vfsuid));
+}
+
+/**
+ * test_vfsuid_root_in_currentns_invalid - Test vfsuid_root_in_currentns with invalid vfsuid
+ *
+ * Verifies that an invalid vfsuid correctly returns false.
+ *
+ * @test: KUnit test context
+ */
+static void test_vfsuid_root_in_currentns_invalid(struct kunit *test)
+{
+ vfsuid_t invalid_vfsuid;
+
+ /* Use the predefined invalid vfsuid */
+ invalid_vfsuid = INVALID_VFSUID;
+
+ /* Invalid vfsuid should return false */
+ KUNIT_EXPECT_FALSE(test, vfsuid_root_in_currentns(invalid_vfsuid));
+}
+
+/**
+ * test_vfsuid_root_in_currentns_nonzero - Test vfsuid_root_in_currentns with non-zero UID
+ *
+ * Verifies that a non-zero UID correctly returns false.
+ *
+ * @test: KUnit test context
+ */
+static void test_vfsuid_root_in_currentns_nonzero(struct kunit *test)
+{
+ vfsuid_t vfsuid;
+ kuid_t kuid;
+
+ /* Create a non-zero UID */
+ kuid = KUIDT_INIT(1000);
+ vfsuid = VFSUIDT_INIT(kuid);
+
+ /* Non-zero UID should return false */
+ KUNIT_EXPECT_FALSE(test, vfsuid_root_in_currentns(vfsuid));
+}
+
+/**
+ * test_kuid_root_in_ns_init_ns_uid0 - Test kuid_root_in_ns with init namespace and UID 0
+ *
+ * Verifies that kuid_root_in_ns correctly identifies UID 0 in init namespace.
+ * This tests the core namespace traversal logic. In init namespace, UID 0
+ * maps to itself, so it should own the namespace.
+ *
+ * @test: KUnit test context
+ */
+static void test_kuid_root_in_ns_init_ns_uid0(struct kunit *test)
+{
+ kuid_t kuid;
+ struct user_namespace *init_ns;
+
+ kuid = KUIDT_INIT(0);
+ init_ns = &init_user_ns;
+
+ /* UID 0 should own init namespace */
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(kuid, init_ns));
+}
+
+/**
+ * test_kuid_root_in_ns_init_ns_nonzero - Test kuid_root_in_ns with init namespace and non-zero UID
+ *
+ * Verifies that kuid_root_in_ns correctly rejects non-zero UIDs in init namespace.
+ * Only UID 0 should own a namespace.
+ *
+ * @test: KUnit test context
+ */
+static void test_kuid_root_in_ns_init_ns_nonzero(struct kunit *test)
+{
+ kuid_t kuid;
+ struct user_namespace *init_ns;
+
+ kuid = KUIDT_INIT(1000);
+ init_ns = &init_user_ns;
+
+ /* Non-zero UID should not own namespace */
+ KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(kuid, init_ns));
+}
+
+/**
+ * create_test_user_ns_with_mapping - Create a mock user namespace with UID mapping
+ *
+ * Creates a minimal user namespace structure for testing where uid 0 in the
+ * namespace maps to a specific kuid in the parent namespace.
+ *
+ * @test: KUnit test context
+ * @parent_ns: Parent namespace (typically init_user_ns)
+ * @mapped_kuid: The kuid that uid 0 in this namespace maps to in parent
+ *
+ * Returns: Pointer to allocated namespace, or NULL on failure
+ */
+static struct user_namespace *create_test_user_ns_with_mapping(struct kunit *test,
+ struct user_namespace *parent_ns,
+ kuid_t mapped_kuid)
+{
+ struct user_namespace *ns;
+ struct uid_gid_extent extent;
+
+ /* Allocate a test namespace - use kzalloc to zero all fields */
+ ns = kunit_kzalloc(test, sizeof(*ns), GFP_KERNEL);
+ if (!ns)
+ return NULL;
+
+ /* Initialize basic namespace structure fields */
+ ns->parent = parent_ns;
+ ns->level = parent_ns ? parent_ns->level + 1 : 0;
+ ns->owner = mapped_kuid;
+ ns->group = KGIDT_INIT(0);
+
+ /* Initialize ns_common structure */
+ refcount_set(&ns->ns.__ns_ref, 1);
+ ns->ns.inum = 0; /* Mock inum */
+
+ /* Set up uid mapping: uid 0 in this namespace maps to mapped_kuid in parent
+ * Format: first (uid in ns) : lower_first (kuid in parent) : count
+ * So: uid 0 in ns -> kuid mapped_kuid in parent
+ * This means from_kuid(ns, mapped_kuid) returns 0
+ */
+ extent.first = 0; /* uid 0 in this namespace */
+ extent.lower_first = __kuid_val(mapped_kuid); /* maps to this kuid in parent */
+ extent.count = 1;
+
+ ns->uid_map.extent[0] = extent;
+ ns->uid_map.nr_extents = 1;
+
+ /* Set up gid mapping: gid 0 maps to gid 0 in parent (simplified) */
+ extent.first = 0;
+ extent.lower_first = 0;
+ extent.count = 1;
+
+ ns->gid_map.extent[0] = extent;
+ ns->gid_map.nr_extents = 1;
+
+ return ns;
+}
+
+/**
+ * test_kuid_root_in_ns_with_mapping - Test kuid_root_in_ns with namespace where uid 0
+ * maps to different kuid
+ *
+ * Creates a user namespace where uid 0 maps to kuid 1000 in the parent namespace.
+ * Verifies that kuid_root_in_ns correctly identifies kuid 1000 as owning the namespace.
+ *
+ * Note: kuid_root_in_ns walks up the namespace hierarchy, so it checks the current
+ * namespace first, then parent, then parent's parent, etc. So:
+ * - kuid 1000 owns test_ns because from_kuid(test_ns, 1000) == 0
+ * - kuid 0 also owns test_ns because from_kuid(init_user_ns, 0) == 0
+ * (checked in parent)
+ *
+ * This tests the actual functionality as requested: creating namespaces with
+ * different values for the namespace's uid 0.
+ *
+ * @test: KUnit test context
+ */
+static void test_kuid_root_in_ns_with_mapping(struct kunit *test)
+{
+ struct user_namespace *test_ns;
+ struct user_namespace *parent_ns;
+ kuid_t mapped_kuid, other_kuid;
+
+ parent_ns = &init_user_ns;
+ mapped_kuid = KUIDT_INIT(1000);
+ other_kuid = KUIDT_INIT(2000);
+
+ test_ns = create_test_user_ns_with_mapping(test, parent_ns, mapped_kuid);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, test_ns);
+
+ /* kuid 1000 should own test_ns because it maps to uid 0 in test_ns */
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(mapped_kuid, test_ns));
+
+ /* kuid 0 should also own test_ns (checked via parent init_user_ns) */
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), test_ns));
+
+ /* Other kuids should not own test_ns */
+ KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(other_kuid, test_ns));
+ KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(500), test_ns));
+}
+
+/**
+ * test_kuid_root_in_ns_with_different_mappings - Test with multiple namespaces
+ *
+ * Creates multiple user namespaces with different UID mappings to verify
+ * that kuid_root_in_ns correctly handles different namespace hierarchies.
+ *
+ * Since kuid_root_in_ns walks up the hierarchy, kuids that map to 0 in init_user_ns
+ * will own all namespaces, while kuids that only map to 0 in specific namespaces
+ * will only own those namespaces and their children.
+ *
+ * @test: KUnit test context
+ */
+static void test_kuid_root_in_ns_with_different_mappings(struct kunit *test)
+{
+ struct user_namespace *ns1, *ns2, *ns3;
+
+ /* Create ns1 where uid 0 maps to kuid 1000 */
+ ns1 = create_test_user_ns_with_mapping(test, &init_user_ns, KUIDT_INIT(1000));
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns1);
+
+ /* Create ns2 where uid 0 maps to kuid 2000 */
+ ns2 = create_test_user_ns_with_mapping(test, &init_user_ns, KUIDT_INIT(2000));
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns2);
+
+ /* Create ns3 as a child of ns1 where uid 0 maps to kuid 3000 */
+ ns3 = create_test_user_ns_with_mapping(test, ns1, KUIDT_INIT(3000));
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns3);
+
+ /* Test ns1: kuid 1000 owns it, kuid 0 owns it (via parent), kuid 2000 does not */
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(1000), ns1));
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), ns1));
+ KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(2000), ns1));
+
+ /* Test ns2: kuid 2000 owns it, kuid 0 owns it (via parent), kuid 1000 does not */
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(2000), ns2));
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), ns2));
+ KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(1000), ns2));
+
+ /* Test ns3: kuid 3000 owns it, kuid 1000 owns it (via parent ns1),
+ * kuid 0 owns it (via init_user_ns), kuid 2000 does not
+ */
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(3000), ns3));
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(1000), ns3));
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), ns3));
+ KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(2000), ns3));
+}
+
+static struct kunit_case commoncap_test_cases[] = {
+ KUNIT_CASE(test_vfsuid_root_in_currentns_init_ns),
+ KUNIT_CASE(test_vfsuid_root_in_currentns_invalid),
+ KUNIT_CASE(test_vfsuid_root_in_currentns_nonzero),
+ KUNIT_CASE(test_kuid_root_in_ns_init_ns_uid0),
+ KUNIT_CASE(test_kuid_root_in_ns_init_ns_nonzero),
+ KUNIT_CASE(test_kuid_root_in_ns_with_mapping),
+ KUNIT_CASE(test_kuid_root_in_ns_with_different_mappings),
+ {}
+};
+
+static struct kunit_suite commoncap_test_suite = {
+ .name = "commoncap",
+ .test_cases = commoncap_test_cases,
+};
+
+kunit_test_suite(commoncap_test_suite);
+
+MODULE_LICENSE("GPL");
+
+#endif /* CONFIG_SECURITY_COMMONCAP_KUNIT_TEST */
--
2.52.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v5] security: Add KUnit tests for kuid_root_in_ns and vfsuid_root_in_currentns
2025-12-30 15:13 ` [PATCH v5] security: Add KUnit tests for kuid_root_in_ns and vfsuid_root_in_currentns Ryan Foster
@ 2026-01-06 21:07 ` Serge E. Hallyn
2026-01-07 21:51 ` [PATCH v6] " Ryan Foster
0 siblings, 1 reply; 8+ messages in thread
From: Serge E. Hallyn @ 2026-01-06 21:07 UTC (permalink / raw)
To: Ryan Foster; +Cc: linux-kernel, linux-security-module, paul, selinux, serge
On Tue, Dec 30, 2025 at 07:13:09AM -0800, Ryan Foster wrote:
> Hi all,
> Sorry for the spam, this aims to fix both issues. Attempted to reproduce
> CI config build locally.
> Thanks, Ryan
> ---
>
> Add comprehensive KUnit tests for the namespace-related capability
> functions that Serge Hallyn refactored in commit 9891d2f79a9f
> ("Clarify the rootid_owns_currentns").
>
> The tests verify:
> - Basic functionality: UID 0 in init namespace, invalid vfsuid,
> non-zero UIDs
> - Actual namespace traversal: Creating user namespaces with different
> UID mappings where uid 0 maps to different kuids (e.g., 1000, 2000,
> 3000)
> - Hierarchy traversal: Testing multiple nested namespaces to verify
> correct namespace hierarchy traversal
>
> This addresses the feedback to "test the actual functionality" by
> creating real user namespaces with different values for the
> namespace's uid 0, rather than just basic input validation.
>
> The test file is included at the end of commoncap.c when
> CONFIG_SECURITY_COMMONCAP_KUNIT_TEST is enabled, following the
> standard kernel pattern (e.g., scsi_lib.c, ext4/mballoc.c). This
> allows tests to access static functions in the same compilation unit
> without modifying production code based on test configuration.
>
> The tests require CONFIG_USER_NS to be enabled since they rely on user
> namespace mapping functionality. The Kconfig dependency ensures the
> tests only build when this requirement is met.
>
> All 7 tests pass:
> - test_vfsuid_root_in_currentns_init_ns
> - test_vfsuid_root_in_currentns_invalid
> - test_vfsuid_root_in_currentns_nonzero
> - test_kuid_root_in_ns_init_ns_uid0
> - test_kuid_root_in_ns_init_ns_nonzero
> - test_kuid_root_in_ns_with_mapping
> - test_kuid_root_in_ns_with_different_mappings
>
> Updated MAINTAINER capabilities to include commoncap test
>
> Signed-off-by: Ryan Foster <foster.ryan.r@gmail.com>
I've applied this one to #caps-next. I note that there is another v5
which was sent the next day, but this patch seemed more correct.
Although I did prefer the commit message in the other one. Please
let me know if I should switch them, although if so, then I think
you need a v6, because the ns3 test seemed broken in the other one.
Reviewed-by: Serge Hallyn <serge@hallyn.com>
Thanks,
-serge
> ---
> MAINTAINERS | 1 +
> security/Kconfig | 17 +++
> security/commoncap.c | 4 +
> security/commoncap_test.c | 290 ++++++++++++++++++++++++++++++++++++++
> 4 files changed, 312 insertions(+)
> create mode 100644 security/commoncap_test.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c0030e126fc8..6f162c736dfb 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -5682,6 +5682,7 @@ F: include/trace/events/capability.h
> F: include/uapi/linux/capability.h
> F: kernel/capability.c
> F: security/commoncap.c
> +F: security/commoncap_test.c
>
> CAPELLA MICROSYSTEMS LIGHT SENSOR DRIVER
> M: Kevin Tsai <ktsai@capellamicro.com>
> diff --git a/security/Kconfig b/security/Kconfig
> index 285f284dfcac..6a4393fce9a1 100644
> --- a/security/Kconfig
> +++ b/security/Kconfig
> @@ -284,6 +284,23 @@ config LSM
>
> If unsure, leave this as the default.
>
> +config SECURITY_COMMONCAP_KUNIT_TEST
> + bool "Build KUnit tests for commoncap" if !KUNIT_ALL_TESTS
> + depends on KUNIT=y && USER_NS
> + default KUNIT_ALL_TESTS
> + help
> + This builds the commoncap KUnit tests.
> +
> + KUnit tests run during boot and output the results to the debug log
> + in TAP format (https://testanything.org/). Only useful for kernel devs
> + running KUnit test harness and are not for inclusion into a
> + production build.
> +
> + For more information on KUnit and unit tests in general please refer
> + to the KUnit documentation in Documentation/dev-tools/kunit/.
> +
> + If unsure, say N.
> +
> source "security/Kconfig.hardening"
>
> endmenu
> diff --git a/security/commoncap.c b/security/commoncap.c
> index 8a23dfab7fac..3399535808fe 100644
> --- a/security/commoncap.c
> +++ b/security/commoncap.c
> @@ -1521,3 +1521,7 @@ DEFINE_LSM(capability) = {
> };
>
> #endif /* CONFIG_SECURITY */
> +
> +#ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST
> +#include "commoncap_test.c"
> +#endif
> diff --git a/security/commoncap_test.c b/security/commoncap_test.c
> new file mode 100644
> index 000000000000..1088364a54e6
> --- /dev/null
> +++ b/security/commoncap_test.c
> @@ -0,0 +1,290 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * KUnit tests for commoncap.c security functions
> + *
> + * Tests for security-critical functions in the capability subsystem,
> + * particularly namespace-related capability checks.
> + */
> +
> +#include <kunit/test.h>
> +#include <linux/user_namespace.h>
> +#include <linux/uidgid.h>
> +#include <linux/cred.h>
> +#include <linux/mnt_idmapping.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/refcount.h>
> +
> +#ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST
> +
> +/* Functions are static in commoncap.c, but we can call them since we're
> + * included in the same compilation unit when tests are enabled.
> + */
> +
> +/**
> + * test_vfsuid_root_in_currentns_init_ns - Test vfsuid_root_in_currentns with init ns
> + *
> + * Verifies that UID 0 in the init namespace correctly owns the current
> + * namespace when running in init_user_ns.
> + *
> + * @test: KUnit test context
> + */
> +static void test_vfsuid_root_in_currentns_init_ns(struct kunit *test)
> +{
> + vfsuid_t vfsuid;
> + kuid_t kuid;
> +
> + /* Create UID 0 in init namespace */
> + kuid = KUIDT_INIT(0);
> + vfsuid = VFSUIDT_INIT(kuid);
> +
> + /* In init namespace, UID 0 should own current namespace */
> + KUNIT_EXPECT_TRUE(test, vfsuid_root_in_currentns(vfsuid));
> +}
> +
> +/**
> + * test_vfsuid_root_in_currentns_invalid - Test vfsuid_root_in_currentns with invalid vfsuid
> + *
> + * Verifies that an invalid vfsuid correctly returns false.
> + *
> + * @test: KUnit test context
> + */
> +static void test_vfsuid_root_in_currentns_invalid(struct kunit *test)
> +{
> + vfsuid_t invalid_vfsuid;
> +
> + /* Use the predefined invalid vfsuid */
> + invalid_vfsuid = INVALID_VFSUID;
> +
> + /* Invalid vfsuid should return false */
> + KUNIT_EXPECT_FALSE(test, vfsuid_root_in_currentns(invalid_vfsuid));
> +}
> +
> +/**
> + * test_vfsuid_root_in_currentns_nonzero - Test vfsuid_root_in_currentns with non-zero UID
> + *
> + * Verifies that a non-zero UID correctly returns false.
> + *
> + * @test: KUnit test context
> + */
> +static void test_vfsuid_root_in_currentns_nonzero(struct kunit *test)
> +{
> + vfsuid_t vfsuid;
> + kuid_t kuid;
> +
> + /* Create a non-zero UID */
> + kuid = KUIDT_INIT(1000);
> + vfsuid = VFSUIDT_INIT(kuid);
> +
> + /* Non-zero UID should return false */
> + KUNIT_EXPECT_FALSE(test, vfsuid_root_in_currentns(vfsuid));
> +}
> +
> +/**
> + * test_kuid_root_in_ns_init_ns_uid0 - Test kuid_root_in_ns with init namespace and UID 0
> + *
> + * Verifies that kuid_root_in_ns correctly identifies UID 0 in init namespace.
> + * This tests the core namespace traversal logic. In init namespace, UID 0
> + * maps to itself, so it should own the namespace.
> + *
> + * @test: KUnit test context
> + */
> +static void test_kuid_root_in_ns_init_ns_uid0(struct kunit *test)
> +{
> + kuid_t kuid;
> + struct user_namespace *init_ns;
> +
> + kuid = KUIDT_INIT(0);
> + init_ns = &init_user_ns;
> +
> + /* UID 0 should own init namespace */
> + KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(kuid, init_ns));
> +}
> +
> +/**
> + * test_kuid_root_in_ns_init_ns_nonzero - Test kuid_root_in_ns with init namespace and non-zero UID
> + *
> + * Verifies that kuid_root_in_ns correctly rejects non-zero UIDs in init namespace.
> + * Only UID 0 should own a namespace.
> + *
> + * @test: KUnit test context
> + */
> +static void test_kuid_root_in_ns_init_ns_nonzero(struct kunit *test)
> +{
> + kuid_t kuid;
> + struct user_namespace *init_ns;
> +
> + kuid = KUIDT_INIT(1000);
> + init_ns = &init_user_ns;
> +
> + /* Non-zero UID should not own namespace */
> + KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(kuid, init_ns));
> +}
> +
> +/**
> + * create_test_user_ns_with_mapping - Create a mock user namespace with UID mapping
> + *
> + * Creates a minimal user namespace structure for testing where uid 0 in the
> + * namespace maps to a specific kuid in the parent namespace.
> + *
> + * @test: KUnit test context
> + * @parent_ns: Parent namespace (typically init_user_ns)
> + * @mapped_kuid: The kuid that uid 0 in this namespace maps to in parent
> + *
> + * Returns: Pointer to allocated namespace, or NULL on failure
> + */
> +static struct user_namespace *create_test_user_ns_with_mapping(struct kunit *test,
> + struct user_namespace *parent_ns,
> + kuid_t mapped_kuid)
> +{
> + struct user_namespace *ns;
> + struct uid_gid_extent extent;
> +
> + /* Allocate a test namespace - use kzalloc to zero all fields */
> + ns = kunit_kzalloc(test, sizeof(*ns), GFP_KERNEL);
> + if (!ns)
> + return NULL;
> +
> + /* Initialize basic namespace structure fields */
> + ns->parent = parent_ns;
> + ns->level = parent_ns ? parent_ns->level + 1 : 0;
> + ns->owner = mapped_kuid;
> + ns->group = KGIDT_INIT(0);
> +
> + /* Initialize ns_common structure */
> + refcount_set(&ns->ns.__ns_ref, 1);
> + ns->ns.inum = 0; /* Mock inum */
> +
> + /* Set up uid mapping: uid 0 in this namespace maps to mapped_kuid in parent
> + * Format: first (uid in ns) : lower_first (kuid in parent) : count
> + * So: uid 0 in ns -> kuid mapped_kuid in parent
> + * This means from_kuid(ns, mapped_kuid) returns 0
> + */
> + extent.first = 0; /* uid 0 in this namespace */
> + extent.lower_first = __kuid_val(mapped_kuid); /* maps to this kuid in parent */
> + extent.count = 1;
> +
> + ns->uid_map.extent[0] = extent;
> + ns->uid_map.nr_extents = 1;
> +
> + /* Set up gid mapping: gid 0 maps to gid 0 in parent (simplified) */
> + extent.first = 0;
> + extent.lower_first = 0;
> + extent.count = 1;
> +
> + ns->gid_map.extent[0] = extent;
> + ns->gid_map.nr_extents = 1;
> +
> + return ns;
> +}
> +
> +/**
> + * test_kuid_root_in_ns_with_mapping - Test kuid_root_in_ns with namespace where uid 0
> + * maps to different kuid
> + *
> + * Creates a user namespace where uid 0 maps to kuid 1000 in the parent namespace.
> + * Verifies that kuid_root_in_ns correctly identifies kuid 1000 as owning the namespace.
> + *
> + * Note: kuid_root_in_ns walks up the namespace hierarchy, so it checks the current
> + * namespace first, then parent, then parent's parent, etc. So:
> + * - kuid 1000 owns test_ns because from_kuid(test_ns, 1000) == 0
> + * - kuid 0 also owns test_ns because from_kuid(init_user_ns, 0) == 0
> + * (checked in parent)
> + *
> + * This tests the actual functionality as requested: creating namespaces with
> + * different values for the namespace's uid 0.
> + *
> + * @test: KUnit test context
> + */
> +static void test_kuid_root_in_ns_with_mapping(struct kunit *test)
> +{
> + struct user_namespace *test_ns;
> + struct user_namespace *parent_ns;
> + kuid_t mapped_kuid, other_kuid;
> +
> + parent_ns = &init_user_ns;
> + mapped_kuid = KUIDT_INIT(1000);
> + other_kuid = KUIDT_INIT(2000);
> +
> + test_ns = create_test_user_ns_with_mapping(test, parent_ns, mapped_kuid);
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, test_ns);
> +
> + /* kuid 1000 should own test_ns because it maps to uid 0 in test_ns */
> + KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(mapped_kuid, test_ns));
> +
> + /* kuid 0 should also own test_ns (checked via parent init_user_ns) */
> + KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), test_ns));
> +
> + /* Other kuids should not own test_ns */
> + KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(other_kuid, test_ns));
> + KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(500), test_ns));
> +}
> +
> +/**
> + * test_kuid_root_in_ns_with_different_mappings - Test with multiple namespaces
> + *
> + * Creates multiple user namespaces with different UID mappings to verify
> + * that kuid_root_in_ns correctly handles different namespace hierarchies.
> + *
> + * Since kuid_root_in_ns walks up the hierarchy, kuids that map to 0 in init_user_ns
> + * will own all namespaces, while kuids that only map to 0 in specific namespaces
> + * will only own those namespaces and their children.
> + *
> + * @test: KUnit test context
> + */
> +static void test_kuid_root_in_ns_with_different_mappings(struct kunit *test)
> +{
> + struct user_namespace *ns1, *ns2, *ns3;
> +
> + /* Create ns1 where uid 0 maps to kuid 1000 */
> + ns1 = create_test_user_ns_with_mapping(test, &init_user_ns, KUIDT_INIT(1000));
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns1);
> +
> + /* Create ns2 where uid 0 maps to kuid 2000 */
> + ns2 = create_test_user_ns_with_mapping(test, &init_user_ns, KUIDT_INIT(2000));
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns2);
> +
> + /* Create ns3 as a child of ns1 where uid 0 maps to kuid 3000 */
> + ns3 = create_test_user_ns_with_mapping(test, ns1, KUIDT_INIT(3000));
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns3);
> +
> + /* Test ns1: kuid 1000 owns it, kuid 0 owns it (via parent), kuid 2000 does not */
> + KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(1000), ns1));
> + KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), ns1));
> + KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(2000), ns1));
> +
> + /* Test ns2: kuid 2000 owns it, kuid 0 owns it (via parent), kuid 1000 does not */
> + KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(2000), ns2));
> + KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), ns2));
> + KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(1000), ns2));
> +
> + /* Test ns3: kuid 3000 owns it, kuid 1000 owns it (via parent ns1),
> + * kuid 0 owns it (via init_user_ns), kuid 2000 does not
> + */
> + KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(3000), ns3));
> + KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(1000), ns3));
> + KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), ns3));
> + KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(2000), ns3));
> +}
> +
> +static struct kunit_case commoncap_test_cases[] = {
> + KUNIT_CASE(test_vfsuid_root_in_currentns_init_ns),
> + KUNIT_CASE(test_vfsuid_root_in_currentns_invalid),
> + KUNIT_CASE(test_vfsuid_root_in_currentns_nonzero),
> + KUNIT_CASE(test_kuid_root_in_ns_init_ns_uid0),
> + KUNIT_CASE(test_kuid_root_in_ns_init_ns_nonzero),
> + KUNIT_CASE(test_kuid_root_in_ns_with_mapping),
> + KUNIT_CASE(test_kuid_root_in_ns_with_different_mappings),
> + {}
> +};
> +
> +static struct kunit_suite commoncap_test_suite = {
> + .name = "commoncap",
> + .test_cases = commoncap_test_cases,
> +};
> +
> +kunit_test_suite(commoncap_test_suite);
> +
> +MODULE_LICENSE("GPL");
> +
> +#endif /* CONFIG_SECURITY_COMMONCAP_KUNIT_TEST */
> --
> 2.52.0
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v6] security: Add KUnit tests for kuid_root_in_ns and vfsuid_root_in_currentns
2026-01-06 21:07 ` Serge E. Hallyn
@ 2026-01-07 21:51 ` Ryan Foster
2026-01-10 4:50 ` Serge E. Hallyn
0 siblings, 1 reply; 8+ messages in thread
From: Ryan Foster @ 2026-01-07 21:51 UTC (permalink / raw)
To: serge; +Cc: foster.ryan.r, linux-kernel, linux-security-module, paul, selinux
Here's v6 with both fixes combined. The Dec 29 version you have in caps-next
is correct for the namespace config - v6 keeps that and adds the KUNIT=y
dependency to fix the Intel CI build error.
Changes in v6:
- Namespace config: all three namespaces are independent children of
init_user_ns (same as Dec 29 you reviewed)
- Build fix: depends on KUNIT=y prevents link errors when KUNIT=m
The Dec 30 patch accidentally reverted the namespace fix when I was adding the
KUNIT=y part. This v6 has both fixes working together.
Thanks, Ryan
Add comprehensive KUnit tests for the namespace-related capability
functions that Serge Hallyn refactored in commit 9891d2f79a9f
("Clarify the rootid_owns_currentns").
The tests verify:
- Basic functionality: UID 0 in init namespace, invalid vfsuid,
non-zero UIDs
- Actual namespace traversal: Creating user namespaces with different
UID mappings where uid 0 maps to different kuids (e.g., 1000, 2000,
3000)
- Hierarchy traversal: Testing multiple nested namespaces to verify
correct namespace hierarchy traversal
This addresses the feedback to "test the actual functionality" by
creating real user namespaces with different values for the
namespace's uid 0, rather than just basic input validation.
The test file is included at the end of commoncap.c when
CONFIG_SECURITY_COMMONCAP_KUNIT_TEST is enabled, following the
standard kernel pattern (e.g., scsi_lib.c, ext4/mballoc.c). This
allows tests to access static functions in the same compilation unit
without modifying production code based on test configuration.
The tests require CONFIG_USER_NS to be enabled since they rely on user
namespace mapping functionality. The Kconfig dependency ensures the
tests only build when this requirement is met.
All 7 tests pass:
- test_vfsuid_root_in_currentns_init_ns
- test_vfsuid_root_in_currentns_invalid
- test_vfsuid_root_in_currentns_nonzero
- test_kuid_root_in_ns_init_ns_uid0
- test_kuid_root_in_ns_init_ns_nonzero
- test_kuid_root_in_ns_with_mapping
- test_kuid_root_in_ns_with_different_mappings
Updated MAINTAINER capabilities to include commoncap test
Signed-off-by: Ryan Foster <foster.ryan.r@gmail.com>
---
MAINTAINERS | 1 +
security/Kconfig | 17 +++
security/commoncap.c | 4 +
security/commoncap_test.c | 288 ++++++++++++++++++++++++++++++++++++++
4 files changed, 310 insertions(+)
create mode 100644 security/commoncap_test.c
diff --git a/MAINTAINERS b/MAINTAINERS
index c0030e126fc8..6f162c736dfb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5682,6 +5682,7 @@ F: include/trace/events/capability.h
F: include/uapi/linux/capability.h
F: kernel/capability.c
F: security/commoncap.c
+F: security/commoncap_test.c
CAPELLA MICROSYSTEMS LIGHT SENSOR DRIVER
M: Kevin Tsai <ktsai@capellamicro.com>
diff --git a/security/Kconfig b/security/Kconfig
index 285f284dfcac..6a4393fce9a1 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -284,6 +284,23 @@ config LSM
If unsure, leave this as the default.
+config SECURITY_COMMONCAP_KUNIT_TEST
+ bool "Build KUnit tests for commoncap" if !KUNIT_ALL_TESTS
+ depends on KUNIT=y && USER_NS
+ default KUNIT_ALL_TESTS
+ help
+ This builds the commoncap KUnit tests.
+
+ KUnit tests run during boot and output the results to the debug log
+ in TAP format (https://testanything.org/). Only useful for kernel devs
+ running KUnit test harness and are not for inclusion into a
+ production build.
+
+ For more information on KUnit and unit tests in general please refer
+ to the KUnit documentation in Documentation/dev-tools/kunit/.
+
+ If unsure, say N.
+
source "security/Kconfig.hardening"
endmenu
diff --git a/security/commoncap.c b/security/commoncap.c
index 8a23dfab7fac..3399535808fe 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -1521,3 +1521,7 @@ DEFINE_LSM(capability) = {
};
#endif /* CONFIG_SECURITY */
+
+#ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST
+#include "commoncap_test.c"
+#endif
diff --git a/security/commoncap_test.c b/security/commoncap_test.c
new file mode 100644
index 000000000000..e9b278be37f1
--- /dev/null
+++ b/security/commoncap_test.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * KUnit tests for commoncap.c security functions
+ *
+ * Tests for security-critical functions in the capability subsystem,
+ * particularly namespace-related capability checks.
+ */
+
+#include <kunit/test.h>
+#include <linux/user_namespace.h>
+#include <linux/uidgid.h>
+#include <linux/cred.h>
+#include <linux/mnt_idmapping.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/refcount.h>
+
+#ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST
+
+/* Functions are static in commoncap.c, but we can call them since we're
+ * included in the same compilation unit when tests are enabled.
+ */
+
+/**
+ * test_vfsuid_root_in_currentns_init_ns - Test vfsuid_root_in_currentns with init ns
+ *
+ * Verifies that UID 0 in the init namespace correctly owns the current
+ * namespace when running in init_user_ns.
+ *
+ * @test: KUnit test context
+ */
+static void test_vfsuid_root_in_currentns_init_ns(struct kunit *test)
+{
+ vfsuid_t vfsuid;
+ kuid_t kuid;
+
+ /* Create UID 0 in init namespace */
+ kuid = KUIDT_INIT(0);
+ vfsuid = VFSUIDT_INIT(kuid);
+
+ /* In init namespace, UID 0 should own current namespace */
+ KUNIT_EXPECT_TRUE(test, vfsuid_root_in_currentns(vfsuid));
+}
+
+/**
+ * test_vfsuid_root_in_currentns_invalid - Test vfsuid_root_in_currentns with invalid vfsuid
+ *
+ * Verifies that an invalid vfsuid correctly returns false.
+ *
+ * @test: KUnit test context
+ */
+static void test_vfsuid_root_in_currentns_invalid(struct kunit *test)
+{
+ vfsuid_t invalid_vfsuid;
+
+ /* Use the predefined invalid vfsuid */
+ invalid_vfsuid = INVALID_VFSUID;
+
+ /* Invalid vfsuid should return false */
+ KUNIT_EXPECT_FALSE(test, vfsuid_root_in_currentns(invalid_vfsuid));
+}
+
+/**
+ * test_vfsuid_root_in_currentns_nonzero - Test vfsuid_root_in_currentns with non-zero UID
+ *
+ * Verifies that a non-zero UID correctly returns false.
+ *
+ * @test: KUnit test context
+ */
+static void test_vfsuid_root_in_currentns_nonzero(struct kunit *test)
+{
+ vfsuid_t vfsuid;
+ kuid_t kuid;
+
+ /* Create a non-zero UID */
+ kuid = KUIDT_INIT(1000);
+ vfsuid = VFSUIDT_INIT(kuid);
+
+ /* Non-zero UID should return false */
+ KUNIT_EXPECT_FALSE(test, vfsuid_root_in_currentns(vfsuid));
+}
+
+/**
+ * test_kuid_root_in_ns_init_ns_uid0 - Test kuid_root_in_ns with init namespace and UID 0
+ *
+ * Verifies that kuid_root_in_ns correctly identifies UID 0 in init namespace.
+ * This tests the core namespace traversal logic. In init namespace, UID 0
+ * maps to itself, so it should own the namespace.
+ *
+ * @test: KUnit test context
+ */
+static void test_kuid_root_in_ns_init_ns_uid0(struct kunit *test)
+{
+ kuid_t kuid;
+ struct user_namespace *init_ns;
+
+ kuid = KUIDT_INIT(0);
+ init_ns = &init_user_ns;
+
+ /* UID 0 should own init namespace */
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(kuid, init_ns));
+}
+
+/**
+ * test_kuid_root_in_ns_init_ns_nonzero - Test kuid_root_in_ns with init namespace and non-zero UID
+ *
+ * Verifies that kuid_root_in_ns correctly rejects non-zero UIDs in init namespace.
+ * Only UID 0 should own a namespace.
+ *
+ * @test: KUnit test context
+ */
+static void test_kuid_root_in_ns_init_ns_nonzero(struct kunit *test)
+{
+ kuid_t kuid;
+ struct user_namespace *init_ns;
+
+ kuid = KUIDT_INIT(1000);
+ init_ns = &init_user_ns;
+
+ /* Non-zero UID should not own namespace */
+ KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(kuid, init_ns));
+}
+
+/**
+ * create_test_user_ns_with_mapping - Create a mock user namespace with UID mapping
+ *
+ * Creates a minimal user namespace structure for testing where uid 0 in the
+ * namespace maps to a specific kuid in the parent namespace.
+ *
+ * @test: KUnit test context
+ * @parent_ns: Parent namespace (typically init_user_ns)
+ * @mapped_kuid: The kuid that uid 0 in this namespace maps to in parent
+ *
+ * Returns: Pointer to allocated namespace, or NULL on failure
+ */
+static struct user_namespace *create_test_user_ns_with_mapping(struct kunit *test,
+ struct user_namespace *parent_ns,
+ kuid_t mapped_kuid)
+{
+ struct user_namespace *ns;
+ struct uid_gid_extent extent;
+
+ /* Allocate a test namespace - use kzalloc to zero all fields */
+ ns = kunit_kzalloc(test, sizeof(*ns), GFP_KERNEL);
+ if (!ns)
+ return NULL;
+
+ /* Initialize basic namespace structure fields */
+ ns->parent = parent_ns;
+ ns->level = parent_ns ? parent_ns->level + 1 : 0;
+ ns->owner = mapped_kuid;
+ ns->group = KGIDT_INIT(0);
+
+ /* Initialize ns_common structure */
+ refcount_set(&ns->ns.__ns_ref, 1);
+ ns->ns.inum = 0; /* Mock inum */
+
+ /* Set up uid mapping: uid 0 in this namespace maps to mapped_kuid in parent
+ * Format: first (uid in ns) : lower_first (kuid in parent) : count
+ * So: uid 0 in ns -> kuid mapped_kuid in parent
+ * This means from_kuid(ns, mapped_kuid) returns 0
+ */
+ extent.first = 0; /* uid 0 in this namespace */
+ extent.lower_first = __kuid_val(mapped_kuid); /* maps to this kuid in parent */
+ extent.count = 1;
+
+ ns->uid_map.extent[0] = extent;
+ ns->uid_map.nr_extents = 1;
+
+ /* Set up gid mapping: gid 0 maps to gid 0 in parent (simplified) */
+ extent.first = 0;
+ extent.lower_first = 0;
+ extent.count = 1;
+
+ ns->gid_map.extent[0] = extent;
+ ns->gid_map.nr_extents = 1;
+
+ return ns;
+}
+
+/**
+ * test_kuid_root_in_ns_with_mapping - Test kuid_root_in_ns with namespace where uid 0
+ * maps to different kuid
+ *
+ * Creates a user namespace where uid 0 maps to kuid 1000 in the parent namespace.
+ * Verifies that kuid_root_in_ns correctly identifies kuid 1000 as owning the namespace.
+ *
+ * Note: kuid_root_in_ns walks up the namespace hierarchy, so it checks the current
+ * namespace first, then parent, then parent's parent, etc. So:
+ * - kuid 1000 owns test_ns because from_kuid(test_ns, 1000) == 0
+ * - kuid 0 also owns test_ns because from_kuid(init_user_ns, 0) == 0
+ * (checked in parent)
+ *
+ * This tests the actual functionality as requested: creating namespaces with
+ * different values for the namespace's uid 0.
+ *
+ * @test: KUnit test context
+ */
+static void test_kuid_root_in_ns_with_mapping(struct kunit *test)
+{
+ struct user_namespace *test_ns;
+ struct user_namespace *parent_ns;
+ kuid_t mapped_kuid, other_kuid;
+
+ parent_ns = &init_user_ns;
+ mapped_kuid = KUIDT_INIT(1000);
+ other_kuid = KUIDT_INIT(2000);
+
+ test_ns = create_test_user_ns_with_mapping(test, parent_ns, mapped_kuid);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, test_ns);
+
+ /* kuid 1000 should own test_ns because it maps to uid 0 in test_ns */
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(mapped_kuid, test_ns));
+
+ /* kuid 0 should also own test_ns (checked via parent init_user_ns) */
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), test_ns));
+
+ /* Other kuids should not own test_ns */
+ KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(other_kuid, test_ns));
+ KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(500), test_ns));
+}
+
+/**
+ * test_kuid_root_in_ns_with_different_mappings - Test with multiple namespaces
+ *
+ * Creates multiple user namespaces with different UID mappings to verify
+ * that kuid_root_in_ns correctly distinguishes between namespaces.
+ *
+ * Each namespace maps uid 0 to a different kuid, and we verify that each
+ * kuid only owns its corresponding namespace (plus kuid 0 owns all via
+ * init_user_ns parent).
+ *
+ * @test: KUnit test context
+ */
+static void test_kuid_root_in_ns_with_different_mappings(struct kunit *test)
+{
+ struct user_namespace *ns1, *ns2, *ns3;
+
+ /* Create three independent namespaces, each mapping uid 0 to different kuids */
+ ns1 = create_test_user_ns_with_mapping(test, &init_user_ns, KUIDT_INIT(1000));
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns1);
+
+ ns2 = create_test_user_ns_with_mapping(test, &init_user_ns, KUIDT_INIT(2000));
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns2);
+
+ ns3 = create_test_user_ns_with_mapping(test, &init_user_ns, KUIDT_INIT(3000));
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns3);
+
+ /* Test ns1: kuid 1000 owns it, kuid 0 owns it (via parent), others do not */
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(1000), ns1));
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), ns1));
+ KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(2000), ns1));
+ KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(3000), ns1));
+
+ /* Test ns2: kuid 2000 owns it, kuid 0 owns it (via parent), others do not */
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(2000), ns2));
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), ns2));
+ KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(1000), ns2));
+ KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(3000), ns2));
+
+ /* Test ns3: kuid 3000 owns it, kuid 0 owns it (via parent), others do not */
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(3000), ns3));
+ KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), ns3));
+ KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(1000), ns3));
+ KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(2000), ns3));
+}
+
+static struct kunit_case commoncap_test_cases[] = {
+ KUNIT_CASE(test_vfsuid_root_in_currentns_init_ns),
+ KUNIT_CASE(test_vfsuid_root_in_currentns_invalid),
+ KUNIT_CASE(test_vfsuid_root_in_currentns_nonzero),
+ KUNIT_CASE(test_kuid_root_in_ns_init_ns_uid0),
+ KUNIT_CASE(test_kuid_root_in_ns_init_ns_nonzero),
+ KUNIT_CASE(test_kuid_root_in_ns_with_mapping),
+ KUNIT_CASE(test_kuid_root_in_ns_with_different_mappings),
+ {}
+};
+
+static struct kunit_suite commoncap_test_suite = {
+ .name = "commoncap",
+ .test_cases = commoncap_test_cases,
+};
+
+kunit_test_suite(commoncap_test_suite);
+
+MODULE_LICENSE("GPL");
+
+#endif /* CONFIG_SECURITY_COMMONCAP_KUNIT_TEST */
--
2.52.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v6] security: Add KUnit tests for kuid_root_in_ns and vfsuid_root_in_currentns
2026-01-07 21:51 ` [PATCH v6] " Ryan Foster
@ 2026-01-10 4:50 ` Serge E. Hallyn
0 siblings, 0 replies; 8+ messages in thread
From: Serge E. Hallyn @ 2026-01-10 4:50 UTC (permalink / raw)
To: Ryan Foster; +Cc: serge, linux-kernel, linux-security-module, paul, selinux
On Wed, Jan 07, 2026 at 01:51:28PM -0800, Ryan Foster wrote:
>
> Here's v6 with both fixes combined. The Dec 29 version you have in caps-next
> is correct for the namespace config - v6 keeps that and adds the KUNIT=y
> dependency to fix the Intel CI build error.
>
> Changes in v6:
> - Namespace config: all three namespaces are independent children of
> init_user_ns (same as Dec 29 you reviewed)
>
> - Build fix: depends on KUNIT=y prevents link errors when KUNIT=m
>
> The Dec 30 patch accidentally reverted the namespace fix when I was adding the
> KUNIT=y part. This v6 has both fixes working together.
>
> Thanks, Ryan
>
> Add comprehensive KUnit tests for the namespace-related capability
> functions that Serge Hallyn refactored in commit 9891d2f79a9f
> ("Clarify the rootid_owns_currentns").
>
> The tests verify:
> - Basic functionality: UID 0 in init namespace, invalid vfsuid,
> non-zero UIDs
> - Actual namespace traversal: Creating user namespaces with different
> UID mappings where uid 0 maps to different kuids (e.g., 1000, 2000,
> 3000)
> - Hierarchy traversal: Testing multiple nested namespaces to verify
> correct namespace hierarchy traversal
>
> This addresses the feedback to "test the actual functionality" by
> creating real user namespaces with different values for the
> namespace's uid 0, rather than just basic input validation.
>
> The test file is included at the end of commoncap.c when
> CONFIG_SECURITY_COMMONCAP_KUNIT_TEST is enabled, following the
> standard kernel pattern (e.g., scsi_lib.c, ext4/mballoc.c). This
> allows tests to access static functions in the same compilation unit
> without modifying production code based on test configuration.
>
> The tests require CONFIG_USER_NS to be enabled since they rely on user
> namespace mapping functionality. The Kconfig dependency ensures the
> tests only build when this requirement is met.
>
> All 7 tests pass:
> - test_vfsuid_root_in_currentns_init_ns
> - test_vfsuid_root_in_currentns_invalid
> - test_vfsuid_root_in_currentns_nonzero
> - test_kuid_root_in_ns_init_ns_uid0
> - test_kuid_root_in_ns_init_ns_nonzero
> - test_kuid_root_in_ns_with_mapping
> - test_kuid_root_in_ns_with_different_mappings
>
> Updated MAINTAINER capabilities to include commoncap test
>
> Signed-off-by: Ryan Foster <foster.ryan.r@gmail.com>
Thanks, applied to git://git.kernel.org/pub/scm/linux/kernel/git/sergeh/linux.git #caps-next
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-01-10 4:50 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-10 14:37 [PATCH] security: Add KUnit tests for rootid_owns_currentns() Ryan Foster
2025-11-11 0:33 ` Serge E. Hallyn
2025-11-11 6:52 ` kernel test robot
2025-11-11 7:03 ` kernel test robot
2025-12-30 15:13 ` [PATCH v5] security: Add KUnit tests for kuid_root_in_ns and vfsuid_root_in_currentns Ryan Foster
2026-01-06 21:07 ` Serge E. Hallyn
2026-01-07 21:51 ` [PATCH v6] " Ryan Foster
2026-01-10 4:50 ` Serge E. Hallyn
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox