* [PATCH 1/2] userns: Add __counted_by_ptr attribute to struct uid_gid_map
[not found] <20260823125155.1136740-1-morbo@google.com>
@ 2026-08-23 12:51 ` Bill Wendling
2026-08-23 13:50 ` Bradley Morgan
` (2 more replies)
2026-08-23 12:51 ` [PATCH 2/2] userns: Add KUnit test suite for uid_gid_map Bill Wendling
2026-08-26 22:00 ` [PATCH v4 1/2] userns: Add __counted_by_ptr attribute to struct uid_gid_map Bill Wendling
2 siblings, 3 replies; 20+ messages in thread
From: Bill Wendling @ 2026-08-23 12:51 UTC (permalink / raw)
To: linux-kernel
Cc: Bill Wendling, Kees Cook, Gustavo A. R. Silva, Christian Brauner,
Aleksa Sarai, Jan Kara, Nathan Chancellor, Miguel Ojeda,
Thomas Gleixner, Nicolas Schier, Gary Guo, Thomas Weißschuh,
Alice Ryhl, Douglas Anderson, Anand Moon, Oleg Nesterov,
codemender-patching+linux, linux-hardening
The compiler attribute __counted_by_ptr associates a pointer field of a
struct with a sibling field within the same struct that specifies the
element count of the allocated memory. This enables KASAN and fortified
bounds-checking to detect out-of-bounds accesses to the pointer field at
runtime.
We can add the __counted_by_ptr attribute to the 'forward' and 'reverse'
pointer fields of 'struct uid_gid_map', which are counted by
'nr_extents'. Since 'nr_extents' is defined in a sibling anonymous
struct inside an anonymous union, the nearest common non-anonymous
struct level is 'struct uid_gid_map' itself, which is supported by the
compiler.
However, doing so has runtime implications. In the original
implementation of insert_extent(), elements are written to
map->forward[map->nr_extents] before map->nr_extents is incremented:
if (map->nr_extents < UID_GID_MAP_MAX_BASE_EXTENTS)
dest = &map->extent[map->nr_extents];
else
dest = &map->forward[map->nr_extents];
*dest = *extent;
map->nr_extents++;
At the time of writing to 'map->forward[map->nr_extents]',
map->nr_extents is still 5, but we are accessing index 5 (which is the
6th element). Under __counted_by_ptr(nr_extents), the compiler and
KASAN expect the accessed index to be strictly less than
map->nr_extents. Therefore, accessing index 5 when the count is 5
triggers an out-of-bounds panic/trap at runtime.
To resolve this, insert_extent() is refactored to increment
map->nr_extents first, and then use map->nr_extents - 1 as the index:
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 - 1];
*dest = *extent;
Assisted-by: Gemini Next
Signed-off-by: Bill Wendling <morbo@google.com>
---
Cc: Kees Cook <kees@kernel.org>
Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Aleksa Sarai <cyphar@cyphar.com>
Cc: Jan Kara <jack@suse.cz>
Cc: Nathan Chancellor <nathan@kernel.org>
Cc: Miguel Ojeda <ojeda@kernel.org>
Cc: Thomas Gleixner <tglx@kernel.org>
Cc: Nicolas Schier <nsc@kernel.org>
Cc: Gary Guo <gary@garyguo.net>
Cc: "Thomas Weißschuh" <thomas.weissschuh@linutronix.de>
Cc: Alice Ryhl <aliceryhl@google.com>
Cc: Douglas Anderson <dianders@chromium.org>
Cc: Anand Moon <linux.amoon@gmail.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: codemender-patching+linux@google.com
Cc: linux-kernel@vger.kernel.org
Cc: linux-hardening@vger.kernel.org
---
include/linux/user_namespace.h | 4 ++--
kernel/user_namespace.c | 8 ++++----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index e38d9e60569f..2962256eddf7 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);
};
};
};
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 0bed462e9b2a..7e5371d8f515 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -809,13 +809,13 @@ 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];
+ 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;
}
--
2.55.0.860.g4b6b3295ed-goog
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 2/2] userns: Add KUnit test suite for uid_gid_map
[not found] <20260823125155.1136740-1-morbo@google.com>
2026-08-23 12:51 ` [PATCH 1/2] userns: Add __counted_by_ptr attribute to struct uid_gid_map Bill Wendling
@ 2026-08-23 12:51 ` Bill Wendling
2026-08-23 12:53 ` Bill Wendling
` (2 more replies)
2026-08-26 22:00 ` [PATCH v4 1/2] userns: Add __counted_by_ptr attribute to struct uid_gid_map Bill Wendling
2 siblings, 3 replies; 20+ messages in thread
From: Bill Wendling @ 2026-08-23 12:51 UTC (permalink / raw)
To: linux-kernel
Cc: Bill Wendling, Kees Cook, Gustavo A. R. Silva, Christian Brauner,
Aleksa Sarai, Jan Kara, Nathan Chancellor, Miguel Ojeda,
Thomas Gleixner, Nicolas Schier, Gary Guo, Thomas Weißschuh,
Alice Ryhl, Douglas Anderson, Anand Moon, Oleg Nesterov,
codemender-patching+linux, linux-hardening
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 Next
Change-Id: If0c2c197a35cd7429cf0d2d6e3b33f0d9f0be66c
Signed-off-by: Bill Wendling <morbo@google.com>
---
Cc: Kees Cook <kees@kernel.org>
Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Aleksa Sarai <cyphar@cyphar.com>
Cc: Jan Kara <jack@suse.cz>
Cc: Nathan Chancellor <nathan@kernel.org>
Cc: Miguel Ojeda <ojeda@kernel.org>
Cc: Thomas Gleixner <tglx@kernel.org>
Cc: Nicolas Schier <nsc@kernel.org>
Cc: Gary Guo <gary@garyguo.net>
Cc: "Thomas Weißschuh" <thomas.weissschuh@linutronix.de>
Cc: Alice Ryhl <aliceryhl@google.com>
Cc: Douglas Anderson <dianders@chromium.org>
Cc: Anand Moon <linux.amoon@gmail.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: codemender-patching+linux@google.com
Cc: linux-kernel@vger.kernel.org
Cc: linux-hardening@vger.kernel.org
---
init/Kconfig | 10 ++++
kernel/.kunitconfig | 3 ++
kernel/user_namespace.c | 4 ++
kernel/user_namespace_kunit.c | 87 +++++++++++++++++++++++++++++++++++
4 files changed, 104 insertions(+)
create mode 100644 kernel/.kunitconfig
create mode 100644 kernel/user_namespace_kunit.c
diff --git a/init/Kconfig b/init/Kconfig
index f63bf5e05e79..ba6a40b7315a 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1457,6 +1457,16 @@ config USER_NS
If unsure, say N.
+config USER_NAMESPACE_KUNIT_TEST
+ tristate "KUnit 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/.kunitconfig b/kernel/.kunitconfig
new file mode 100644
index 000000000000..7314dce05dc2
--- /dev/null
+++ b/kernel/.kunitconfig
@@ -0,0 +1,3 @@
+CONFIG_KUNIT=y
+CONFIG_USER_NS=y
+CONFIG_USER_NAMESPACE_KUNIT_TEST=y
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 7e5371d8f515..64c64e1028e8 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -1413,3 +1413,7 @@ static __init int user_namespaces_init(void)
return 0;
}
subsys_initcall(user_namespaces_init);
+
+#if IS_ENABLED(CONFIG_USER_NAMESPACE_KUNIT_TEST)
+#include "user_namespace_kunit.c"
+#endif
diff --git a/kernel/user_namespace_kunit.c b/kernel/user_namespace_kunit.c
new file mode 100644
index 000000000000..6d7662ef1916
--- /dev/null
+++ b/kernel/user_namespace_kunit.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test for user namespace map insertion and sorting.
+ */
+
+#include <kunit/test.h>
+#include <linux/user_namespace.h>
+
+static void test_user_ns_map_insert_base(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 (5) 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_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, map.nr_extents, i + 1);
+ 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 test_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 (e.g., 10) elements */
+ for (i = 0; i < 10; i++) {
+ extent.first = i * 10;
+ extent.lower_first = i * 100;
+ extent.count = 5;
+
+ ret = insert_extent(&map, &extent);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, map.nr_extents, i + 1);
+
+ if (i < UID_GID_MAP_MAX_BASE_EXTENTS) {
+ KUNIT_EXPECT_EQ(test, map.extent[i].first, i * 10);
+ } else {
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, map.forward);
+ 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);
+ }
+ }
+
+ /* Now sort the map to set up reverse mapping */
+ ret = sort_idmaps(&map);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, map.reverse);
+
+ /* Verify sorting is correct */
+ for (i = 0; i < map.nr_extents; i++) {
+ KUNIT_EXPECT_EQ(test, map.forward[i].count, 5);
+ KUNIT_EXPECT_EQ(test, map.reverse[i].count, 5);
+ }
+
+ /* Clean up allocations to avoid leaks */
+ kfree(map.forward);
+ kfree(map.reverse);
+}
+
+static struct kunit_case user_ns_map_test_cases[] = {
+ KUNIT_CASE(test_user_ns_map_insert_base),
+ KUNIT_CASE(test_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);
--
2.55.0.860.g4b6b3295ed-goog
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 2/2] userns: Add KUnit test suite for uid_gid_map
2026-08-23 12:51 ` [PATCH 2/2] userns: Add KUnit test suite for uid_gid_map Bill Wendling
@ 2026-08-23 12:53 ` Bill Wendling
2026-08-26 20:43 ` [PATCH v3 " Bill Wendling
2026-08-23 13:48 ` [PATCH " Bradley Morgan
2026-08-24 6:13 ` Thomas Weißschuh
2 siblings, 1 reply; 20+ messages in thread
From: Bill Wendling @ 2026-08-23 12:53 UTC (permalink / raw)
Cc: Bill Wendling, Kees Cook, Gustavo A. R. Silva, Christian Brauner,
Aleksa Sarai, Jan Kara, Nathan Chancellor, Miguel Ojeda,
Thomas Gleixner, Nicolas Schier, Gary Guo, Thomas Weißschuh,
Alice Ryhl, Douglas Anderson, Anand Moon, Oleg Nesterov,
codemender-patching+linux, linux-kernel, linux-hardening
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 Next
Signed-off-by: Bill Wendling <morbo@google.com>
---
v2 - Remove Gerrit tag.
---
Cc: Kees Cook <kees@kernel.org>
Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Aleksa Sarai <cyphar@cyphar.com>
Cc: Jan Kara <jack@suse.cz>
Cc: Nathan Chancellor <nathan@kernel.org>
Cc: Miguel Ojeda <ojeda@kernel.org>
Cc: Thomas Gleixner <tglx@kernel.org>
Cc: Nicolas Schier <nsc@kernel.org>
Cc: Gary Guo <gary@garyguo.net>
Cc: "Thomas Weißschuh" <thomas.weissschuh@linutronix.de>
Cc: Alice Ryhl <aliceryhl@google.com>
Cc: Douglas Anderson <dianders@chromium.org>
Cc: Anand Moon <linux.amoon@gmail.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: codemender-patching+linux@google.com
Cc: linux-kernel@vger.kernel.org
Cc: linux-hardening@vger.kernel.org
---
init/Kconfig | 10 ++++
kernel/.kunitconfig | 3 ++
kernel/user_namespace.c | 4 ++
kernel/user_namespace_kunit.c | 87 +++++++++++++++++++++++++++++++++++
4 files changed, 104 insertions(+)
create mode 100644 kernel/.kunitconfig
create mode 100644 kernel/user_namespace_kunit.c
diff --git a/init/Kconfig b/init/Kconfig
index f63bf5e05e79..ba6a40b7315a 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1457,6 +1457,16 @@ config USER_NS
If unsure, say N.
+config USER_NAMESPACE_KUNIT_TEST
+ tristate "KUnit 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/.kunitconfig b/kernel/.kunitconfig
new file mode 100644
index 000000000000..7314dce05dc2
--- /dev/null
+++ b/kernel/.kunitconfig
@@ -0,0 +1,3 @@
+CONFIG_KUNIT=y
+CONFIG_USER_NS=y
+CONFIG_USER_NAMESPACE_KUNIT_TEST=y
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 7e5371d8f515..64c64e1028e8 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -1413,3 +1413,7 @@ static __init int user_namespaces_init(void)
return 0;
}
subsys_initcall(user_namespaces_init);
+
+#if IS_ENABLED(CONFIG_USER_NAMESPACE_KUNIT_TEST)
+#include "user_namespace_kunit.c"
+#endif
diff --git a/kernel/user_namespace_kunit.c b/kernel/user_namespace_kunit.c
new file mode 100644
index 000000000000..6d7662ef1916
--- /dev/null
+++ b/kernel/user_namespace_kunit.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test for user namespace map insertion and sorting.
+ */
+
+#include <kunit/test.h>
+#include <linux/user_namespace.h>
+
+static void test_user_ns_map_insert_base(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 (5) 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_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, map.nr_extents, i + 1);
+ 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 test_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 (e.g., 10) elements */
+ for (i = 0; i < 10; i++) {
+ extent.first = i * 10;
+ extent.lower_first = i * 100;
+ extent.count = 5;
+
+ ret = insert_extent(&map, &extent);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, map.nr_extents, i + 1);
+
+ if (i < UID_GID_MAP_MAX_BASE_EXTENTS) {
+ KUNIT_EXPECT_EQ(test, map.extent[i].first, i * 10);
+ } else {
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, map.forward);
+ 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);
+ }
+ }
+
+ /* Now sort the map to set up reverse mapping */
+ ret = sort_idmaps(&map);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, map.reverse);
+
+ /* Verify sorting is correct */
+ for (i = 0; i < map.nr_extents; i++) {
+ KUNIT_EXPECT_EQ(test, map.forward[i].count, 5);
+ KUNIT_EXPECT_EQ(test, map.reverse[i].count, 5);
+ }
+
+ /* Clean up allocations to avoid leaks */
+ kfree(map.forward);
+ kfree(map.reverse);
+}
+
+static struct kunit_case user_ns_map_test_cases[] = {
+ KUNIT_CASE(test_user_ns_map_insert_base),
+ KUNIT_CASE(test_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);
--
2.55.0.860.g4b6b3295ed-goog
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH 2/2] userns: Add KUnit test suite for uid_gid_map
2026-08-23 12:51 ` [PATCH 2/2] userns: Add KUnit test suite for uid_gid_map Bill Wendling
2026-08-23 12:53 ` Bill Wendling
@ 2026-08-23 13:48 ` Bradley Morgan
2026-08-26 20:37 ` Bill Wendling
2026-08-24 6:13 ` Thomas Weißschuh
2 siblings, 1 reply; 20+ messages in thread
From: Bradley Morgan @ 2026-08-23 13:48 UTC (permalink / raw)
To: morbo
Cc: aliceryhl, brauner, codemender-patching+linux, cyphar, dianders,
gary, gustavoars, jack, kees, linux-hardening, linux-kernel,
linux.amoon, nathan, nsc, ojeda, oleg, tglx, thomas.weissschuh
Hi Bill,
> +config USER_NAMESPACE_KUNIT_TEST
> + tristate "KUnit test for user namespace map insertion" if !KUNIT_ALL_TESTS
> + depends on USER_NS && KUNIT
The test is #include'd into user_namespace.c, which is builtin (USER_NS
is a bool), so =m here still compiles the suite into vmlinux. With
KUNIT=m that calls kunit symbols that live in a module, and the link
fails. Make it bool and depend on KUNIT=y, like EXEC_KUNIT_TEST:
bool "KUnit test for user namespace map insertion" if !KUNIT_ALL_TESTS
depends on USER_NS && KUNIT=y
> + /* Verify sorting is correct */
> + for (i = 0; i < map.nr_extents; i++) {
> + KUNIT_EXPECT_EQ(test, map.forward[i].count, 5);
> + KUNIT_EXPECT_EQ(test, map.reverse[i].count, 5);
> + }
This doesn't verify any sorting. Every extent was inserted with count
5, so the loop passes even if sort_idmaps() did nothing. Either assert
that forward is ordered by .first and reverse by .lower_first, or drop
the sorting claim from the changelog.
> + /* Clean up allocations to avoid leaks */
> + kfree(map.forward);
> + kfree(map.reverse);
Nice.
No tag, add me into V2, please?
Thanks!
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/2] userns: Add __counted_by_ptr attribute to struct uid_gid_map
2026-08-23 12:51 ` [PATCH 1/2] userns: Add __counted_by_ptr attribute to struct uid_gid_map Bill Wendling
@ 2026-08-23 13:50 ` Bradley Morgan
2026-08-23 14:52 ` Oleg Nesterov
2026-08-24 19:18 ` Gustavo A. R. Silva
2 siblings, 0 replies; 20+ messages in thread
From: Bradley Morgan @ 2026-08-23 13:50 UTC (permalink / raw)
To: morbo
Cc: aliceryhl, brauner, codemender-patching+linux, cyphar, dianders,
gary, gustavoars, jack, kees, linux-hardening, linux-kernel,
linux.amoon, nathan, nsc, ojeda, oleg, tglx, thomas.weissschuh
On 23 August 2026 13:51:47 BST, Bill Wendling <morbo@google.com> wrote:
>The compiler attribute __counted_by_ptr associates a pointer field of a
>struct with a sibling field within the same struct that specifies the
>element count of the allocated memory. This enables KASAN and fortified
>bounds-checking to detect out-of-bounds accesses to the pointer field at
>runtime.
>
Ack.
>We can add the __counted_by_ptr attribute to the 'forward' and 'reverse'
>pointer fields of 'struct uid_gid_map', which are counted by
>'nr_extents'. Since 'nr_extents' is defined in a sibling anonymous
>struct inside an anonymous union, the nearest common non-anonymous
>struct level is 'struct uid_gid_map' itself, which is supported by the
>compiler.
love it.
>However, doing so has runtime implications. In the original
>implementation of insert_extent(), elements are written to
>map->forward[map->nr_extents] before map->nr_extents is incremented:
>
Resounding ack.
> if (map->nr_extents < UID_GID_MAP_MAX_BASE_EXTENTS)
> dest = &map->extent[map->nr_extents];
> else
> dest = &map->forward[map->nr_extents];
>
> *dest = *extent;
> map->nr_extents++;
>
>At the time of writing to 'map->forward[map->nr_extents]',
>map->nr_extents is still 5, but we are accessing index 5 (which is the
>6th element). Under __counted_by_ptr(nr_extents), the compiler and
>KASAN expect the accessed index to be strictly less than
>map->nr_extents. Therefore, accessing index 5 when the count is 5
>triggers an out-of-bounds panic/trap at runtime.
oh!
>To resolve this, insert_extent() is refactored to increment
>map->nr_extents first, and then use map->nr_extents - 1 as the index:
>
> 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 - 1];
>
> *dest = *extent;
>
>Assisted-by: Gemini Next
Id like to wonder what the hell that model is Gemini 3.5 pro?, I gave it a
Google and saw nothing.
Btw.
Reviewed-by: Bradley Morgan <include@grrlz.net>
>Signed-off-by: Bill Wendling <morbo@google.com>
>---
>Cc: Kees Cook <kees@kernel.org>
>Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org>
>Cc: Christian Brauner <brauner@kernel.org>
>Cc: Aleksa Sarai <cyphar>
@cyphar.com>
>Cc: Jan Kara <jack@suse.cz>
>Cc: Nathan Chancellor <nathan@kernel.org>
>Cc: Miguel Ojeda <ojeda@kernel.org>
>Cc: Thomas Gleixner <tglx@kernel.org>
>Cc: Nicolas Schier <nsc@kernel.org>
>Cc: Gary Guo <gary@garyguo.net>
>Cc: "Thomas Weißschuh" <thomas.weissschuh@linutronix.de>
>Cc: Alice Ryhl <aliceryhl@google.com>
>Cc: Douglas Anderson <dianders@chromium.org>
>Cc: Anand Moon <linux.amoon@gmail.com>
>Cc: Oleg Nesterov <oleg@redhat.com>
>Cc: codemender-patching+linux@google.com
>Cc: linux-kernel@vger.kernel.org
>Cc: linux-hardening@vger.kernel.org
>---
> include/linux/user_namespace.h | 4 ++--
> kernel/user_namespace.c | 8 ++++----
> 2 files changed, 6 insertions(+), 6 deletions(-)
>
>diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
>index e38d9e60569f..2962256eddf7 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);
> };
> };
> };
>diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
>index 0bed462e9b2a..7e5371d8f515 100644
>--- a/kernel/user_namespace.c
>+++ b/kernel/user_namespace.c
>@@ -809,13 +809,13 @@ 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];
>+ 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;
> }
>
>
Thanks!
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/2] userns: Add __counted_by_ptr attribute to struct uid_gid_map
2026-08-23 12:51 ` [PATCH 1/2] userns: Add __counted_by_ptr attribute to struct uid_gid_map Bill Wendling
2026-08-23 13:50 ` Bradley Morgan
@ 2026-08-23 14:52 ` Oleg Nesterov
2026-08-26 22:05 ` Bill Wendling
2026-08-24 19:18 ` Gustavo A. R. Silva
2 siblings, 1 reply; 20+ messages in thread
From: Oleg Nesterov @ 2026-08-23 14:52 UTC (permalink / raw)
To: Bill Wendling
Cc: linux-kernel, Kees Cook, Gustavo A. R. Silva, Christian Brauner,
Aleksa Sarai, Jan Kara, Nathan Chancellor, Miguel Ojeda,
Thomas Gleixner, Nicolas Schier, Gary Guo, Thomas Weißschuh,
Alice Ryhl, Douglas Anderson, Anand Moon,
codemender-patching+linux, linux-hardening, Alexey Gladkov
On 08/23, Bill Wendling wrote:
>
> The compiler attribute __counted_by_ptr associates a pointer field of a
> struct with a sibling field within the same struct that specifies the
> element count of the allocated memory. This enables KASAN and fortified
> bounds-checking to detect out-of-bounds accesses to the pointer field at
> runtime.
>
> We can add the __counted_by_ptr attribute to the 'forward' and 'reverse'
> pointer fields of 'struct uid_gid_map', which are counted by
> 'nr_extents'. Since 'nr_extents' is defined in a sibling anonymous
> struct inside an anonymous union, the nearest common non-anonymous
> struct level is 'struct uid_gid_map' itself, which is supported by the
> compiler.
>
> However, doing so has runtime implications. In the original
> implementation of insert_extent(), elements are written to
> map->forward[map->nr_extents] before map->nr_extents is incremented:
>
> if (map->nr_extents < UID_GID_MAP_MAX_BASE_EXTENTS)
> dest = &map->extent[map->nr_extents];
> else
> dest = &map->forward[map->nr_extents];
>
> *dest = *extent;
> map->nr_extents++;
>
> At the time of writing to 'map->forward[map->nr_extents]',
> map->nr_extents is still 5, but we are accessing index 5 (which is the
> 6th element). Under __counted_by_ptr(nr_extents), the compiler and
> KASAN expect the accessed index to be strictly less than
> map->nr_extents. Therefore, accessing index 5 when the count is 5
> triggers an out-of-bounds panic/trap at runtime.
>
> To resolve this, insert_extent() is refactored to increment
> map->nr_extents first, and then use map->nr_extents - 1 as the index:
>
> 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 - 1];
>
> *dest = *extent;
I leave this to you and other reviewers (add Alexey), you can safely
ignore my nit.
To me
if (map->nr_extents < UID_GID_MAP_MAX_BASE_EXTENTS)
dest = &map->extent;
else
dest = &map->forward;
map->nr_extents++;
dest[map->nr_extents - 1] = *extent;
looks a bit more clear, but this is minor/subjective.
Either way, I think this needs a short comment to explain why do we
need to increment ->nr_extents first, then subtract 1. IOW, to explain
why (say)
dest[map->nr_extents++] = *extent;
would be wrong.
Oleg.
> Assisted-by: Gemini Next
> Signed-off-by: Bill Wendling <morbo@google.com>
> ---
> Cc: Kees Cook <kees@kernel.org>
> Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org>
> Cc: Christian Brauner <brauner@kernel.org>
> Cc: Aleksa Sarai <cyphar@cyphar.com>
> Cc: Jan Kara <jack@suse.cz>
> Cc: Nathan Chancellor <nathan@kernel.org>
> Cc: Miguel Ojeda <ojeda@kernel.org>
> Cc: Thomas Gleixner <tglx@kernel.org>
> Cc: Nicolas Schier <nsc@kernel.org>
> Cc: Gary Guo <gary@garyguo.net>
> Cc: "Thomas Weißschuh" <thomas.weissschuh@linutronix.de>
> Cc: Alice Ryhl <aliceryhl@google.com>
> Cc: Douglas Anderson <dianders@chromium.org>
> Cc: Anand Moon <linux.amoon@gmail.com>
> Cc: Oleg Nesterov <oleg@redhat.com>
> Cc: codemender-patching+linux@google.com
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-hardening@vger.kernel.org
> ---
> include/linux/user_namespace.h | 4 ++--
> kernel/user_namespace.c | 8 ++++----
> 2 files changed, 6 insertions(+), 6 deletions(-)
>
> diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
> index e38d9e60569f..2962256eddf7 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);
> };
> };
> };
> diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
> index 0bed462e9b2a..7e5371d8f515 100644
> --- a/kernel/user_namespace.c
> +++ b/kernel/user_namespace.c
> @@ -809,13 +809,13 @@ 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];
> + 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;
> }
>
> --
> 2.55.0.860.g4b6b3295ed-goog
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 2/2] userns: Add KUnit test suite for uid_gid_map
2026-08-23 12:51 ` [PATCH 2/2] userns: Add KUnit test suite for uid_gid_map Bill Wendling
2026-08-23 12:53 ` Bill Wendling
2026-08-23 13:48 ` [PATCH " Bradley Morgan
@ 2026-08-24 6:13 ` Thomas Weißschuh
2026-08-26 20:40 ` Bill Wendling
2 siblings, 1 reply; 20+ messages in thread
From: Thomas Weißschuh @ 2026-08-24 6:13 UTC (permalink / raw)
To: Bill Wendling
Cc: linux-kernel, Kees Cook, Gustavo A. R. Silva, Christian Brauner,
Aleksa Sarai, Jan Kara, Nathan Chancellor, Miguel Ojeda,
Thomas Gleixner, Nicolas Schier, Gary Guo, Alice Ryhl,
Douglas Anderson, Anand Moon, Oleg Nesterov,
codemender-patching+linux, linux-hardening
On Sun, Aug 23, 2026 at 12:51:48PM +0000, Bill Wendling 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.
AFAIU patch 1 is supposed to not change any behavior.
You could move the unit test to the front to make that clearer
and also validate it.
> Assisted-by: Gemini Next
> Change-Id: If0c2c197a35cd7429cf0d2d6e3b33f0d9f0be66c
Change-Id should not be used upstream.
See Documentation/dev-tools/checkpatch.rst.
> Signed-off-by: Bill Wendling <morbo@google.com>
> ---
> Cc: Kees Cook <kees@kernel.org>
> Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org>
> Cc: Christian Brauner <brauner@kernel.org>
> Cc: Aleksa Sarai <cyphar@cyphar.com>
> Cc: Jan Kara <jack@suse.cz>
> Cc: Nathan Chancellor <nathan@kernel.org>
> Cc: Miguel Ojeda <ojeda@kernel.org>
> Cc: Thomas Gleixner <tglx@kernel.org>
> Cc: Nicolas Schier <nsc@kernel.org>
> Cc: Gary Guo <gary@garyguo.net>
> Cc: "Thomas Weißschuh" <thomas.weissschuh@linutronix.de>
> Cc: Alice Ryhl <aliceryhl@google.com>
> Cc: Douglas Anderson <dianders@chromium.org>
> Cc: Anand Moon <linux.amoon@gmail.com>
> Cc: Oleg Nesterov <oleg@redhat.com>
> Cc: codemender-patching+linux@google.com
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-hardening@vger.kernel.org
> ---
> init/Kconfig | 10 ++++
> kernel/.kunitconfig | 3 ++
> kernel/user_namespace.c | 4 ++
> kernel/user_namespace_kunit.c | 87 +++++++++++++++++++++++++++++++++++
> 4 files changed, 104 insertions(+)
> create mode 100644 kernel/.kunitconfig
> create mode 100644 kernel/user_namespace_kunit.c
(...)
> +static void test_user_ns_map_insert_extended(struct kunit *test)
> +{
> + struct uid_gid_map map;
> + struct uid_gid_extent extent;
> + int i, ret;
(...)
> + /* Now sort the map to set up reverse mapping */
> + ret = sort_idmaps(&map);
> + KUNIT_EXPECT_EQ(test, ret, 0);
> + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, map.reverse);
KUNIT_EXPECT_*() will *not* abort the test when the assertion fails ...
> +
> + /* Verify sorting is correct */
> + for (i = 0; i < map.nr_extents; i++) {
> + KUNIT_EXPECT_EQ(test, map.forward[i].count, 5);
> + KUNIT_EXPECT_EQ(test, map.reverse[i].count, 5);
... leading to a crash here if map.reverse is invalid.
To also abort the test on assertion failure use KUNIT_ASSERT_*().
> + }
> +
> + /* Clean up allocations to avoid leaks */
Pointless comment. This is true for every single call of kfree().
> + kfree(map.forward);
> + kfree(map.reverse);
> +}
(...)
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/2] userns: Add __counted_by_ptr attribute to struct uid_gid_map
2026-08-23 12:51 ` [PATCH 1/2] userns: Add __counted_by_ptr attribute to struct uid_gid_map Bill Wendling
2026-08-23 13:50 ` Bradley Morgan
2026-08-23 14:52 ` Oleg Nesterov
@ 2026-08-24 19:18 ` Gustavo A. R. Silva
2 siblings, 0 replies; 20+ messages in thread
From: Gustavo A. R. Silva @ 2026-08-24 19:18 UTC (permalink / raw)
To: Bill Wendling, linux-kernel
Cc: Kees Cook, Gustavo A. R. Silva, Christian Brauner, Aleksa Sarai,
Jan Kara, Nathan Chancellor, Miguel Ojeda, Thomas Gleixner,
Nicolas Schier, Gary Guo, Thomas Weißschuh, Alice Ryhl,
Douglas Anderson, Anand Moon, Oleg Nesterov,
codemender-patching+linux, linux-hardening
On 8/23/26 06:51, Bill Wendling wrote:
> The compiler attribute __counted_by_ptr associates a pointer field of a
> struct with a sibling field within the same struct that specifies the
> element count of the allocated memory. This enables KASAN and fortified
> bounds-checking to detect out-of-bounds accesses to the pointer field at
> runtime.
>
> We can add the __counted_by_ptr attribute to the 'forward' and 'reverse'
> pointer fields of 'struct uid_gid_map', which are counted by
> 'nr_extents'. Since 'nr_extents' is defined in a sibling anonymous
> struct inside an anonymous union, the nearest common non-anonymous
> struct level is 'struct uid_gid_map' itself, which is supported by the
> compiler.
>
> However, doing so has runtime implications. In the original
> implementation of insert_extent(), elements are written to
> map->forward[map->nr_extents] before map->nr_extents is incremented:
>
> if (map->nr_extents < UID_GID_MAP_MAX_BASE_EXTENTS)
> dest = &map->extent[map->nr_extents];
> else
> dest = &map->forward[map->nr_extents];
>
> *dest = *extent;
> map->nr_extents++;
>
> At the time of writing to 'map->forward[map->nr_extents]',
> map->nr_extents is still 5, but we are accessing index 5 (which is the
> 6th element). Under __counted_by_ptr(nr_extents), the compiler and
> KASAN expect the accessed index to be strictly less than
> map->nr_extents. Therefore, accessing index 5 when the count is 5
> triggers an out-of-bounds panic/trap at runtime.
>
> To resolve this, insert_extent() is refactored to increment
> map->nr_extents first, and then use map->nr_extents - 1 as the index:
>
> 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 - 1];
>
> *dest = *extent;
>
> Assisted-by: Gemini Next
> Signed-off-by: Bill Wendling <morbo@google.com>
Reviewed-by: Gustavo A. R. Silva <gustavoars@kernel.org>
Thanks
-Gustavo
> ---
> Cc: Kees Cook <kees@kernel.org>
> Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org>
> Cc: Christian Brauner <brauner@kernel.org>
> Cc: Aleksa Sarai <cyphar@cyphar.com>
> Cc: Jan Kara <jack@suse.cz>
> Cc: Nathan Chancellor <nathan@kernel.org>
> Cc: Miguel Ojeda <ojeda@kernel.org>
> Cc: Thomas Gleixner <tglx@kernel.org>
> Cc: Nicolas Schier <nsc@kernel.org>
> Cc: Gary Guo <gary@garyguo.net>
> Cc: "Thomas Weißschuh" <thomas.weissschuh@linutronix.de>
> Cc: Alice Ryhl <aliceryhl@google.com>
> Cc: Douglas Anderson <dianders@chromium.org>
> Cc: Anand Moon <linux.amoon@gmail.com>
> Cc: Oleg Nesterov <oleg@redhat.com>
> Cc: codemender-patching+linux@google.com
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-hardening@vger.kernel.org
> ---
> include/linux/user_namespace.h | 4 ++--
> kernel/user_namespace.c | 8 ++++----
> 2 files changed, 6 insertions(+), 6 deletions(-)
>
> diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
> index e38d9e60569f..2962256eddf7 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);
> };
> };
> };
> diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
> index 0bed462e9b2a..7e5371d8f515 100644
> --- a/kernel/user_namespace.c
> +++ b/kernel/user_namespace.c
> @@ -809,13 +809,13 @@ 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];
> + 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;
> }
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 2/2] userns: Add KUnit test suite for uid_gid_map
2026-08-23 13:48 ` [PATCH " Bradley Morgan
@ 2026-08-26 20:37 ` Bill Wendling
0 siblings, 0 replies; 20+ messages in thread
From: Bill Wendling @ 2026-08-26 20:37 UTC (permalink / raw)
To: Bradley Morgan
Cc: aliceryhl, brauner, codemender-patching+linux, cyphar, dianders,
gary, gustavoars, jack, kees, linux-hardening, linux-kernel,
linux.amoon, nathan, nsc, ojeda, oleg, tglx, thomas.weissschuh
Hi Bradley,
On Sun, Aug 23, 2026 at 6:48 AM Bradley Morgan <include@grrlz.net> wrote:
>
> Hi Bill,
>
> > +config USER_NAMESPACE_KUNIT_TEST
> > + tristate "KUnit test for user namespace map insertion" if !KUNIT_ALL_TESTS
> > + depends on USER_NS && KUNIT
>
> The test is #include'd into user_namespace.c, which is builtin (USER_NS
> is a bool), so =m here still compiles the suite into vmlinux. With
> KUNIT=m that calls kunit symbols that live in a module, and the link
> fails. Make it bool and depend on KUNIT=y, like EXEC_KUNIT_TEST:
>
> bool "KUnit test for user namespace map insertion" if !KUNIT_ALL_TESTS
> depends on USER_NS && KUNIT=y
>
Okay.
> > + /* Verify sorting is correct */
> > + for (i = 0; i < map.nr_extents; i++) {
> > + KUNIT_EXPECT_EQ(test, map.forward[i].count, 5);
> > + KUNIT_EXPECT_EQ(test, map.reverse[i].count, 5);
> > + }
>
> This doesn't verify any sorting. Every extent was inserted with count
> 5, so the loop passes even if sort_idmaps() did nothing. Either assert
> that forward is ordered by .first and reverse by .lower_first, or drop
> the sorting claim from the changelog.
>
Good call. I added in checking for actual values.
> > + /* Clean up allocations to avoid leaks */
> > + kfree(map.forward);
> > + kfree(map.reverse);
>
> Nice.
>
> No tag, add me into V2, please?
>
Done!
-bw
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 2/2] userns: Add KUnit test suite for uid_gid_map
2026-08-24 6:13 ` Thomas Weißschuh
@ 2026-08-26 20:40 ` Bill Wendling
2026-08-27 13:25 ` Thomas Weißschuh
0 siblings, 1 reply; 20+ messages in thread
From: Bill Wendling @ 2026-08-26 20:40 UTC (permalink / raw)
To: Thomas Weißschuh
Cc: linux-kernel, Kees Cook, Gustavo A. R. Silva, Christian Brauner,
Aleksa Sarai, Jan Kara, Nathan Chancellor, Miguel Ojeda,
Thomas Gleixner, Nicolas Schier, Gary Guo, Alice Ryhl,
Douglas Anderson, Anand Moon, Oleg Nesterov,
codemender-patching+linux, linux-hardening
Hi Thomas,
On Sun, Aug 23, 2026 at 11:13 PM Thomas Weißschuh
<thomas.weissschuh@linutronix.de> wrote:
>
> On Sun, Aug 23, 2026 at 12:51:48PM +0000, Bill Wendling 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.
>
> AFAIU patch 1 is supposed to not change any behavior.
> You could move the unit test to the front to make that clearer
> and also validate it.
>
Patch 1 generates code to assert if a memory address outside of the
bounds of a pointer is accessed. So it does change the behavior.
Should I still move the test case?
> > Assisted-by: Gemini Next
> > Change-Id: If0c2c197a35cd7429cf0d2d6e3b33f0d9f0be66c
>
> Change-Id should not be used upstream.
> See Documentation/dev-tools/checkpatch.rst.
>
I had removed it in v2. :-)
> > Signed-off-by: Bill Wendling <morbo@google.com>
> > ---
> > Cc: Kees Cook <kees@kernel.org>
> > Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org>
> > Cc: Christian Brauner <brauner@kernel.org>
> > Cc: Aleksa Sarai <cyphar@cyphar.com>
> > Cc: Jan Kara <jack@suse.cz>
> > Cc: Nathan Chancellor <nathan@kernel.org>
> > Cc: Miguel Ojeda <ojeda@kernel.org>
> > Cc: Thomas Gleixner <tglx@kernel.org>
> > Cc: Nicolas Schier <nsc@kernel.org>
> > Cc: Gary Guo <gary@garyguo.net>
> > Cc: "Thomas Weißschuh" <thomas.weissschuh@linutronix.de>
> > Cc: Alice Ryhl <aliceryhl@google.com>
> > Cc: Douglas Anderson <dianders@chromium.org>
> > Cc: Anand Moon <linux.amoon@gmail.com>
> > Cc: Oleg Nesterov <oleg@redhat.com>
> > Cc: codemender-patching+linux@google.com
> > Cc: linux-kernel@vger.kernel.org
> > Cc: linux-hardening@vger.kernel.org
> > ---
> > init/Kconfig | 10 ++++
> > kernel/.kunitconfig | 3 ++
> > kernel/user_namespace.c | 4 ++
> > kernel/user_namespace_kunit.c | 87 +++++++++++++++++++++++++++++++++++
> > 4 files changed, 104 insertions(+)
> > create mode 100644 kernel/.kunitconfig
> > create mode 100644 kernel/user_namespace_kunit.c
>
> (...)
>
> > +static void test_user_ns_map_insert_extended(struct kunit *test)
> > +{
> > + struct uid_gid_map map;
> > + struct uid_gid_extent extent;
> > + int i, ret;
>
> (...)
>
> > + /* Now sort the map to set up reverse mapping */
> > + ret = sort_idmaps(&map);
> > + KUNIT_EXPECT_EQ(test, ret, 0);
> > + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, map.reverse);
>
> KUNIT_EXPECT_*() will *not* abort the test when the assertion fails ...
>
> > +
> > + /* Verify sorting is correct */
> > + for (i = 0; i < map.nr_extents; i++) {
> > + KUNIT_EXPECT_EQ(test, map.forward[i].count, 5);
> > + KUNIT_EXPECT_EQ(test, map.reverse[i].count, 5);
>
> ... leading to a crash here if map.reverse is invalid.
>
> To also abort the test on assertion failure use KUNIT_ASSERT_*().
>
Roger that! I changed them to KUNIT_ASSERT_*.
> > + }
> > +
> > + /* Clean up allocations to avoid leaks */
>
> Pointless comment. This is true for every single call of kfree().
>
Kiboshed.
Thanks!
-bw
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v3 2/2] userns: Add KUnit test suite for uid_gid_map
2026-08-23 12:53 ` Bill Wendling
@ 2026-08-26 20:43 ` Bill Wendling
2026-08-26 21:21 ` Bradley Morgan
0 siblings, 1 reply; 20+ messages in thread
From: Bill Wendling @ 2026-08-26 20:43 UTC (permalink / raw)
Cc: Bill Wendling, Bradley Morgan, Thomas Weißschuh, Kees Cook,
Gustavo A. R. Silva, Christian Brauner, Aleksa Sarai, Jan Kara,
Nathan Chancellor, Miguel Ojeda, Thomas Gleixner, Nicolas Schier,
Gary Guo, Alice Ryhl, Douglas Anderson, Anand Moon, Oleg Nesterov,
codemender-patching+linux, linux-kernel, linux-hardening
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 Next
Signed-off-by: Bill Wendling <morbo@google.com>
---
v2 - Remove Gerrit tag.
v3 - s/KUNIT_EXPECT_NOT_ERR_OR_NULL/KUNIT_ASSERT_NOT_ERR_OR_NULL/
- Fixed Kconfig tests.
---
Cc: Bradley Morgan <brads@mainlining.org>
Cc: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
Cc: Kees Cook <kees@kernel.org>
Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Aleksa Sarai <cyphar@cyphar.com>
Cc: Jan Kara <jack@suse.cz>
Cc: Nathan Chancellor <nathan@kernel.org>
Cc: Miguel Ojeda <ojeda@kernel.org>
Cc: Thomas Gleixner <tglx@kernel.org>
Cc: Nicolas Schier <nsc@kernel.org>
Cc: Gary Guo <gary@garyguo.net>
Cc: "Thomas Weißschuh" <thomas.weissschuh@linutronix.de>
Cc: Alice Ryhl <aliceryhl@google.com>
Cc: Douglas Anderson <dianders@chromium.org>
Cc: Anand Moon <linux.amoon@gmail.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: codemender-patching+linux@google.com
Cc: linux-kernel@vger.kernel.org
Cc: linux-hardening@vger.kernel.org
---
init/Kconfig | 10 ++++
kernel/.kunitconfig | 3 ++
kernel/user_namespace.c | 4 ++
kernel/user_namespace_kunit.c | 91 +++++++++++++++++++++++++++++++++++
4 files changed, 108 insertions(+)
create mode 100644 kernel/.kunitconfig
create mode 100644 kernel/user_namespace_kunit.c
diff --git a/init/Kconfig b/init/Kconfig
index f63bf5e05e79..ba6a40b7315a 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1457,6 +1457,16 @@ config USER_NS
If unsure, say N.
+config USER_NAMESPACE_KUNIT_TEST
+ bool "KUnit 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/.kunitconfig b/kernel/.kunitconfig
new file mode 100644
index 000000000000..7314dce05dc2
--- /dev/null
+++ b/kernel/.kunitconfig
@@ -0,0 +1,3 @@
+CONFIG_KUNIT=y
+CONFIG_USER_NS=y
+CONFIG_USER_NAMESPACE_KUNIT_TEST=y
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 7e5371d8f515..64c64e1028e8 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -1413,3 +1413,7 @@ static __init int user_namespaces_init(void)
return 0;
}
subsys_initcall(user_namespaces_init);
+
+#if IS_ENABLED(CONFIG_USER_NAMESPACE_KUNIT_TEST)
+#include "user_namespace_kunit.c"
+#endif
diff --git a/kernel/user_namespace_kunit.c b/kernel/user_namespace_kunit.c
new file mode 100644
index 000000000000..d97dbac65e50
--- /dev/null
+++ b/kernel/user_namespace_kunit.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test for user namespace map insertion and sorting.
+ */
+
+#include <kunit/test.h>
+#include <linux/user_namespace.h>
+
+static void test_user_ns_map_insert_base(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 (5) 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_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, map.nr_extents, i + 1);
+ 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 test_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 (e.g., 10) elements */
+ for (i = 0; i < 10; i++) {
+ extent.first = i * 10;
+ extent.lower_first = i * 100;
+ extent.count = 5;
+
+ ret = insert_extent(&map, &extent);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, map.nr_extents, i + 1);
+
+ if (i < UID_GID_MAP_MAX_BASE_EXTENTS) {
+ KUNIT_EXPECT_EQ(test, map.extent[i].first, i * 10);
+ } else {
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, map.forward);
+ 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);
+ }
+ }
+
+ /* Now sort the map to set up reverse mapping */
+ ret = sort_idmaps(&map);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, map.reverse);
+
+ /* Verify 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(test_user_ns_map_insert_base),
+ KUNIT_CASE(test_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);
--
2.55.0.897.gb25b4bd76c-goog
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH v3 2/2] userns: Add KUnit test suite for uid_gid_map
2026-08-26 20:43 ` [PATCH v3 " Bill Wendling
@ 2026-08-26 21:21 ` Bradley Morgan
2026-08-26 22:04 ` Bill Wendling
0 siblings, 1 reply; 20+ messages in thread
From: Bradley Morgan @ 2026-08-26 21:21 UTC (permalink / raw)
To: Bill Wendling
Cc: Thomas Weißschuh, Kees Cook, Gustavo A. R. Silva,
Christian Brauner, Aleksa Sarai, Jan Kara, Nathan Chancellor,
Miguel Ojeda, Thomas Gleixner, Nicolas Schier, Gary Guo,
Alice Ryhl, Douglas Anderson, Anand Moon, Oleg Nesterov,
codemender-patching+linux, linux-kernel, linux-hardening
> + /* Insert more than UID_GID_MAP_MAX_BASE_EXTENTS (e.g., 10) elements */
> + for (i = 0; i < 10; i++) {
> + extent.first = i * 10;
> + extent.lower_first = i * 100;
Both .first and .lower_first increase monotonically with i. So
forward[] is already sorted by .first and reverse[] is already sorted
by .lower_first before sort_idmaps() runs. The sort is a no op on
this input.
> + /* Verify 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.reverse[i].lower_first, i * 100);
This passes even if sort_idmaps() did nothing. The test cannot tell
a working sort from a broken one.
To actually verify sorting, insert the extents out of order so that
sort_idmaps() has to rearrange them. For example:
extent.first = (9 - i) * 10;
extent.lower_first = (9 - i) * 100;
Then after sort_idmaps(), forward[0].first should be 0, forward[1]
should be 10, etc. That proves the sort moved things.
Nits:
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, map.forward);
Redundant. insert_extent() just allocated it on the 6th call, and
the previous KUNIT_EXPECT_EQ(test, ret, 0) already confirmed success.
If you want to keep it, at least put it after the loop where it
protects the sort, not inside the insertion loop where it tests
something that just succeeded.
> + KUNIT_EXPECT_EQ(test, ret, 0);
Should be KUNIT_ASSERT_EQ. If insert_extent() fails the test
continues and the next assertions run on uninitialized data. Same
for the base test.
No tag yet, fix the test data and I'll tag v4.
--- Thanks!
https://lore.kernel.org/all/EE579805-42F2-4C58-B752-F28779EEB717@grrlz.net/
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v4 1/2] userns: Add __counted_by_ptr attribute to struct uid_gid_map
[not found] <20260823125155.1136740-1-morbo@google.com>
2026-08-23 12:51 ` [PATCH 1/2] userns: Add __counted_by_ptr attribute to struct uid_gid_map Bill Wendling
2026-08-23 12:51 ` [PATCH 2/2] userns: Add KUnit test suite for uid_gid_map Bill Wendling
@ 2026-08-26 22:00 ` Bill Wendling
2026-08-26 22:00 ` [PATCH v4 2/2] userns: Add KUnit test suite for uid_gid_map Bill Wendling
2 siblings, 1 reply; 20+ messages in thread
From: Bill Wendling @ 2026-08-26 22:00 UTC (permalink / raw)
Cc: Bill Wendling, Gustavo A. R. Silva, Bradley Morgan,
Thomas Weißschuh, Kees Cook, Christian Brauner, Aleksa Sarai,
Jan Kara, Nathan Chancellor, Miguel Ojeda, Thomas Gleixner,
Nicolas Schier, Gary Guo, Alice Ryhl, Douglas Anderson,
Anand Moon, Oleg Nesterov, codemender-patching+linux,
linux-kernel, linux-hardening
The compiler attribute __counted_by_ptr associates a pointer field of a
struct with a sibling field within the same struct that specifies the
element count of the allocated memory. This enables KASAN and fortified
bounds-checking to detect out-of-bounds accesses to the pointer field at
runtime.
We can add the __counted_by_ptr attribute to the 'forward' and 'reverse'
pointer fields of 'struct uid_gid_map', which are counted by
'nr_extents'. Since 'nr_extents' is defined in a sibling anonymous
struct inside an anonymous union, the nearest common non-anonymous
struct level is 'struct uid_gid_map' itself, which is supported by the
compiler.
However, doing so has runtime implications. In the original
implementation of insert_extent(), elements are written to
map->forward[map->nr_extents] before map->nr_extents is incremented:
if (map->nr_extents < UID_GID_MAP_MAX_BASE_EXTENTS)
dest = &map->extent[map->nr_extents];
else
dest = &map->forward[map->nr_extents];
*dest = *extent;
map->nr_extents++;
At the time of writing to 'map->forward[map->nr_extents]',
map->nr_extents is still 5, but we are accessing index 5 (which is the
6th element). Under __counted_by_ptr(nr_extents), the compiler and
KASAN expect the accessed index to be strictly less than
map->nr_extents. Therefore, accessing index 5 when the count is 5
triggers an out-of-bounds panic/trap at runtime.
To resolve this, insert_extent() is refactored to increment
map->nr_extents first, and then use map->nr_extents - 1 as the index:
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 - 1];
*dest = *extent;
Assisted-by: Gemini:3.1-pro-preview
Signed-off-by: Bill Wendling <morbo@google.com>
Reviewed-by: Gustavo A. R. Silva <gustavoars@kernel.org>
Reviewed-by: Bradley Morgan <brads@mainlining.org>
---
v2 - Remove Gerrit tag.
v4 - Added comment explaning the change. Corrected the "Assisted-by" tag.
---
Cc: Bradley Morgan <brads@mainlining.org>
Cc: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
Cc: Kees Cook <kees@kernel.org>
Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Aleksa Sarai <cyphar@cyphar.com>
Cc: Jan Kara <jack@suse.cz>
Cc: Nathan Chancellor <nathan@kernel.org>
Cc: Miguel Ojeda <ojeda@kernel.org>
Cc: Thomas Gleixner <tglx@kernel.org>
Cc: Nicolas Schier <nsc@kernel.org>
Cc: Gary Guo <gary@garyguo.net>
Cc: "Thomas Weißschuh" <thomas.weissschuh@linutronix.de>
Cc: Alice Ryhl <aliceryhl@google.com>
Cc: Douglas Anderson <dianders@chromium.org>
Cc: Anand Moon <linux.amoon@gmail.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: codemender-patching+linux@google.com
Cc: linux-kernel@vger.kernel.org
Cc: linux-hardening@vger.kernel.org
---
include/linux/user_namespace.h | 4 ++--
kernel/user_namespace.c | 12 ++++++++----
2 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index e38d9e60569f..2962256eddf7 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);
};
};
};
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 0bed462e9b2a..786dbf0506ca 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -809,13 +809,17 @@ 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;
}
--
2.55.0.897.gb25b4bd76c-goog
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v4 2/2] userns: Add KUnit test suite for uid_gid_map
2026-08-26 22:00 ` [PATCH v4 1/2] userns: Add __counted_by_ptr attribute to struct uid_gid_map Bill Wendling
@ 2026-08-26 22:00 ` Bill Wendling
2026-08-26 22:27 ` Bradley Morgan
2026-08-27 13:36 ` Thomas Weißschuh
0 siblings, 2 replies; 20+ messages in thread
From: Bill Wendling @ 2026-08-26 22:00 UTC (permalink / raw)
Cc: Bill Wendling, Bradley Morgan, Thomas Weißschuh, Kees Cook,
Gustavo A. R. Silva, Christian Brauner, Aleksa Sarai, Jan Kara,
Nathan Chancellor, Miguel Ojeda, Thomas Gleixner, Nicolas Schier,
Gary Guo, Alice Ryhl, Douglas Anderson, Anand Moon, Oleg Nesterov,
codemender-patching+linux, linux-kernel, linux-hardening
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 <morbo@google.com>
---
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.
---
Cc: Bradley Morgan <brads@mainlining.org>
Cc: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
Cc: Kees Cook <kees@kernel.org>
Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Aleksa Sarai <cyphar@cyphar.com>
Cc: Jan Kara <jack@suse.cz>
Cc: Nathan Chancellor <nathan@kernel.org>
Cc: Miguel Ojeda <ojeda@kernel.org>
Cc: Thomas Gleixner <tglx@kernel.org>
Cc: Nicolas Schier <nsc@kernel.org>
Cc: Gary Guo <gary@garyguo.net>
Cc: "Thomas Weißschuh" <thomas.weissschuh@linutronix.de>
Cc: Alice Ryhl <aliceryhl@google.com>
Cc: Douglas Anderson <dianders@chromium.org>
Cc: Anand Moon <linux.amoon@gmail.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: codemender-patching+linux@google.com
Cc: linux-kernel@vger.kernel.org
Cc: linux-hardening@vger.kernel.org
---
init/Kconfig | 10 ++++
kernel/.kunitconfig | 3 ++
kernel/user_namespace.c | 4 ++
kernel/user_namespace_kunit.c | 92 +++++++++++++++++++++++++++++++++++
4 files changed, 109 insertions(+)
create mode 100644 kernel/.kunitconfig
create mode 100644 kernel/user_namespace_kunit.c
diff --git a/init/Kconfig b/init/Kconfig
index f63bf5e05e79..d460344539a5 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1457,6 +1457,16 @@ config USER_NS
If unsure, say N.
+config USER_NAMESPACE_KUNIT_TEST
+ bool "Test user namespace map insertion" if !KUNIT_ALL_TESTS
+ depends on KUNIT=y
+ 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/.kunitconfig b/kernel/.kunitconfig
new file mode 100644
index 000000000000..7314dce05dc2
--- /dev/null
+++ b/kernel/.kunitconfig
@@ -0,0 +1,3 @@
+CONFIG_KUNIT=y
+CONFIG_USER_NS=y
+CONFIG_USER_NAMESPACE_KUNIT_TEST=y
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 786dbf0506ca..0e7373085af9 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -1417,3 +1417,7 @@ static __init int user_namespaces_init(void)
return 0;
}
subsys_initcall(user_namespaces_init);
+
+#if IS_ENABLED(CONFIG_USER_NAMESPACE_KUNIT_TEST)
+#include "user_namespace_kunit.c"
+#endif
diff --git a/kernel/user_namespace_kunit.c b/kernel/user_namespace_kunit.c
new file mode 100644
index 000000000000..88467361efdf
--- /dev/null
+++ b/kernel/user_namespace_kunit.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test for user namespace map insertion and sorting.
+ */
+
+#include <kunit/test.h>
+#include <linux/user_namespace.h>
+
+static void test_user_ns_map_insert_base(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 (5) 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, i + 1);
+ 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 test_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 (e.g., 10) elements */
+ for (i = 0; i < 10; 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, i + 1);
+
+ if (i < UID_GID_MAP_MAX_BASE_EXTENTS) {
+ KUNIT_EXPECT_EQ(test, map.extent[i].first, value * 10);
+ } else {
+ KUNIT_EXPECT_EQ(test, map.forward[i].first, value * 10);
+ KUNIT_EXPECT_EQ(test, map.forward[i].lower_first, value * 100);
+ KUNIT_EXPECT_EQ(test, map.forward[i].count, 5);
+ }
+ }
+
+ /* Now sort the map to set up reverse mapping */
+ ret = sort_idmaps(&map);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, map.reverse);
+
+ /* Verify 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(test_user_ns_map_insert_base),
+ KUNIT_CASE(test_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);
--
2.55.0.897.gb25b4bd76c-goog
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH v3 2/2] userns: Add KUnit test suite for uid_gid_map
2026-08-26 21:21 ` Bradley Morgan
@ 2026-08-26 22:04 ` Bill Wendling
0 siblings, 0 replies; 20+ messages in thread
From: Bill Wendling @ 2026-08-26 22:04 UTC (permalink / raw)
To: Bradley Morgan
Cc: Thomas Weißschuh, Kees Cook, Gustavo A. R. Silva,
Christian Brauner, Aleksa Sarai, Jan Kara, Nathan Chancellor,
Miguel Ojeda, Thomas Gleixner, Nicolas Schier, Gary Guo,
Alice Ryhl, Douglas Anderson, Anand Moon, Oleg Nesterov,
codemender-patching+linux, linux-kernel, linux-hardening
On Wed, Aug 26, 2026 at 2:21 PM Bradley Morgan <brads@mainlining.org> wrote:
>
> > + /* Insert more than UID_GID_MAP_MAX_BASE_EXTENTS (e.g., 10) elements */
> > + for (i = 0; i < 10; i++) {
> > + extent.first = i * 10;
> > + extent.lower_first = i * 100;
>
> Both .first and .lower_first increase monotonically with i. So
> forward[] is already sorted by .first and reverse[] is already sorted
> by .lower_first before sort_idmaps() runs. The sort is a no op on
> this input.
>
> > + /* Verify 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.reverse[i].lower_first, i * 100);
>
> This passes even if sort_idmaps() did nothing. The test cannot tell
> a working sort from a broken one.
>
> To actually verify sorting, insert the extents out of order so that
> sort_idmaps() has to rearrange them. For example:
>
> extent.first = (9 - i) * 10;
> extent.lower_first = (9 - i) * 100;
>
> Then after sort_idmaps(), forward[0].first should be 0, forward[1]
> should be 10, etc. That proves the sort moved things.
>
> Nits:
>
> > + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, map.forward);
>
> Redundant. insert_extent() just allocated it on the 6th call, and
> the previous KUNIT_EXPECT_EQ(test, ret, 0) already confirmed success.
> If you want to keep it, at least put it after the loop where it
> protects the sort, not inside the insertion loop where it tests
> something that just succeeded.
>
> > + KUNIT_EXPECT_EQ(test, ret, 0);
>
> Should be KUNIT_ASSERT_EQ. If insert_extent() fails the test
> continues and the next assertions run on uninitialized data. Same
> for the base test.
>
Billionth times the charm!
I hope I've addressed everyone's concerns. Obviously let me know if I
didn't. :-)
> No tag yet, fix the test data and I'll tag v4.
I added your "Reviewed-by" on "PATCH 1/2", but not this one.
-bw
>
> --- Thanks!
> https://lore.kernel.org/all/EE579805-42F2-4C58-B752-F28779EEB717@grrlz.net/
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/2] userns: Add __counted_by_ptr attribute to struct uid_gid_map
2026-08-23 14:52 ` Oleg Nesterov
@ 2026-08-26 22:05 ` Bill Wendling
0 siblings, 0 replies; 20+ messages in thread
From: Bill Wendling @ 2026-08-26 22:05 UTC (permalink / raw)
To: Oleg Nesterov
Cc: linux-kernel, Kees Cook, Gustavo A. R. Silva, Christian Brauner,
Aleksa Sarai, Jan Kara, Nathan Chancellor, Miguel Ojeda,
Thomas Gleixner, Nicolas Schier, Gary Guo, Thomas Weißschuh,
Alice Ryhl, Douglas Anderson, Anand Moon,
codemender-patching+linux, linux-hardening, Alexey Gladkov
On Sun, Aug 23, 2026 at 7:52 AM Oleg Nesterov <oleg@redhat.com> wrote:
>
> On 08/23, Bill Wendling wrote:
> >
> > The compiler attribute __counted_by_ptr associates a pointer field of a
> > struct with a sibling field within the same struct that specifies the
> > element count of the allocated memory. This enables KASAN and fortified
> > bounds-checking to detect out-of-bounds accesses to the pointer field at
> > runtime.
> >
> > We can add the __counted_by_ptr attribute to the 'forward' and 'reverse'
> > pointer fields of 'struct uid_gid_map', which are counted by
> > 'nr_extents'. Since 'nr_extents' is defined in a sibling anonymous
> > struct inside an anonymous union, the nearest common non-anonymous
> > struct level is 'struct uid_gid_map' itself, which is supported by the
> > compiler.
> >
> > However, doing so has runtime implications. In the original
> > implementation of insert_extent(), elements are written to
> > map->forward[map->nr_extents] before map->nr_extents is incremented:
> >
> > if (map->nr_extents < UID_GID_MAP_MAX_BASE_EXTENTS)
> > dest = &map->extent[map->nr_extents];
> > else
> > dest = &map->forward[map->nr_extents];
> >
> > *dest = *extent;
> > map->nr_extents++;
> >
> > At the time of writing to 'map->forward[map->nr_extents]',
> > map->nr_extents is still 5, but we are accessing index 5 (which is the
> > 6th element). Under __counted_by_ptr(nr_extents), the compiler and
> > KASAN expect the accessed index to be strictly less than
> > map->nr_extents. Therefore, accessing index 5 when the count is 5
> > triggers an out-of-bounds panic/trap at runtime.
> >
> > To resolve this, insert_extent() is refactored to increment
> > map->nr_extents first, and then use map->nr_extents - 1 as the index:
> >
> > 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 - 1];
> >
> > *dest = *extent;
>
> I leave this to you and other reviewers (add Alexey), you can safely
> ignore my nit.
>
> To me
>
> if (map->nr_extents < UID_GID_MAP_MAX_BASE_EXTENTS)
> dest = &map->extent;
> else
> dest = &map->forward;
>
> map->nr_extents++;
> dest[map->nr_extents - 1] = *extent;
>
> looks a bit more clear, but this is minor/subjective.
>
> Either way, I think this needs a short comment to explain why do we
> need to increment ->nr_extents first, then subtract 1. IOW, to explain
> why (say)
>
> dest[map->nr_extents++] = *extent;
>
> would be wrong.
>
Hi Oleg,
I added a comment before the code in question explaining why it's
organized that way. I certainly don't hate your version, but for
simplicity of reviews I just left the code as is.
-bw
> Oleg.
>
> > Assisted-by: Gemini Next
> > Signed-off-by: Bill Wendling <morbo@google.com>
> > ---
> > Cc: Kees Cook <kees@kernel.org>
> > Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org>
> > Cc: Christian Brauner <brauner@kernel.org>
> > Cc: Aleksa Sarai <cyphar@cyphar.com>
> > Cc: Jan Kara <jack@suse.cz>
> > Cc: Nathan Chancellor <nathan@kernel.org>
> > Cc: Miguel Ojeda <ojeda@kernel.org>
> > Cc: Thomas Gleixner <tglx@kernel.org>
> > Cc: Nicolas Schier <nsc@kernel.org>
> > Cc: Gary Guo <gary@garyguo.net>
> > Cc: "Thomas Weißschuh" <thomas.weissschuh@linutronix.de>
> > Cc: Alice Ryhl <aliceryhl@google.com>
> > Cc: Douglas Anderson <dianders@chromium.org>
> > Cc: Anand Moon <linux.amoon@gmail.com>
> > Cc: Oleg Nesterov <oleg@redhat.com>
> > Cc: codemender-patching+linux@google.com
> > Cc: linux-kernel@vger.kernel.org
> > Cc: linux-hardening@vger.kernel.org
> > ---
> > include/linux/user_namespace.h | 4 ++--
> > kernel/user_namespace.c | 8 ++++----
> > 2 files changed, 6 insertions(+), 6 deletions(-)
> >
> > diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
> > index e38d9e60569f..2962256eddf7 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);
> > };
> > };
> > };
> > diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
> > index 0bed462e9b2a..7e5371d8f515 100644
> > --- a/kernel/user_namespace.c
> > +++ b/kernel/user_namespace.c
> > @@ -809,13 +809,13 @@ 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];
> > + 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;
> > }
> >
> > --
> > 2.55.0.860.g4b6b3295ed-goog
> >
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v4 2/2] userns: Add KUnit test suite for uid_gid_map
2026-08-26 22:00 ` [PATCH v4 2/2] userns: Add KUnit test suite for uid_gid_map Bill Wendling
@ 2026-08-26 22:27 ` Bradley Morgan
2026-08-27 13:36 ` Thomas Weißschuh
1 sibling, 0 replies; 20+ messages in thread
From: Bradley Morgan @ 2026-08-26 22:27 UTC (permalink / raw)
To: Bill Wendling
Cc: Thomas Weißschuh, Kees Cook, Gustavo A. R. Silva,
Christian Brauner, Aleksa Sarai, Jan Kara, Nathan Chancellor,
Miguel Ojeda, Thomas Gleixner, Nicolas Schier, Gary Guo,
Alice Ryhl, Douglas Anderson, Anand Moon, Oleg Nesterov,
codemender-patching+linux, linux-kernel, linux-hardening
On 26 August 2026 23:00:35 BST, Bill Wendling <morbo@google.com> 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
Now for all the work s/you/your AI done, I've done the tests and they
passed, and they LGTM
Tested-by: Bradley Morgan <brads@mainlining.org>
Reviewed-by: Bradley Morgan <brads@mainlining.org>
Thanks for your patch mate, (pls slow down tho, we can review oh so quick)
>Signed-off-by: Bill Wendling <morbo@google.com>
>---
>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.
>---
>Cc: Bradley Morgan <brads@mainlining.org>
>Cc: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
>Cc: Kees Cook <kees@kernel.org>
>Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org>
>Cc: Christian Brauner <brauner@kernel.org>
>Cc: Aleksa Sarai <cyphar@cyphar.com>
>Cc: Jan Kara <jack@suse.cz>
>Cc: Nathan Chancellor <nathan@kernel.org>
>Cc: Miguel Ojeda <ojeda@kernel.org>
>Cc: Thomas Gleixner <tglx@kernel.org>
>Cc: Nicolas Schier <nsc@kernel.org>
>Cc: Gary Guo <gary@garyguo.net>
>Cc: "Thomas Weißschuh" <thomas.weissschuh@linutronix.de>
>Cc: Alice Ryhl <aliceryhl@google.com>
>Cc: Douglas Anderson <dianders@chromium.org>
>Cc: Anand Moon <linux.amoon@gmail.com>
>Cc: Oleg Nesterov <oleg@redhat.com>
>Cc: codemender-patching+linux@google.com
>Cc: linux-kernel@vger.kernel.org
>Cc: linux-hardening@vger.kernel.org
>---
> init/Kconfig | 10 ++++
> kernel/.kunitconfig | 3 ++
> kernel/user_namespace.c | 4 ++
> kernel/user_namespace_kunit.c | 92 +++++++++++++++++++++++++++++++++++
> 4 files changed, 109 insertions(+)
> create mode 100644 kernel/.kunitconfig
> create mode 100644 kernel/user_namespace_kunit.c
>
>diff --git a/init/Kconfig b/init/Kconfig
>index f63bf5e05e79..d460344539a5 100644
>--- a/init/Kconfig
>+++ b/init/Kconfig
>@@ -1457,6 +1457,16 @@ config USER_NS
>
> If unsure, say N.
>
>+config USER_NAMESPACE_KUNIT_TEST
>+ bool "Test user namespace map insertion" if !KUNIT_ALL_TESTS
>+ depends on KUNIT=y
>+ 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/.kunitconfig b/kernel/.kunitconfig
>new file mode 100644
>index 000000000000..7314dce05dc2
>--- /dev/null
>+++ b/kernel/.kunitconfig
>@@ -0,0 +1,3 @@
>+CONFIG_KUNIT=y
>+CONFIG_USER_NS=y
>+CONFIG_USER_NAMESPACE_KUNIT_TEST=y
>diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
>index 786dbf0506ca..0e7373085af9 100644
>--- a/kernel/user_namespace.c
>+++ b/kernel/user_namespace.c
>@@ -1417,3 +1417,7 @@ static __init int user_namespaces_init(void)
> return 0;
> }
> subsys_initcall(user_namespaces_init);
>+
>+#if IS_ENABLED(CONFIG_USER_NAMESPACE_KUNIT_TEST)
>+#include "user_namespace_kunit.c"
>+#endif
>diff --git a/kernel/user_namespace_kunit.c b/kernel/user_namespace_kunit.c
>new file mode 100644
>index 000000000000..88467361efdf
>--- /dev/null
>+++ b/kernel/user_namespace_kunit.c
>@@ -0,0 +1,92 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/*
>+ * KUnit test for user namespace map insertion and sorting.
>+ */
>+
>+#include <kunit/test.h>
>+#include <linux/user_namespace.h>
>+
>+static void test_user_ns_map_insert_base(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 (5) 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, i + 1);
>+ 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 test_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 (e.g., 10) elements */
>+ for (i = 0; i < 10; 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, i + 1);
>+
>+ if (i < UID_GID_MAP_MAX_BASE_EXTENTS) {
>+ KUNIT_EXPECT_EQ(test, map.extent[i].first, value * 10);
>+ } else {
>+ KUNIT_EXPECT_EQ(test, map.forward[i].first, value * 10);
>+ KUNIT_EXPECT_EQ(test, map.forward[i].lower_first, value * 100);
>+ KUNIT_EXPECT_EQ(test, map.forward[i].count, 5);
>+ }
>+ }
>+
>+ /* Now sort the map to set up reverse mapping */
>+ ret = sort_idmaps(&map);
>+ KUNIT_EXPECT_EQ(test, ret, 0);
>+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, map.reverse);
>+
>+ /* Verify 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(test_user_ns_map_insert_base),
>+ KUNIT_CASE(test_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);
>
--- Thanks!
https://lore.kernel.org/all/EE579805-42F2-4C58-B752-F28779EEB717@grrlz.net/
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 2/2] userns: Add KUnit test suite for uid_gid_map
2026-08-26 20:40 ` Bill Wendling
@ 2026-08-27 13:25 ` Thomas Weißschuh
0 siblings, 0 replies; 20+ messages in thread
From: Thomas Weißschuh @ 2026-08-27 13:25 UTC (permalink / raw)
To: Bill Wendling
Cc: linux-kernel, Kees Cook, Gustavo A. R. Silva, Christian Brauner,
Aleksa Sarai, Jan Kara, Nathan Chancellor, Miguel Ojeda,
Thomas Gleixner, Nicolas Schier, Gary Guo, Alice Ryhl,
Douglas Anderson, Anand Moon, Oleg Nesterov,
codemender-patching+linux, linux-hardening
On Wed, Aug 26, 2026 at 01:40:03PM -0700, Bill Wendling wrote:
> On Sun, Aug 23, 2026 at 11:13 PM Thomas Weißschuh
> <thomas.weissschuh@linutronix.de> wrote:
> >
> > On Sun, Aug 23, 2026 at 12:51:48PM +0000, Bill Wendling 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.
> >
> > AFAIU patch 1 is supposed to not change any behavior.
> > You could move the unit test to the front to make that clearer
> > and also validate it.
> >
> Patch 1 generates code to assert if a memory address outside of the
> bounds of a pointer is accessed. So it does change the behavior.
It should not change the behavior observed by a well-behaved caller.
And the test only simulates such a well-behaved caller.
> Should I still move the test case?
I think so. At least if you resend the series anyways.
Make sure to also run the test before your other patch is applied.
>
> > > Assisted-by: Gemini Next
> > > Change-Id: If0c2c197a35cd7429cf0d2d6e3b33f0d9f0be66c
> >
> > Change-Id should not be used upstream.
> > See Documentation/dev-tools/checkpatch.rst.
> >
> I had removed it in v2. :-)
Thanks!
(...)
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v4 2/2] userns: Add KUnit test suite for uid_gid_map
2026-08-26 22:00 ` [PATCH v4 2/2] userns: Add KUnit test suite for uid_gid_map Bill Wendling
2026-08-26 22:27 ` Bradley Morgan
@ 2026-08-27 13:36 ` Thomas Weißschuh
2026-08-27 19:27 ` Bill Wendling
1 sibling, 1 reply; 20+ messages in thread
From: Thomas Weißschuh @ 2026-08-27 13:36 UTC (permalink / raw)
To: Bill Wendling
Cc: Bradley Morgan, Kees Cook, Gustavo A. R. Silva, Christian Brauner,
Aleksa Sarai, Jan Kara, Nathan Chancellor, Miguel Ojeda,
Thomas Gleixner, Nicolas Schier, Gary Guo, Alice Ryhl,
Douglas Anderson, Anand Moon, Oleg Nesterov,
codemender-patching+linux, linux-kernel, linux-hardening
On Wed, Aug 26, 2026 at 10:00:35PM +0000, Bill Wendling 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 <morbo@google.com>
> ---
> 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.
> ---
> Cc: Bradley Morgan <brads@mainlining.org>
> Cc: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
> Cc: Kees Cook <kees@kernel.org>
> Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org>
> Cc: Christian Brauner <brauner@kernel.org>
> Cc: Aleksa Sarai <cyphar@cyphar.com>
> Cc: Jan Kara <jack@suse.cz>
> Cc: Nathan Chancellor <nathan@kernel.org>
> Cc: Miguel Ojeda <ojeda@kernel.org>
> Cc: Thomas Gleixner <tglx@kernel.org>
> Cc: Nicolas Schier <nsc@kernel.org>
> Cc: Gary Guo <gary@garyguo.net>
> Cc: "Thomas Weißschuh" <thomas.weissschuh@linutronix.de>
> Cc: Alice Ryhl <aliceryhl@google.com>
> Cc: Douglas Anderson <dianders@chromium.org>
> Cc: Anand Moon <linux.amoon@gmail.com>
> Cc: Oleg Nesterov <oleg@redhat.com>
> Cc: codemender-patching+linux@google.com
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-hardening@vger.kernel.org
> ---
> init/Kconfig | 10 ++++
> kernel/.kunitconfig | 3 ++
> kernel/user_namespace.c | 4 ++
> kernel/user_namespace_kunit.c | 92 +++++++++++++++++++++++++++++++++++
> 4 files changed, 109 insertions(+)
> create mode 100644 kernel/.kunitconfig
> create mode 100644 kernel/user_namespace_kunit.c
>
> diff --git a/init/Kconfig b/init/Kconfig
> index f63bf5e05e79..d460344539a5 100644
> --- a/init/Kconfig
> +++ b/init/Kconfig
> @@ -1457,6 +1457,16 @@ config USER_NS
>
> If unsure, say N.
>
> +config USER_NAMESPACE_KUNIT_TEST
> + bool "Test user namespace map insertion" if !KUNIT_ALL_TESTS
> + depends on KUNIT=y
Urgh.
> + 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/.kunitconfig b/kernel/.kunitconfig
> new file mode 100644
> index 000000000000..7314dce05dc2
> --- /dev/null
> +++ b/kernel/.kunitconfig
> @@ -0,0 +1,3 @@
> +CONFIG_KUNIT=y
> +CONFIG_USER_NS=y
> +CONFIG_USER_NAMESPACE_KUNIT_TEST=y
> diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
> index 786dbf0506ca..0e7373085af9 100644
> --- a/kernel/user_namespace.c
> +++ b/kernel/user_namespace.c
> @@ -1417,3 +1417,7 @@ static __init int user_namespaces_init(void)
> return 0;
> }
> subsys_initcall(user_namespaces_init);
> +
> +#if IS_ENABLED(CONFIG_USER_NAMESPACE_KUNIT_TEST)
> +#include "user_namespace_kunit.c"
> +#endif
> diff --git a/kernel/user_namespace_kunit.c b/kernel/user_namespace_kunit.c
> new file mode 100644
> index 000000000000..88467361efdf
> --- /dev/null
> +++ b/kernel/user_namespace_kunit.c
> @@ -0,0 +1,92 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * KUnit test for user namespace map insertion and sorting.
> + */
> +
> +#include <kunit/test.h>
> +#include <linux/user_namespace.h>
> +
> +static void test_user_ns_map_insert_base(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 (5) 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, i + 1);
> + 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);
> + }
> +}
The extended test below already tests everything the 'base' one does.
Do we need both?
> +
> +static void test_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 (e.g., 10) elements */
When UID_GID_MAP_MAX_BASE_EXTENTS is ever increased, this might not be true anymore.
Add an assertion or make the iteration count dynamic.
> + for (i = 0; i < 10; 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, i + 1);
> +
> + if (i < UID_GID_MAP_MAX_BASE_EXTENTS) {
> + KUNIT_EXPECT_EQ(test, map.extent[i].first, value * 10);
> + } else {
> + KUNIT_EXPECT_EQ(test, map.forward[i].first, value * 10);
> + KUNIT_EXPECT_EQ(test, map.forward[i].lower_first, value * 100);
> + KUNIT_EXPECT_EQ(test, map.forward[i].count, 5);
> + }
> + }
> +
> + /* Now sort the map to set up reverse mapping */
> + ret = sort_idmaps(&map);
> + KUNIT_EXPECT_EQ(test, ret, 0);
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, map.reverse);
> +
> + /* Verify 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);
> + }
Isn't this the same as the original order?
> +
> + kfree(map.forward);
> + kfree(map.reverse);
> +}
(...)
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v4 2/2] userns: Add KUnit test suite for uid_gid_map
2026-08-27 13:36 ` Thomas Weißschuh
@ 2026-08-27 19:27 ` Bill Wendling
0 siblings, 0 replies; 20+ messages in thread
From: Bill Wendling @ 2026-08-27 19:27 UTC (permalink / raw)
To: Thomas Weißschuh
Cc: Bradley Morgan, Kees Cook, Gustavo A. R. Silva, Christian Brauner,
Aleksa Sarai, Jan Kara, Nathan Chancellor, Miguel Ojeda,
Thomas Gleixner, Nicolas Schier, Gary Guo, Alice Ryhl,
Douglas Anderson, Anand Moon, Oleg Nesterov,
codemender-patching+linux, linux-kernel, linux-hardening
On Thu, Aug 27, 2026 at 6:37 AM Thomas Weißschuh
<thomas.weissschuh@linutronix.de> wrote:
>
> On Wed, Aug 26, 2026 at 10:00:35PM +0000, Bill Wendling 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 <morbo@google.com>
> > ---
> > 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.
> > ---
> > Cc: Bradley Morgan <brads@mainlining.org>
> > Cc: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
> > Cc: Kees Cook <kees@kernel.org>
> > Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org>
> > Cc: Christian Brauner <brauner@kernel.org>
> > Cc: Aleksa Sarai <cyphar@cyphar.com>
> > Cc: Jan Kara <jack@suse.cz>
> > Cc: Nathan Chancellor <nathan@kernel.org>
> > Cc: Miguel Ojeda <ojeda@kernel.org>
> > Cc: Thomas Gleixner <tglx@kernel.org>
> > Cc: Nicolas Schier <nsc@kernel.org>
> > Cc: Gary Guo <gary@garyguo.net>
> > Cc: "Thomas Weißschuh" <thomas.weissschuh@linutronix.de>
> > Cc: Alice Ryhl <aliceryhl@google.com>
> > Cc: Douglas Anderson <dianders@chromium.org>
> > Cc: Anand Moon <linux.amoon@gmail.com>
> > Cc: Oleg Nesterov <oleg@redhat.com>
> > Cc: codemender-patching+linux@google.com
> > Cc: linux-kernel@vger.kernel.org
> > Cc: linux-hardening@vger.kernel.org
> > ---
> > init/Kconfig | 10 ++++
> > kernel/.kunitconfig | 3 ++
> > kernel/user_namespace.c | 4 ++
> > kernel/user_namespace_kunit.c | 92 +++++++++++++++++++++++++++++++++++
> > 4 files changed, 109 insertions(+)
> > create mode 100644 kernel/.kunitconfig
> > create mode 100644 kernel/user_namespace_kunit.c
> >
> > diff --git a/init/Kconfig b/init/Kconfig
> > index f63bf5e05e79..d460344539a5 100644
> > --- a/init/Kconfig
> > +++ b/init/Kconfig
> > @@ -1457,6 +1457,16 @@ config USER_NS
> >
> > If unsure, say N.
> >
> > +config USER_NAMESPACE_KUNIT_TEST
> > + bool "Test user namespace map insertion" if !KUNIT_ALL_TESTS
> > + depends on KUNIT=y
>
> Urgh.
>
?? What's wrong? It's identical to the conditional for EXEC_KUNIT_TEST:
config EXEC_KUNIT_TEST
bool "Build execve tests" if !KUNIT_ALL_TESTS
depends on KUNIT=y
default KUNIT_ALL_TESTS
help
This builds the exec KUnit tests, which tests boundary conditions
of various aspects of the exec internals.
> > + 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/.kunitconfig b/kernel/.kunitconfig
> > new file mode 100644
> > index 000000000000..7314dce05dc2
> > --- /dev/null
> > +++ b/kernel/.kunitconfig
> > @@ -0,0 +1,3 @@
> > +CONFIG_KUNIT=y
> > +CONFIG_USER_NS=y
> > +CONFIG_USER_NAMESPACE_KUNIT_TEST=y
> > diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
> > index 786dbf0506ca..0e7373085af9 100644
> > --- a/kernel/user_namespace.c
> > +++ b/kernel/user_namespace.c
> > @@ -1417,3 +1417,7 @@ static __init int user_namespaces_init(void)
> > return 0;
> > }
> > subsys_initcall(user_namespaces_init);
> > +
> > +#if IS_ENABLED(CONFIG_USER_NAMESPACE_KUNIT_TEST)
> > +#include "user_namespace_kunit.c"
> > +#endif
> > diff --git a/kernel/user_namespace_kunit.c b/kernel/user_namespace_kunit.c
> > new file mode 100644
> > index 000000000000..88467361efdf
> > --- /dev/null
> > +++ b/kernel/user_namespace_kunit.c
> > @@ -0,0 +1,92 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * KUnit test for user namespace map insertion and sorting.
> > + */
> > +
> > +#include <kunit/test.h>
> > +#include <linux/user_namespace.h>
> > +
> > +static void test_user_ns_map_insert_base(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 (5) 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, i + 1);
> > + 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);
> > + }
> > +}
>
> The extended test below already tests everything the 'base' one does.
> Do we need both?
>
The one below tests the sorting algorithm.
> > +
> > +static void test_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 (e.g., 10) elements */
>
> When UID_GID_MAP_MAX_BASE_EXTENTS is ever increased, this might not be true anymore.
> Add an assertion or make the iteration count dynamic.
>
Sure, I can make it something like "UID_GID_MAP_MAX_BASE_EXTENTS + 42"
or something. Let me know about the "Urgh" comment above first.
> > + for (i = 0; i < 10; 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, i + 1);
> > +
> > + if (i < UID_GID_MAP_MAX_BASE_EXTENTS) {
> > + KUNIT_EXPECT_EQ(test, map.extent[i].first, value * 10);
> > + } else {
> > + KUNIT_EXPECT_EQ(test, map.forward[i].first, value * 10);
> > + KUNIT_EXPECT_EQ(test, map.forward[i].lower_first, value * 100);
> > + KUNIT_EXPECT_EQ(test, map.forward[i].count, 5);
> > + }
> > + }
> > +
> > + /* Now sort the map to set up reverse mapping */
> > + ret = sort_idmaps(&map);
> > + KUNIT_EXPECT_EQ(test, ret, 0);
> > + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, map.reverse);
> > +
> > + /* Verify 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);
> > + }
>
> Isn't this the same as the original order?
>
No. The original order uses "9 - i" for the base value, so it's not in
sorted order (though it's not exactly random either).
-bw
> > +
> > + kfree(map.forward);
> > + kfree(map.reverse);
> > +}
>
> (...)
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2026-08-27 19:27 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20260823125155.1136740-1-morbo@google.com>
2026-08-23 12:51 ` [PATCH 1/2] userns: Add __counted_by_ptr attribute to struct uid_gid_map Bill Wendling
2026-08-23 13:50 ` Bradley Morgan
2026-08-23 14:52 ` Oleg Nesterov
2026-08-26 22:05 ` Bill Wendling
2026-08-24 19:18 ` Gustavo A. R. Silva
2026-08-23 12:51 ` [PATCH 2/2] userns: Add KUnit test suite for uid_gid_map Bill Wendling
2026-08-23 12:53 ` Bill Wendling
2026-08-26 20:43 ` [PATCH v3 " Bill Wendling
2026-08-26 21:21 ` Bradley Morgan
2026-08-26 22:04 ` Bill Wendling
2026-08-23 13:48 ` [PATCH " Bradley Morgan
2026-08-26 20:37 ` Bill Wendling
2026-08-24 6:13 ` Thomas Weißschuh
2026-08-26 20:40 ` Bill Wendling
2026-08-27 13:25 ` Thomas Weißschuh
2026-08-26 22:00 ` [PATCH v4 1/2] userns: Add __counted_by_ptr attribute to struct uid_gid_map Bill Wendling
2026-08-26 22:00 ` [PATCH v4 2/2] userns: Add KUnit test suite for uid_gid_map Bill Wendling
2026-08-26 22:27 ` Bradley Morgan
2026-08-27 13:36 ` Thomas Weißschuh
2026-08-27 19:27 ` Bill Wendling
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox