linux-security-module.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [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; 7+ 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] 7+ 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; 7+ 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] 7+ 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-11-21 17:48 ` [PATCH v2] security: Rename functions and add namespace mapping tests Ryan Foster
  3 siblings, 0 replies; 7+ 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] 7+ 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-11-21 17:48 ` [PATCH v2] security: Rename functions and add namespace mapping tests Ryan Foster
  3 siblings, 0 replies; 7+ 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] 7+ messages in thread

* [PATCH v2] security: Rename functions and add namespace mapping tests
  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-11-21 17:48 ` Ryan Foster
  2025-11-25 15:15   ` Serge E. Hallyn
  3 siblings, 1 reply; 7+ messages in thread
From: Ryan Foster @ 2025-11-21 17:48 UTC (permalink / raw)
  To: serge, paul, linux-security-module; +Cc: linux-kernel, Ryan Foster

Rename rootid_owns_currentns() to uid_owns_currentns() and
rootid_owns_userns() to uid_owns_ns() for clarity, as the function checks
any UID, not just root. Update all call sites accordingly.

Add tests that create actual user namespaces with different UID mappings
to verify namespace traversal logic. The tests create namespaces where
uid 0 maps to different kuids (e.g., kuid 1000, 2000) and verify that
uid_owns_ns() correctly identifies ownership based on the namespace
hierarchy traversal.

This addresses feedback to use clearer function naming and test actual
namespace functionality with real user namespace creation and mappings,
rather than just basic input validation.
---
 security/commoncap.c      |  26 ++--
 security/commoncap_test.c | 286 ++++++++++++++++++++++++++++++++------
 2 files changed, 254 insertions(+), 58 deletions(-)

diff --git a/security/commoncap.c b/security/commoncap.c
index 15d8147a34c4..cca291df9551 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -359,16 +359,16 @@ int cap_inode_killpriv(struct mnt_idmap *idmap, struct dentry *dentry)
 }
 
 #ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST
-bool rootid_owns_userns(struct user_namespace *ns, kuid_t kroot);
-bool rootid_owns_userns(struct user_namespace *ns, kuid_t kroot)
+bool uid_owns_ns(struct user_namespace *ns, kuid_t kuid);
+bool uid_owns_ns(struct user_namespace *ns, kuid_t kuid)
 #else
-static bool rootid_owns_userns(struct user_namespace *ns, kuid_t kroot)
+static bool uid_owns_ns(struct user_namespace *ns, kuid_t kuid)
 #endif
 {
 	struct user_namespace *iter;
 
 	for (iter = ns;; iter = iter->parent) {
-		if (from_kuid(iter, kroot) == 0)
+		if (from_kuid(iter, kuid) == 0)
 			return true;
 		if (iter == &init_user_ns)
 			break;
@@ -378,19 +378,19 @@ static bool rootid_owns_userns(struct user_namespace *ns, kuid_t kroot)
 }
 
 #ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST
-bool rootid_owns_currentns(vfsuid_t rootvfsuid);
-bool rootid_owns_currentns(vfsuid_t rootvfsuid)
+bool uid_owns_currentns(vfsuid_t vfsuid);
+bool uid_owns_currentns(vfsuid_t vfsuid)
 #else
-static bool rootid_owns_currentns(vfsuid_t rootvfsuid)
+static bool uid_owns_currentns(vfsuid_t vfsuid)
 #endif
 {
-	kuid_t kroot;
+	kuid_t kuid;
 
-	if (!vfsuid_valid(rootvfsuid))
+	if (!vfsuid_valid(vfsuid))
 		return false;
 
-	kroot = vfsuid_into_kuid(rootvfsuid);
-	return rootid_owns_userns(current_user_ns(), kroot);
+	kuid = vfsuid_into_kuid(vfsuid);
+	return uid_owns_ns(current_user_ns(), kuid);
 }
 
 static __u32 sansflags(__u32 m)
@@ -497,7 +497,7 @@ int cap_inode_getsecurity(struct mnt_idmap *idmap,
 		goto out_free;
 	}
 
-	if (!rootid_owns_currentns(vfsroot)) {
+	if (!uid_owns_currentns(vfsroot)) {
 		size = -EOVERFLOW;
 		goto out_free;
 	}
@@ -738,7 +738,7 @@ int get_vfs_caps_from_disk(struct mnt_idmap *idmap,
 	/* Limit the caps to the mounter of the filesystem
 	 * or the more limited uid specified in the xattr.
 	 */
-	if (!rootid_owns_currentns(rootvfsuid))
+	if (!uid_owns_currentns(rootvfsuid))
 		return -ENODATA;
 
 	cpu_caps->permitted.val = le32_to_cpu(caps->data[0].permitted);
diff --git a/security/commoncap_test.c b/security/commoncap_test.c
index 962aa899455d..7f066dc0df5d 100644
--- a/security/commoncap_test.c
+++ b/security/commoncap_test.c
@@ -10,6 +10,8 @@
 #include <linux/user_namespace.h>
 #include <linux/uidgid.h>
 #include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/refcount.h>
 
 /* Forward declare types and functions we need from mnt_idmapping.h
  * We avoid including the full header because it contains inline functions
@@ -50,38 +52,38 @@ static inline kuid_t vfsuid_into_kuid(vfsuid_t vfsuid)
 #ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST
 
 /* Forward declarations - functions are exported when KUNIT_TEST is enabled */
-extern bool rootid_owns_userns(struct user_namespace *ns, kuid_t kroot);
-extern bool rootid_owns_currentns(vfsuid_t rootvfsuid);
+extern bool uid_owns_ns(struct user_namespace *ns, kuid_t kuid);
+extern bool uid_owns_currentns(vfsuid_t vfsuid);
 
 /**
- * test_rootid_owns_currentns_init_ns - Test rootid_owns_currentns with init ns
+ * test_uid_owns_currentns_init_ns - Test uid_owns_currentns with init ns
  *
- * Verifies that a root ID in the init namespace correctly owns the current
+ * 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_rootid_owns_currentns_init_ns(struct kunit *test)
+static void test_uid_owns_currentns_init_ns(struct kunit *test)
 {
-	vfsuid_t root_vfsuid;
-	kuid_t root_kuid;
+	vfsuid_t vfsuid;
+	kuid_t kuid;
 
-	/* Create a root UID in init namespace */
-	root_kuid = KUIDT_INIT(0);
-	root_vfsuid = VFSUIDT_INIT(root_kuid);
+	/* Create UID 0 in init namespace */
+	kuid = KUIDT_INIT(0);
+	vfsuid = VFSUIDT_INIT(kuid);
 
-	/* In init namespace, root should own current namespace */
-	KUNIT_EXPECT_TRUE(test, rootid_owns_currentns(root_vfsuid));
+	/* In init namespace, UID 0 should own current namespace */
+	KUNIT_EXPECT_TRUE(test, uid_owns_currentns(vfsuid));
 }
 
 /**
- * test_rootid_owns_currentns_invalid - Test rootid_owns_currentns with invalid vfsuid
+ * test_uid_owns_currentns_invalid - Test uid_owns_currentns with invalid vfsuid
  *
  * Verifies that an invalid vfsuid correctly returns false.
  *
  * @test: KUnit test context
  */
-static void test_rootid_owns_currentns_invalid(struct kunit *test)
+static void test_uid_owns_currentns_invalid(struct kunit *test)
 {
 	vfsuid_t invalid_vfsuid;
 
@@ -89,74 +91,268 @@ static void test_rootid_owns_currentns_invalid(struct kunit *test)
 	invalid_vfsuid = INVALID_VFSUID;
 
 	/* Invalid vfsuid should return false */
-	KUNIT_EXPECT_FALSE(test, rootid_owns_currentns(invalid_vfsuid));
+	KUNIT_EXPECT_FALSE(test, uid_owns_currentns(invalid_vfsuid));
 }
 
 /**
- * test_rootid_owns_currentns_nonroot - Test rootid_owns_currentns with non-root UID
+ * test_uid_owns_currentns_nonzero - Test uid_owns_currentns with non-zero UID
  *
- * Verifies that a non-root UID correctly returns false.
+ * Verifies that a non-zero UID correctly returns false.
  *
  * @test: KUnit test context
  */
-static void test_rootid_owns_currentns_nonroot(struct kunit *test)
+static void test_uid_owns_currentns_nonzero(struct kunit *test)
 {
-	vfsuid_t nonroot_vfsuid;
-	kuid_t nonroot_kuid;
+	vfsuid_t vfsuid;
+	kuid_t kuid;
 
-	/* Create a non-root UID */
-	nonroot_kuid = KUIDT_INIT(1000);
-	nonroot_vfsuid = VFSUIDT_INIT(nonroot_kuid);
+	/* Create a non-zero UID */
+	kuid = KUIDT_INIT(1000);
+	vfsuid = VFSUIDT_INIT(kuid);
 
-	/* Non-root UID should return false */
-	KUNIT_EXPECT_FALSE(test, rootid_owns_currentns(nonroot_vfsuid));
+	/* Non-zero UID should return false */
+	KUNIT_EXPECT_FALSE(test, uid_owns_currentns(vfsuid));
 }
 
 /**
- * test_rootid_owns_userns_init_ns - Test rootid_owns_userns with init namespace
+ * test_uid_owns_ns_init_ns_uid0 - Test uid_owns_ns with init namespace and UID 0
  *
- * Verifies that rootid_owns_userns correctly identifies root UID in init namespace.
- * This tests the core namespace traversal logic.
+ * Verifies that uid_owns_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_rootid_owns_userns_init_ns(struct kunit *test)
+static void test_uid_owns_ns_init_ns_uid0(struct kunit *test)
 {
-	kuid_t root_kuid;
+	kuid_t kuid;
 	struct user_namespace *init_ns;
 
-	root_kuid = KUIDT_INIT(0);
+	kuid = KUIDT_INIT(0);
 	init_ns = &init_user_ns;
 
-	/* Root UID should own init namespace */
-	KUNIT_EXPECT_TRUE(test, rootid_owns_userns(init_ns, root_kuid));
+	/* UID 0 should own init namespace */
+	KUNIT_EXPECT_TRUE(test, uid_owns_ns(init_ns, kuid));
 }
 
 /**
- * test_rootid_owns_userns_nonroot - Test rootid_owns_userns with non-root UID
+ * test_uid_owns_ns_init_ns_nonzero - Test uid_owns_ns with init namespace and non-zero UID
  *
- * Verifies that rootid_owns_userns correctly rejects non-root UIDs.
+ * Verifies that uid_owns_ns correctly rejects non-zero UIDs in init namespace.
+ * Only UID 0 should own a namespace.
  *
  * @test: KUnit test context
  */
-static void test_rootid_owns_userns_nonroot(struct kunit *test)
+static void test_uid_owns_ns_init_ns_nonzero(struct kunit *test)
 {
-	kuid_t nonroot_kuid;
+	kuid_t kuid;
 	struct user_namespace *init_ns;
 
-	nonroot_kuid = KUIDT_INIT(1000);
+	kuid = KUIDT_INIT(1000);
 	init_ns = &init_user_ns;
 
-	/* Non-root UID should not own namespace */
-	KUNIT_EXPECT_FALSE(test, rootid_owns_userns(init_ns, nonroot_kuid));
+	/* Non-zero UID should not own namespace */
+	KUNIT_EXPECT_FALSE(test, uid_owns_ns(init_ns, kuid));
+}
+
+/**
+ * test_uid_owns_ns_init_ns_various_uids - Test uid_owns_ns with various UIDs
+ *
+ * Verifies that uid_owns_ns correctly identifies only UID 0 as owning
+ * the namespace, regardless of the UID value tested.
+ *
+ * @test: KUnit test context
+ */
+static void test_uid_owns_ns_init_ns_various_uids(struct kunit *test)
+{
+	struct user_namespace *init_ns;
+	kuid_t kuid;
+
+	init_ns = &init_user_ns;
+
+	/* UID 0 should own the namespace */
+	kuid = KUIDT_INIT(0);
+	KUNIT_EXPECT_TRUE(test, uid_owns_ns(init_ns, kuid));
+
+	/* Other UIDs should not own the namespace */
+	kuid = KUIDT_INIT(1);
+	KUNIT_EXPECT_FALSE(test, uid_owns_ns(init_ns, kuid));
+
+	kuid = KUIDT_INIT(1000);
+	KUNIT_EXPECT_FALSE(test, uid_owns_ns(init_ns, kuid));
+
+	kuid = KUIDT_INIT(65534);
+	KUNIT_EXPECT_FALSE(test, uid_owns_ns(init_ns, kuid));
+}
+
+/**
+ * create_test_user_ns_with_mapping - Create a test user namespace with uid mapping
+ *
+ * Creates a minimal user namespace for testing where uid 0 in the namespace
+ * maps to the specified kuid in the parent namespace.
+ *
+ * The mapping semantics:
+ * - first: uid in this namespace (0)
+ * - lower_first: kuid in parent namespace (mapped_kuid)
+ * - count: range size (1)
+ *
+ * This means: from_kuid(ns, mapped_kuid) will return 0
+ * because map_id_up looks for kuid in [lower_first, lower_first+count)
+ * and returns first + (kuid - lower_first) = 0 + (mapped_kuid - mapped_kuid) = 0
+ *
+ * @test: KUnit test context
+ * @parent_ns: Parent user namespace
+ * @mapped_kuid: The kuid that uid 0 in the new namespace maps to
+ *
+ * Returns: The new user 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);
+
+	/* 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_uid_owns_ns_with_mapping - Test uid_owns_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 uid_owns_ns correctly identifies kuid 1000 as owning the namespace.
+ *
+ * Note: uid_owns_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_uid_owns_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);  /* uid 0 in test_ns maps to kuid 1000 */
+	other_kuid = KUIDT_INIT(2000);   /* This should not own the namespace */
+
+	/* Create test namespace where uid 0 maps to kuid 1000 */
+	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 the namespace (because uid 0 in test_ns maps to it) */
+	KUNIT_EXPECT_TRUE(test, uid_owns_ns(test_ns, mapped_kuid));
+
+	/* kuid 0 also owns the namespace because it maps to 0 in init_user_ns (parent) */
+	KUNIT_EXPECT_TRUE(test, uid_owns_ns(test_ns, KUIDT_INIT(0)));
+
+	/* Other kuids that don't map to 0 in test_ns or any parent should not own */
+	KUNIT_EXPECT_FALSE(test, uid_owns_ns(test_ns, other_kuid));
+	KUNIT_EXPECT_FALSE(test, uid_owns_ns(test_ns, KUIDT_INIT(500)));
+}
+
+/**
+ * test_uid_owns_ns_with_different_mappings - Test with multiple namespaces
+ *					      having different mappings
+ *
+ * Creates multiple test namespaces with different uid 0 mappings to verify
+ * the function correctly identifies ownership based on the mapping.
+ *
+ * Since uid_owns_ns walks up the hierarchy, kuids that map to 0 in init_user_ns
+ * (like kuid 0) will own all namespaces. But we can still verify that the
+ * specific mapped kuids own their respective namespaces.
+ *
+ * @test: KUnit test context
+ */
+static void test_uid_owns_ns_with_different_mappings(struct kunit *test)
+{
+	struct user_namespace *ns1, *ns2, *ns3;
+	struct user_namespace *parent_ns;
+
+	parent_ns = &init_user_ns;
+
+	/* Namespace 1: uid 0 maps to kuid 1000 */
+	ns1 = create_test_user_ns_with_mapping(test, parent_ns, KUIDT_INIT(1000));
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns1);
+	/* kuid 1000 owns ns1 because it maps to uid 0 in ns1 */
+	KUNIT_EXPECT_TRUE(test, uid_owns_ns(ns1, KUIDT_INIT(1000)));
+	/* kuid 0 also owns ns1 because it maps to 0 in init_user_ns (parent) */
+	KUNIT_EXPECT_TRUE(test, uid_owns_ns(ns1, KUIDT_INIT(0)));
+	/* kuid 2000 doesn't map to 0 in ns1 or any parent */
+	KUNIT_EXPECT_FALSE(test, uid_owns_ns(ns1, KUIDT_INIT(2000)));
+
+	/* Namespace 2: uid 0 maps to kuid 2000 */
+	ns2 = create_test_user_ns_with_mapping(test, parent_ns, KUIDT_INIT(2000));
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns2);
+	/* kuid 2000 owns ns2 because it maps to uid 0 in ns2 */
+	KUNIT_EXPECT_TRUE(test, uid_owns_ns(ns2, KUIDT_INIT(2000)));
+	/* kuid 0 also owns ns2 because it maps to 0 in init_user_ns (parent) */
+	KUNIT_EXPECT_TRUE(test, uid_owns_ns(ns2, KUIDT_INIT(0)));
+	/* kuid 1000 doesn't map to 0 in ns2 or any parent */
+	KUNIT_EXPECT_FALSE(test, uid_owns_ns(ns2, KUIDT_INIT(1000)));
+
+	/* Namespace 3: uid 0 maps to kuid 0 (identity mapping) */
+	ns3 = create_test_user_ns_with_mapping(test, parent_ns, KUIDT_INIT(0));
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns3);
+	/* kuid 0 owns ns3 because it maps to uid 0 in ns3 */
+	KUNIT_EXPECT_TRUE(test, uid_owns_ns(ns3, KUIDT_INIT(0)));
+	/* kuid 1000 doesn't map to 0 in ns3 or any parent */
+	KUNIT_EXPECT_FALSE(test, uid_owns_ns(ns3, KUIDT_INIT(1000)));
+	/* kuid 2000 doesn't map to 0 in ns3 or any parent */
+	KUNIT_EXPECT_FALSE(test, uid_owns_ns(ns3, KUIDT_INIT(2000)));
 }
 
 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),
-	KUNIT_CASE(test_rootid_owns_userns_init_ns),
-	KUNIT_CASE(test_rootid_owns_userns_nonroot),
+	KUNIT_CASE(test_uid_owns_currentns_init_ns),
+	KUNIT_CASE(test_uid_owns_currentns_invalid),
+	KUNIT_CASE(test_uid_owns_currentns_nonzero),
+	KUNIT_CASE(test_uid_owns_ns_init_ns_uid0),
+	KUNIT_CASE(test_uid_owns_ns_init_ns_nonzero),
+	KUNIT_CASE(test_uid_owns_ns_init_ns_various_uids),
+	KUNIT_CASE(test_uid_owns_ns_with_mapping),
+	KUNIT_CASE(test_uid_owns_ns_with_different_mappings),
 	{}
 };
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v2] security: Rename functions and add namespace mapping tests
  2025-11-21 17:48 ` [PATCH v2] security: Rename functions and add namespace mapping tests Ryan Foster
@ 2025-11-25 15:15   ` Serge E. Hallyn
  2025-12-04 21:56     ` [PATCH] security: Add KUnit tests for kuid_root_in_ns and vfsuid_root_in_currentns Ryan Foster
  0 siblings, 1 reply; 7+ messages in thread
From: Serge E. Hallyn @ 2025-11-25 15:15 UTC (permalink / raw)
  To: Ryan Foster; +Cc: serge, paul, linux-security-module, linux-kernel

On Fri, Nov 21, 2025 at 09:48:26AM -0800, Ryan Foster wrote:
> Rename rootid_owns_currentns() to uid_owns_currentns() and
> rootid_owns_userns() to uid_owns_ns() for clarity, as the function checks
> any UID, not just root. Update all call sites accordingly.
> 
> Add tests that create actual user namespaces with different UID mappings
> to verify namespace traversal logic. The tests create namespaces where
> uid 0 maps to different kuids (e.g., kuid 1000, 2000) and verify that
> uid_owns_ns() correctly identifies ownership based on the namespace
> hierarchy traversal.
> 
> This addresses feedback to use clearer function naming and test actual
> namespace functionality with real user namespace creation and mappings,
> rather than just basic input validation.

Hi Ryan,

did you see https://lore.kernel.org/all/aR0JrOvDxDKZPELd@mail.hallyn.com ?

That is now in linux-next, and should be merged into 6.19 when that window
opens.  So please base your patch on that (so you can drop your uid_owns_ns()
renames).

I haven't looked closely at the tests, but at a cursory glance this is what
I had in mind, thanks!  I'll look more closely when you send next version.

> ---
>  security/commoncap.c      |  26 ++--
>  security/commoncap_test.c | 286 ++++++++++++++++++++++++++++++++------
>  2 files changed, 254 insertions(+), 58 deletions(-)
> 
> diff --git a/security/commoncap.c b/security/commoncap.c
> index 15d8147a34c4..cca291df9551 100644
> --- a/security/commoncap.c
> +++ b/security/commoncap.c
> @@ -359,16 +359,16 @@ int cap_inode_killpriv(struct mnt_idmap *idmap, struct dentry *dentry)
>  }
>  
>  #ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST
> -bool rootid_owns_userns(struct user_namespace *ns, kuid_t kroot);
> -bool rootid_owns_userns(struct user_namespace *ns, kuid_t kroot)
> +bool uid_owns_ns(struct user_namespace *ns, kuid_t kuid);
> +bool uid_owns_ns(struct user_namespace *ns, kuid_t kuid)
>  #else
> -static bool rootid_owns_userns(struct user_namespace *ns, kuid_t kroot)
> +static bool uid_owns_ns(struct user_namespace *ns, kuid_t kuid)
>  #endif
>  {
>  	struct user_namespace *iter;
>  
>  	for (iter = ns;; iter = iter->parent) {
> -		if (from_kuid(iter, kroot) == 0)
> +		if (from_kuid(iter, kuid) == 0)
>  			return true;
>  		if (iter == &init_user_ns)
>  			break;
> @@ -378,19 +378,19 @@ static bool rootid_owns_userns(struct user_namespace *ns, kuid_t kroot)
>  }
>  
>  #ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST
> -bool rootid_owns_currentns(vfsuid_t rootvfsuid);
> -bool rootid_owns_currentns(vfsuid_t rootvfsuid)
> +bool uid_owns_currentns(vfsuid_t vfsuid);
> +bool uid_owns_currentns(vfsuid_t vfsuid)
>  #else
> -static bool rootid_owns_currentns(vfsuid_t rootvfsuid)
> +static bool uid_owns_currentns(vfsuid_t vfsuid)
>  #endif
>  {
> -	kuid_t kroot;
> +	kuid_t kuid;
>  
> -	if (!vfsuid_valid(rootvfsuid))
> +	if (!vfsuid_valid(vfsuid))
>  		return false;
>  
> -	kroot = vfsuid_into_kuid(rootvfsuid);
> -	return rootid_owns_userns(current_user_ns(), kroot);
> +	kuid = vfsuid_into_kuid(vfsuid);
> +	return uid_owns_ns(current_user_ns(), kuid);
>  }
>  
>  static __u32 sansflags(__u32 m)
> @@ -497,7 +497,7 @@ int cap_inode_getsecurity(struct mnt_idmap *idmap,
>  		goto out_free;
>  	}
>  
> -	if (!rootid_owns_currentns(vfsroot)) {
> +	if (!uid_owns_currentns(vfsroot)) {
>  		size = -EOVERFLOW;
>  		goto out_free;
>  	}
> @@ -738,7 +738,7 @@ int get_vfs_caps_from_disk(struct mnt_idmap *idmap,
>  	/* Limit the caps to the mounter of the filesystem
>  	 * or the more limited uid specified in the xattr.
>  	 */
> -	if (!rootid_owns_currentns(rootvfsuid))
> +	if (!uid_owns_currentns(rootvfsuid))
>  		return -ENODATA;
>  
>  	cpu_caps->permitted.val = le32_to_cpu(caps->data[0].permitted);
> diff --git a/security/commoncap_test.c b/security/commoncap_test.c
> index 962aa899455d..7f066dc0df5d 100644
> --- a/security/commoncap_test.c
> +++ b/security/commoncap_test.c
> @@ -10,6 +10,8 @@
>  #include <linux/user_namespace.h>
>  #include <linux/uidgid.h>
>  #include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/refcount.h>
>  
>  /* Forward declare types and functions we need from mnt_idmapping.h
>   * We avoid including the full header because it contains inline functions
> @@ -50,38 +52,38 @@ static inline kuid_t vfsuid_into_kuid(vfsuid_t vfsuid)
>  #ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST
>  
>  /* Forward declarations - functions are exported when KUNIT_TEST is enabled */
> -extern bool rootid_owns_userns(struct user_namespace *ns, kuid_t kroot);
> -extern bool rootid_owns_currentns(vfsuid_t rootvfsuid);
> +extern bool uid_owns_ns(struct user_namespace *ns, kuid_t kuid);
> +extern bool uid_owns_currentns(vfsuid_t vfsuid);
>  
>  /**
> - * test_rootid_owns_currentns_init_ns - Test rootid_owns_currentns with init ns
> + * test_uid_owns_currentns_init_ns - Test uid_owns_currentns with init ns
>   *
> - * Verifies that a root ID in the init namespace correctly owns the current
> + * 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_rootid_owns_currentns_init_ns(struct kunit *test)
> +static void test_uid_owns_currentns_init_ns(struct kunit *test)
>  {
> -	vfsuid_t root_vfsuid;
> -	kuid_t root_kuid;
> +	vfsuid_t vfsuid;
> +	kuid_t kuid;
>  
> -	/* Create a root UID in init namespace */
> -	root_kuid = KUIDT_INIT(0);
> -	root_vfsuid = VFSUIDT_INIT(root_kuid);
> +	/* Create UID 0 in init namespace */
> +	kuid = KUIDT_INIT(0);
> +	vfsuid = VFSUIDT_INIT(kuid);
>  
> -	/* In init namespace, root should own current namespace */
> -	KUNIT_EXPECT_TRUE(test, rootid_owns_currentns(root_vfsuid));
> +	/* In init namespace, UID 0 should own current namespace */
> +	KUNIT_EXPECT_TRUE(test, uid_owns_currentns(vfsuid));
>  }
>  
>  /**
> - * test_rootid_owns_currentns_invalid - Test rootid_owns_currentns with invalid vfsuid
> + * test_uid_owns_currentns_invalid - Test uid_owns_currentns with invalid vfsuid
>   *
>   * Verifies that an invalid vfsuid correctly returns false.
>   *
>   * @test: KUnit test context
>   */
> -static void test_rootid_owns_currentns_invalid(struct kunit *test)
> +static void test_uid_owns_currentns_invalid(struct kunit *test)
>  {
>  	vfsuid_t invalid_vfsuid;
>  
> @@ -89,74 +91,268 @@ static void test_rootid_owns_currentns_invalid(struct kunit *test)
>  	invalid_vfsuid = INVALID_VFSUID;
>  
>  	/* Invalid vfsuid should return false */
> -	KUNIT_EXPECT_FALSE(test, rootid_owns_currentns(invalid_vfsuid));
> +	KUNIT_EXPECT_FALSE(test, uid_owns_currentns(invalid_vfsuid));
>  }
>  
>  /**
> - * test_rootid_owns_currentns_nonroot - Test rootid_owns_currentns with non-root UID
> + * test_uid_owns_currentns_nonzero - Test uid_owns_currentns with non-zero UID
>   *
> - * Verifies that a non-root UID correctly returns false.
> + * Verifies that a non-zero UID correctly returns false.
>   *
>   * @test: KUnit test context
>   */
> -static void test_rootid_owns_currentns_nonroot(struct kunit *test)
> +static void test_uid_owns_currentns_nonzero(struct kunit *test)
>  {
> -	vfsuid_t nonroot_vfsuid;
> -	kuid_t nonroot_kuid;
> +	vfsuid_t vfsuid;
> +	kuid_t kuid;
>  
> -	/* Create a non-root UID */
> -	nonroot_kuid = KUIDT_INIT(1000);
> -	nonroot_vfsuid = VFSUIDT_INIT(nonroot_kuid);
> +	/* Create a non-zero UID */
> +	kuid = KUIDT_INIT(1000);
> +	vfsuid = VFSUIDT_INIT(kuid);
>  
> -	/* Non-root UID should return false */
> -	KUNIT_EXPECT_FALSE(test, rootid_owns_currentns(nonroot_vfsuid));
> +	/* Non-zero UID should return false */
> +	KUNIT_EXPECT_FALSE(test, uid_owns_currentns(vfsuid));
>  }
>  
>  /**
> - * test_rootid_owns_userns_init_ns - Test rootid_owns_userns with init namespace
> + * test_uid_owns_ns_init_ns_uid0 - Test uid_owns_ns with init namespace and UID 0
>   *
> - * Verifies that rootid_owns_userns correctly identifies root UID in init namespace.
> - * This tests the core namespace traversal logic.
> + * Verifies that uid_owns_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_rootid_owns_userns_init_ns(struct kunit *test)
> +static void test_uid_owns_ns_init_ns_uid0(struct kunit *test)
>  {
> -	kuid_t root_kuid;
> +	kuid_t kuid;
>  	struct user_namespace *init_ns;
>  
> -	root_kuid = KUIDT_INIT(0);
> +	kuid = KUIDT_INIT(0);
>  	init_ns = &init_user_ns;
>  
> -	/* Root UID should own init namespace */
> -	KUNIT_EXPECT_TRUE(test, rootid_owns_userns(init_ns, root_kuid));
> +	/* UID 0 should own init namespace */
> +	KUNIT_EXPECT_TRUE(test, uid_owns_ns(init_ns, kuid));
>  }
>  
>  /**
> - * test_rootid_owns_userns_nonroot - Test rootid_owns_userns with non-root UID
> + * test_uid_owns_ns_init_ns_nonzero - Test uid_owns_ns with init namespace and non-zero UID
>   *
> - * Verifies that rootid_owns_userns correctly rejects non-root UIDs.
> + * Verifies that uid_owns_ns correctly rejects non-zero UIDs in init namespace.
> + * Only UID 0 should own a namespace.
>   *
>   * @test: KUnit test context
>   */
> -static void test_rootid_owns_userns_nonroot(struct kunit *test)
> +static void test_uid_owns_ns_init_ns_nonzero(struct kunit *test)
>  {
> -	kuid_t nonroot_kuid;
> +	kuid_t kuid;
>  	struct user_namespace *init_ns;
>  
> -	nonroot_kuid = KUIDT_INIT(1000);
> +	kuid = KUIDT_INIT(1000);
>  	init_ns = &init_user_ns;
>  
> -	/* Non-root UID should not own namespace */
> -	KUNIT_EXPECT_FALSE(test, rootid_owns_userns(init_ns, nonroot_kuid));
> +	/* Non-zero UID should not own namespace */
> +	KUNIT_EXPECT_FALSE(test, uid_owns_ns(init_ns, kuid));
> +}
> +
> +/**
> + * test_uid_owns_ns_init_ns_various_uids - Test uid_owns_ns with various UIDs
> + *
> + * Verifies that uid_owns_ns correctly identifies only UID 0 as owning
> + * the namespace, regardless of the UID value tested.
> + *
> + * @test: KUnit test context
> + */
> +static void test_uid_owns_ns_init_ns_various_uids(struct kunit *test)
> +{
> +	struct user_namespace *init_ns;
> +	kuid_t kuid;
> +
> +	init_ns = &init_user_ns;
> +
> +	/* UID 0 should own the namespace */
> +	kuid = KUIDT_INIT(0);
> +	KUNIT_EXPECT_TRUE(test, uid_owns_ns(init_ns, kuid));
> +
> +	/* Other UIDs should not own the namespace */
> +	kuid = KUIDT_INIT(1);
> +	KUNIT_EXPECT_FALSE(test, uid_owns_ns(init_ns, kuid));
> +
> +	kuid = KUIDT_INIT(1000);
> +	KUNIT_EXPECT_FALSE(test, uid_owns_ns(init_ns, kuid));
> +
> +	kuid = KUIDT_INIT(65534);
> +	KUNIT_EXPECT_FALSE(test, uid_owns_ns(init_ns, kuid));
> +}
> +
> +/**
> + * create_test_user_ns_with_mapping - Create a test user namespace with uid mapping
> + *
> + * Creates a minimal user namespace for testing where uid 0 in the namespace
> + * maps to the specified kuid in the parent namespace.
> + *
> + * The mapping semantics:
> + * - first: uid in this namespace (0)
> + * - lower_first: kuid in parent namespace (mapped_kuid)
> + * - count: range size (1)
> + *
> + * This means: from_kuid(ns, mapped_kuid) will return 0
> + * because map_id_up looks for kuid in [lower_first, lower_first+count)
> + * and returns first + (kuid - lower_first) = 0 + (mapped_kuid - mapped_kuid) = 0
> + *
> + * @test: KUnit test context
> + * @parent_ns: Parent user namespace
> + * @mapped_kuid: The kuid that uid 0 in the new namespace maps to
> + *
> + * Returns: The new user 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);
> +
> +	/* 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_uid_owns_ns_with_mapping - Test uid_owns_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 uid_owns_ns correctly identifies kuid 1000 as owning the namespace.
> + *
> + * Note: uid_owns_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_uid_owns_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);  /* uid 0 in test_ns maps to kuid 1000 */
> +	other_kuid = KUIDT_INIT(2000);   /* This should not own the namespace */
> +
> +	/* Create test namespace where uid 0 maps to kuid 1000 */
> +	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 the namespace (because uid 0 in test_ns maps to it) */
> +	KUNIT_EXPECT_TRUE(test, uid_owns_ns(test_ns, mapped_kuid));
> +
> +	/* kuid 0 also owns the namespace because it maps to 0 in init_user_ns (parent) */
> +	KUNIT_EXPECT_TRUE(test, uid_owns_ns(test_ns, KUIDT_INIT(0)));
> +
> +	/* Other kuids that don't map to 0 in test_ns or any parent should not own */
> +	KUNIT_EXPECT_FALSE(test, uid_owns_ns(test_ns, other_kuid));
> +	KUNIT_EXPECT_FALSE(test, uid_owns_ns(test_ns, KUIDT_INIT(500)));
> +}
> +
> +/**
> + * test_uid_owns_ns_with_different_mappings - Test with multiple namespaces
> + *					      having different mappings
> + *
> + * Creates multiple test namespaces with different uid 0 mappings to verify
> + * the function correctly identifies ownership based on the mapping.
> + *
> + * Since uid_owns_ns walks up the hierarchy, kuids that map to 0 in init_user_ns
> + * (like kuid 0) will own all namespaces. But we can still verify that the
> + * specific mapped kuids own their respective namespaces.
> + *
> + * @test: KUnit test context
> + */
> +static void test_uid_owns_ns_with_different_mappings(struct kunit *test)
> +{
> +	struct user_namespace *ns1, *ns2, *ns3;
> +	struct user_namespace *parent_ns;
> +
> +	parent_ns = &init_user_ns;
> +
> +	/* Namespace 1: uid 0 maps to kuid 1000 */
> +	ns1 = create_test_user_ns_with_mapping(test, parent_ns, KUIDT_INIT(1000));
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns1);
> +	/* kuid 1000 owns ns1 because it maps to uid 0 in ns1 */
> +	KUNIT_EXPECT_TRUE(test, uid_owns_ns(ns1, KUIDT_INIT(1000)));
> +	/* kuid 0 also owns ns1 because it maps to 0 in init_user_ns (parent) */
> +	KUNIT_EXPECT_TRUE(test, uid_owns_ns(ns1, KUIDT_INIT(0)));
> +	/* kuid 2000 doesn't map to 0 in ns1 or any parent */
> +	KUNIT_EXPECT_FALSE(test, uid_owns_ns(ns1, KUIDT_INIT(2000)));
> +
> +	/* Namespace 2: uid 0 maps to kuid 2000 */
> +	ns2 = create_test_user_ns_with_mapping(test, parent_ns, KUIDT_INIT(2000));
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns2);
> +	/* kuid 2000 owns ns2 because it maps to uid 0 in ns2 */
> +	KUNIT_EXPECT_TRUE(test, uid_owns_ns(ns2, KUIDT_INIT(2000)));
> +	/* kuid 0 also owns ns2 because it maps to 0 in init_user_ns (parent) */
> +	KUNIT_EXPECT_TRUE(test, uid_owns_ns(ns2, KUIDT_INIT(0)));
> +	/* kuid 1000 doesn't map to 0 in ns2 or any parent */
> +	KUNIT_EXPECT_FALSE(test, uid_owns_ns(ns2, KUIDT_INIT(1000)));
> +
> +	/* Namespace 3: uid 0 maps to kuid 0 (identity mapping) */
> +	ns3 = create_test_user_ns_with_mapping(test, parent_ns, KUIDT_INIT(0));
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns3);
> +	/* kuid 0 owns ns3 because it maps to uid 0 in ns3 */
> +	KUNIT_EXPECT_TRUE(test, uid_owns_ns(ns3, KUIDT_INIT(0)));
> +	/* kuid 1000 doesn't map to 0 in ns3 or any parent */
> +	KUNIT_EXPECT_FALSE(test, uid_owns_ns(ns3, KUIDT_INIT(1000)));
> +	/* kuid 2000 doesn't map to 0 in ns3 or any parent */
> +	KUNIT_EXPECT_FALSE(test, uid_owns_ns(ns3, KUIDT_INIT(2000)));
>  }
>  
>  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),
> -	KUNIT_CASE(test_rootid_owns_userns_init_ns),
> -	KUNIT_CASE(test_rootid_owns_userns_nonroot),
> +	KUNIT_CASE(test_uid_owns_currentns_init_ns),
> +	KUNIT_CASE(test_uid_owns_currentns_invalid),
> +	KUNIT_CASE(test_uid_owns_currentns_nonzero),
> +	KUNIT_CASE(test_uid_owns_ns_init_ns_uid0),
> +	KUNIT_CASE(test_uid_owns_ns_init_ns_nonzero),
> +	KUNIT_CASE(test_uid_owns_ns_init_ns_various_uids),
> +	KUNIT_CASE(test_uid_owns_ns_with_mapping),
> +	KUNIT_CASE(test_uid_owns_ns_with_different_mappings),
>  	{}
>  };
>  
> -- 
> 2.43.0

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH] security: Add KUnit tests for kuid_root_in_ns and vfsuid_root_in_currentns
  2025-11-25 15:15   ` Serge E. Hallyn
@ 2025-12-04 21:56     ` Ryan Foster
  0 siblings, 0 replies; 7+ messages in thread
From: Ryan Foster @ 2025-12-04 21:56 UTC (permalink / raw)
  To: linux-security-module; +Cc: serge, paul, linux-kernel, Ryan Foster

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.

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
---
 security/Kconfig          |  17 +++
 security/commoncap.c      |   4 +
 security/commoncap_test.c | 290 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 311 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/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.43.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2025-12-04 21:56 UTC | newest]

Thread overview: 7+ 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-11-21 17:48 ` [PATCH v2] security: Rename functions and add namespace mapping tests Ryan Foster
2025-11-25 15:15   ` Serge E. Hallyn
2025-12-04 21:56     ` [PATCH] security: Add KUnit tests for kuid_root_in_ns and vfsuid_root_in_currentns Ryan Foster

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).