summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Brauner <brauner@kernel.org>2026-09-25 18:20:10 +0200
committerChristian Brauner <brauner@kernel.org>2026-09-25 18:20:10 +0200
commitad05919c8f99ee173375681568176c24109b6500 (patch)
treee4b57c09e42f9115463e1f66874ee7088ec37464
parent4af31804c582569f6e9813336f630b871f9261a2 (diff)
parent15aa70de9f706fb0e3852f636b8a0a9d5bc048ab (diff)
downloadlinux-next-ad05919c8f99ee173375681568176c24109b6500.tar.gz
linux-next-ad05919c8f99ee173375681568176c24109b6500.zip
Merge branch 'namespace-7.4.misc' into vfs.all
Signed-off-by: Christian Brauner <brauner@kernel.org>
-rw-r--r--fs/namespace.c2
-rw-r--r--include/linux/user_namespace.h11
-rw-r--r--init/Kconfig11
-rw-r--r--kernel/Makefile1
-rw-r--r--kernel/tests/.kunitconfig4
-rw-r--r--kernel/tests/user_ns_map_kunit.c98
-rw-r--r--kernel/user_namespace.c32
-rw-r--r--kernel/utsname.c1
8 files changed, 142 insertions, 18 deletions
diff --git a/fs/namespace.c b/fs/namespace.c
index 580877e46b1a..1d60f0e195d4 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -4247,8 +4247,6 @@ struct mnt_namespace *copy_mnt_ns(u64 flags, struct mnt_namespace *ns,
struct mount *new;
int copy_flags;
- BUG_ON(!ns);
-
if (likely(!(flags & CLONE_NEWNS))) {
get_mnt_ns(ns);
return ns;
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index e38d9e60569f..91232053775d 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -29,8 +29,8 @@ struct uid_gid_map { /* 64 bytes -- 1 cache line */
u32 nr_extents;
};
struct {
- struct uid_gid_extent *forward;
- struct uid_gid_extent *reverse;
+ struct uid_gid_extent *forward __counted_by_ptr(nr_extents);
+ struct uid_gid_extent *reverse __counted_by_ptr(nr_extents);
};
};
};
@@ -207,6 +207,13 @@ 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_KUNIT)
+extern int uid_gid_map_insert_extent(struct uid_gid_map *map,
+ struct uid_gid_extent *extent);
+extern int uid_gid_map_sort(struct uid_gid_map *map);
+#endif /* CONFIG_KUNIT */
+
#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..033dccc6a535
--- /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 = uid_gid_map_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 = uid_gid_map_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 = uid_gid_map_sort(&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 0bed462e9b2a..98b0279b6cc6 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>
@@ -162,9 +163,6 @@ int create_user_ns(struct cred *new)
ns_tree_add(ns);
return 0;
fail_keyring:
-#ifdef CONFIG_PERSISTENT_KEYRINGS
- key_put(ns->persistent_keyring_register);
-#endif
ns_common_free(ns);
fail_free:
kmem_cache_free(user_ns_cachep, ns);
@@ -782,11 +780,13 @@ static bool mappings_overlap(struct uid_gid_map *new_map,
}
/*
- * insert_extent - Safely insert a new idmap extent into struct uid_gid_map.
+ * uid_gid_map_insert_extent - Safely insert a new idmap extent into
+ * struct uid_gid_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 uid_gid_map_insert_extent(struct uid_gid_map *map,
+ struct uid_gid_extent *extent)
{
struct uid_gid_extent *dest;
@@ -809,15 +809,20 @@ static int insert_extent(struct uid_gid_map *map, struct uid_gid_extent *extent)
map->reverse = NULL;
}
- if (map->nr_extents < UID_GID_MAP_MAX_BASE_EXTENTS)
- dest = &map->extent[map->nr_extents];
+ /*
+ * nr_extents must be updated before the extent and forward arrays are
+ * accessed, otherwise KSAN will assert an out-of-bounds error.
+ */
+ map->nr_extents++;
+ if (map->nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS)
+ dest = &map->extent[map->nr_extents - 1];
else
- dest = &map->forward[map->nr_extents];
+ dest = &map->forward[map->nr_extents - 1];
*dest = *extent;
- map->nr_extents++;
return 0;
}
+EXPORT_SYMBOL_IF_KUNIT(uid_gid_map_insert_extent);
/* cmp function to sort() forward mappings */
static int cmp_extents_forward(const void *a, const void *b)
@@ -850,10 +855,10 @@ static int cmp_extents_reverse(const void *a, const void *b)
}
/*
- * sort_idmaps - Sorts an array of idmap entries.
+ * uid_gid_map_sort - 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 uid_gid_map_sort(struct uid_gid_map *map)
{
if (map->nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS)
return 0;
@@ -874,6 +879,7 @@ static int sort_idmaps(struct uid_gid_map *map)
return 0;
}
+EXPORT_SYMBOL_IF_KUNIT(uid_gid_map_sort);
/**
* verify_root_map() - check the uid 0 mapping
@@ -1042,7 +1048,7 @@ static ssize_t map_write(struct file *file, const char __user *buf,
(next_line != NULL))
goto out;
- ret = insert_extent(&new_map, &extent);
+ ret = uid_gid_map_insert_extent(&new_map, &extent);
if (ret < 0)
goto out;
ret = -EINVAL;
@@ -1086,7 +1092,7 @@ static ssize_t map_write(struct file *file, const char __user *buf,
* If we want to use binary search for lookup, this clones the extent
* array and sorts both copies.
*/
- ret = sort_idmaps(&new_map);
+ ret = uid_gid_map_sort(&new_map);
if (ret < 0)
goto out;
diff --git a/kernel/utsname.c b/kernel/utsname.c
index ebbfc578a9d3..1ebf87e24607 100644
--- a/kernel/utsname.c
+++ b/kernel/utsname.c
@@ -81,7 +81,6 @@ struct uts_namespace *copy_utsname(u64 flags,
{
struct uts_namespace *new_ns;
- BUG_ON(!old_ns);
get_uts_ns(old_ns);
if (!(flags & CLONE_NEWUTS))