On 8 September 2026 23:27:30 BST, Bill Wendling <[email protected]> wrote:
>Add a KUnit test suite to verify the insertion and sorting of mappings
>in struct uid_gid_map. This test suite validates both base extent
>insertion (<= 5 mappings) and extended extent insertion (> 5 mappings,
>which triggers the allocation of the forward and reverse pointers).
>
>This is especially useful for verifying that the __counted_by_ptr
>attribute added to 'forward' and 'reverse' pointers works correctly
>without causing any runtime bounds-checking panics or traps.
>
>Assisted-by: Gemini:3.1-pro-preview
>Signed-off-by: Bill Wendling <[email protected]>
>Tested-by: Bradley Morgan <[email protected]>
>Reviewed-by: Bradley Morgan <[email protected]>
>---
>v2 - Remove Gerrit tag.
>v3 - s/KUNIT_EXPECT_NOT_ERR_OR_NULL/KUNIT_ASSERT_NOT_ERR_OR_NULL/
> - Fixed Kconfig tests.
>v4 - Actually test on unsorted data. Corrected the "Assisted-by" tag.
>v5 - Convert the KUnit test to a format that follows the KUnit style
> guild more closely. This includes some renameing and using macros
> in the "visibility.h" header. We no longer #include the test file
> into the implementation file. This should be much cleaner.
>---
>Cc: Bradley Morgan <[email protected]>
>Cc: Thomas Weißschuh <[email protected]>
>Cc: Kees Cook <[email protected]>
>Cc: "Gustavo A. R. Silva" <[email protected]>
>Cc: Christian Brauner <[email protected]>
>Cc: Aleksa Sarai <[email protected]>
>Cc: Jan Kara <[email protected]>
>Cc: Nathan Chancellor <[email protected]>
>Cc: Miguel Ojeda <[email protected]>
>Cc: Thomas Gleixner <[email protected]>
>Cc: Nicolas Schier <[email protected]>
>Cc: Gary Guo <[email protected]>
>Cc: "Thomas Weißschuh" <[email protected]>
>Cc: Alice Ryhl <[email protected]>
>Cc: Douglas Anderson <[email protected]>
>Cc: Anand Moon <[email protected]>
>Cc: Oleg Nesterov <[email protected]>
>Cc: [email protected]
>Cc: [email protected]
>Cc: [email protected]
>---
> include/linux/user_namespace.h | 6 ++
> init/Kconfig | 11 ++++
> kernel/Makefile | 1 +
> kernel/tests/.kunitconfig | 4 ++
> kernel/tests/user_ns_map_kunit.c | 98 ++++++++++++++++++++++++++++++++
> kernel/user_namespace.c | 8 ++-
> 6 files changed, 126 insertions(+), 2 deletions(-)
> create mode 100644 kernel/tests/.kunitconfig
> create mode 100644 kernel/tests/user_ns_map_kunit.c
>
>diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
>index 2962256eddf7..633157781edc 100644
>--- a/include/linux/user_namespace.h
>+++ b/include/linux/user_namespace.h
>@@ -207,6 +207,12 @@ extern bool in_userns(const struct user_namespace
>*ancestor,
> const struct user_namespace *child);
> extern bool current_in_userns(const struct user_namespace *target_ns);
> struct ns_common *ns_get_owner(struct ns_common *ns);
>+
>+#if IS_ENABLED(CONFIG_USER_NS_MAP_KUNIT_TEST)
>+extern int insert_extent(struct uid_gid_map *map, struct uid_gid_extent
>*extent);
>+extern int sort_idmaps(struct uid_gid_map *map);
>+#endif /* CONFIG_USER_NS_MAP_KUNIT_TEST */
>+
> #else
>
> static inline struct user_namespace *get_user_ns(struct user_namespace
> *ns)
>diff --git a/init/Kconfig b/init/Kconfig
>index 8583d9f06c52..27c1ffc675bf 100644
>--- a/init/Kconfig
>+++ b/init/Kconfig
>@@ -1457,6 +1457,17 @@ config USER_NS
>
> If unsure, say N.
>
>+config USER_NS_MAP_KUNIT_TEST
>+ tristate "KUint test for user
Hmm! s/KUint/KUnit
namespace map insertion" if !KUNIT_ALL_TESTS
>+ depends on USER_NS && KUNIT
>+ default KUNIT_ALL_TESTS
>+ help
>+ This builds the KUnit test for user namespace uid/gid map insertion.
>+ It validates map insertion, limits, dynamic allocation of the
>+ extended extents array, and mapping sorting functions.
>+
>+ If unsure, say N.
>+
> config PID_NS
> bool "PID Namespaces"
> default y
>diff --git a/kernel/Makefile b/kernel/Makefile
>index 1e1a31673577..2a64282749b8 100644
>--- a/kernel/Makefile
>+++ b/kernel/Makefile
>@@ -141,6 +141,7 @@ obj-$(CONFIG_WATCH_QUEUE) += watch_queue.o
>
> obj-$(CONFIG_RESOURCE_KUNIT_TEST) += resource_kunit.o
> obj-$(CONFIG_SYSCTL_KUNIT_TEST) += sysctl-test.o
>+obj-$(CONFIG_USER_NS_MAP_KUNIT_TEST) += tests/user_ns_map_kunit.o
>
> CFLAGS_kstack_erase.o += $(DISABLE_KSTACK_ERASE)
> CFLAGS_kstack_erase.o += $(call cc-option,-mgeneral-regs-only)
>diff --git a/kernel/tests/.kunitconfig b/kernel/tests/.kunitconfig
>new file mode 100644
>index 000000000000..b3d1206fd81a
>--- /dev/null
>+++ b/kernel/tests/.kunitconfig
>@@ -0,0 +1,4 @@
>+CONFIG_KUNIT=y
>+CONFIG_NAMESPACES=y
>+CONFIG_USER_NS=y
>+CONFIG_USER_NS_MAP_KUNIT_TEST=y
>diff --git a/kernel/tests/user_ns_map_kunit.c
>b/kernel/tests/user_ns_map_kunit.c
>new file mode 100644
>index 000000000000..24c21e43a36c
>--- /dev/null
>+++ b/kernel/tests/user_ns_map_kunit.c
>@@ -0,0 +1,98 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/*
>+ * KUnit test for user namespace map insertion and sorting.
>+ */
>+
>+#define pr_fmt(fmt) "user_namespace: " fmt
>+
>+#include <kunit/test.h>
>+#include <linux/user_namespace.h>
>+
>+#define NR_EXTENTS (UID_GID_MAP_MAX_BASE_EXTENTS + 5)
>+
>+static void user_ns_map_insert(struct kunit *test)
>+{
>+ struct uid_gid_map map;
>+ struct uid_gid_extent extent;
>+ int i, ret;
>+
>+ memset(&map, 0, sizeof(map));
>+
>+ /* Insert up to UID_GID_MAP_MAX_BASE_EXTENTS elements */
>+ for (i = 0; i < UID_GID_MAP_MAX_BASE_EXTENTS; i++) {
>+ extent.first = i * 10;
>+ extent.lower_first = i * 100;
>+ extent.count = 5;
>+
>+ ret = insert_extent(&map, &extent);
>+ KUNIT_ASSERT_EQ(test, ret, 0);
>+ }
>+
>+ KUNIT_EXPECT_EQ(test, map.nr_extents, UID_GID_MAP_MAX_BASE_EXTENTS);
>+
>+ /* Verify the elements ended up in the 'extent' array */
>+ for (i = 0; i < UID_GID_MAP_MAX_BASE_EXTENTS; i++) {
>+ KUNIT_EXPECT_EQ(test, map.extent[i].first, i * 10);
>+ KUNIT_EXPECT_EQ(test, map.extent[i].lower_first, i * 100);
>+ KUNIT_EXPECT_EQ(test, map.extent[i].count, 5);
>+ }
>+}
>+
>+static void user_ns_map_insert_extended(struct kunit *test)
>+{
>+ struct uid_gid_map map;
>+ struct uid_gid_extent extent;
>+ int i, ret;
>+
>+ memset(&map, 0, sizeof(map));
>+
>+ /* Insert more than UID_GID_MAP_MAX_BASE_EXTENTS elements */
>+ for (i = 0; i < NR_EXTENTS; i++) {
>+ int value = 9 - i;
>+
>+ extent.first = value * 10;
>+ extent.lower_first = value * 100;
>+ extent.count = 5;
>+
>+ ret = insert_extent(&map, &extent);
>+ KUNIT_ASSERT_EQ(test, ret, 0);
>+ }
>+
>+ KUNIT_EXPECT_EQ(test, map.nr_extents, NR_EXTENTS);
>+
>+ /* Now sort the map to set up reverse mapping */
>+ ret = sort_idmaps(&map);
>+ KUNIT_ASSERT_EQ(test, ret, 0);
>+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, map.reverse);
>+
>+ /* Verify the elements are in 'forward' and that sorting is correct */
>+ for (i = 0; i < map.nr_extents; i++) {
>+ KUNIT_EXPECT_EQ(test, map.forward[i].first, i * 10);
>+ KUNIT_EXPECT_EQ(test, map.forward[i].lower_first, i * 100);
>+ KUNIT_EXPECT_EQ(test, map.forward[i].count, 5);
>+
>+ KUNIT_EXPECT_EQ(test, map.reverse[i].first, i * 10);
>+ KUNIT_EXPECT_EQ(test, map.reverse[i].lower_first, i * 100);
>+ KUNIT_EXPECT_EQ(test, map.reverse[i].count, 5);
>+ }
>+
>+ kfree(map.forward);
>+ kfree(map.reverse);
>+}
>+
>+static struct kunit_case user_ns_map_test_cases[] = {
>+ KUNIT_CASE(user_ns_map_insert),
>+ KUNIT_CASE(user_ns_map_insert_extended),
>+ {}
>+};
>+
>+static struct kunit_suite user_ns_map_test_suite = {
>+ .name = "user_ns_map",
>+ .test_cases = user_ns_map_test_cases,
>+};
>+
>+kunit_test_suite(user_ns_map_test_suite);
>+
>+MODULE_LICENSE("GPL");
>+MODULE_DESCRIPTION("KUnit test for user namespace map insertion");
>+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
>diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
>index 786dbf0506ca..d8cbefd36598 100644
>--- a/kernel/user_namespace.c
>+++ b/kernel/user_namespace.c
>@@ -1,5 +1,6 @@
> // SPDX-License-Identifier: GPL-2.0-only
>
>+#include <kunit/visibility.h>
> #include <linux/export.h>
> #include <linux/nsproxy.h>
> #include <linux/slab.h>
>@@ -786,7 +787,8 @@ static bool mappings_overlap(struct uid_gid_map *new_map,
> * Takes care to allocate a 4K block of memory if the number of mappings
> exceeds
> * UID_GID_MAP_MAX_BASE_EXTENTS.
> */
>-static int insert_extent(struct uid_gid_map *map, struct uid_gid_extent
>*extent)
>+VISIBLE_IF_KUNIT int insert_extent(struct uid_gid_map *map,
>+ struct uid_gid_extent *extent)
> {
> struct uid_gid_extent *dest;
>
>@@ -822,6 +824,7 @@ static int insert_extent(struct uid_gid_map *map, struct
>uid_gid_extent *extent)
> *dest = *extent;
> return 0;
> }
>+EXPORT_SYMBOL_IF_KUNIT(insert_extent);
>
> /* cmp function to sort() forward mappings */
> static int cmp_extents_forward(const void *a, const void *b)
>@@ -857,7 +860,7 @@ static int cmp_extents_reverse(const void *a, const void
>*b)
> * sort_idmaps - Sorts an array of idmap entries.
> * Can only be called if number of mappings exceeds
> UID_GID_MAP_MAX_BASE_EXTENTS.
> */
>-static int sort_idmaps(struct uid_gid_map *map)
>+VISIBLE_IF_KUNIT int sort_idmaps(struct uid_gid_map *map)
> {
> if (map->nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS)
> return 0;
>@@ -878,6 +881,7 @@ static int sort_idmaps(struct uid_gid_map *map)
>
> return 0;
> }
>+EXPORT_SYMBOL_IF_KUNIT(sort_idmaps);
>
> /**
> * verify_root_map() - check the uid 0 mapping
>
--- Thanks!
https://lore.kernel.org/all/[email protected]/