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 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 -- 2.55.0.979.g7e5102b832-goog

