Merge patch series "userns: Add __counted_by_ptr attribute to struct uid_gid_map"

Add kunit tests for user namespace idmappings.

* patches from https://patch.msgid.link/20260908222734.3048684-1-morbo@google.com:
  userns: Add KUnit test suite for uid_gid_map
  userns: Add __counted_by_ptr attribute to struct uid_gid_map

Link: https://patch.msgid.link/20260908222734.3048684-1-morbo@google.com
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
Christian Brauner 2026-09-09 09:45:17 +02:00
commit 34a496d638
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2
6 changed files with 136 additions and 8 deletions

View File

@ -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,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)

View File

@ -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

View File

@ -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)

View File

@ -0,0 +1,4 @@
CONFIG_KUNIT=y
CONFIG_NAMESPACES=y
CONFIG_USER_NS=y
CONFIG_USER_NS_MAP_KUNIT_TEST=y

View File

@ -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");

View File

@ -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>
@ -783,7 +784,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;
@ -806,15 +808,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(insert_extent);
/* cmp function to sort() forward mappings */
static int cmp_extents_forward(const void *a, const void *b)
@ -850,7 +857,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;
@ -871,6 +878,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