Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

security: Add KUnit tests for kuid_root_in_ns and vfsuid_root_in_currentns

Add comprehensive KUnit tests for the namespace-related capability
functions that Serge Hallyn refactored in commit 9891d2f79a9f
("Clarify the rootid_owns_currentns").

The tests verify:
- Basic functionality: UID 0 in init namespace, invalid vfsuid,
non-zero UIDs
- Actual namespace traversal: Creating user namespaces with different
UID mappings where uid 0 maps to different kuids (e.g., 1000, 2000,
3000)
- Hierarchy traversal: Testing multiple nested namespaces to verify
correct namespace hierarchy traversal

This addresses the feedback to "test the actual functionality" by
creating real user namespaces with different values for the
namespace's uid 0, rather than just basic input validation.

The test file is included at the end of commoncap.c when
CONFIG_SECURITY_COMMONCAP_KUNIT_TEST is enabled, following the
standard kernel pattern (e.g., scsi_lib.c, ext4/mballoc.c). This
allows tests to access static functions in the same compilation unit
without modifying production code based on test configuration.

The tests require CONFIG_USER_NS to be enabled since they rely on user
namespace mapping functionality. The Kconfig dependency ensures the
tests only build when this requirement is met.

All 7 tests pass:
- test_vfsuid_root_in_currentns_init_ns
- test_vfsuid_root_in_currentns_invalid
- test_vfsuid_root_in_currentns_nonzero
- test_kuid_root_in_ns_init_ns_uid0
- test_kuid_root_in_ns_init_ns_nonzero
- test_kuid_root_in_ns_with_mapping
- test_kuid_root_in_ns_with_different_mappings

Updated MAINTAINER capabilities to include commoncap test

Signed-off-by: Ryan Foster <foster.ryan.r@gmail.com>
Signed-off-by: Serge Hallyn <sergeh@kernel.org>

authored by

Ryan Foster and committed by
Serge Hallyn
24e9b431 9ace4753

+310
+1
MAINTAINERS
··· 5682 5682 F: include/uapi/linux/capability.h 5683 5683 F: kernel/capability.c 5684 5684 F: security/commoncap.c 5685 + F: security/commoncap_test.c 5685 5686 5686 5687 CAPELLA MICROSYSTEMS LIGHT SENSOR DRIVER 5687 5688 M: Kevin Tsai <ktsai@capellamicro.com>
+17
security/Kconfig
··· 284 284 285 285 If unsure, leave this as the default. 286 286 287 + config SECURITY_COMMONCAP_KUNIT_TEST 288 + bool "Build KUnit tests for commoncap" if !KUNIT_ALL_TESTS 289 + depends on KUNIT=y && USER_NS 290 + default KUNIT_ALL_TESTS 291 + help 292 + This builds the commoncap KUnit tests. 293 + 294 + KUnit tests run during boot and output the results to the debug log 295 + in TAP format (https://testanything.org/). Only useful for kernel devs 296 + running KUnit test harness and are not for inclusion into a 297 + production build. 298 + 299 + For more information on KUnit and unit tests in general please refer 300 + to the KUnit documentation in Documentation/dev-tools/kunit/. 301 + 302 + If unsure, say N. 303 + 287 304 source "security/Kconfig.hardening" 288 305 289 306 endmenu
+4
security/commoncap.c
··· 1521 1521 }; 1522 1522 1523 1523 #endif /* CONFIG_SECURITY */ 1524 + 1525 + #ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST 1526 + #include "commoncap_test.c" 1527 + #endif
+288
security/commoncap_test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * KUnit tests for commoncap.c security functions 4 + * 5 + * Tests for security-critical functions in the capability subsystem, 6 + * particularly namespace-related capability checks. 7 + */ 8 + 9 + #include <kunit/test.h> 10 + #include <linux/user_namespace.h> 11 + #include <linux/uidgid.h> 12 + #include <linux/cred.h> 13 + #include <linux/mnt_idmapping.h> 14 + #include <linux/module.h> 15 + #include <linux/slab.h> 16 + #include <linux/refcount.h> 17 + 18 + #ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST 19 + 20 + /* Functions are static in commoncap.c, but we can call them since we're 21 + * included in the same compilation unit when tests are enabled. 22 + */ 23 + 24 + /** 25 + * test_vfsuid_root_in_currentns_init_ns - Test vfsuid_root_in_currentns with init ns 26 + * 27 + * Verifies that UID 0 in the init namespace correctly owns the current 28 + * namespace when running in init_user_ns. 29 + * 30 + * @test: KUnit test context 31 + */ 32 + static void test_vfsuid_root_in_currentns_init_ns(struct kunit *test) 33 + { 34 + vfsuid_t vfsuid; 35 + kuid_t kuid; 36 + 37 + /* Create UID 0 in init namespace */ 38 + kuid = KUIDT_INIT(0); 39 + vfsuid = VFSUIDT_INIT(kuid); 40 + 41 + /* In init namespace, UID 0 should own current namespace */ 42 + KUNIT_EXPECT_TRUE(test, vfsuid_root_in_currentns(vfsuid)); 43 + } 44 + 45 + /** 46 + * test_vfsuid_root_in_currentns_invalid - Test vfsuid_root_in_currentns with invalid vfsuid 47 + * 48 + * Verifies that an invalid vfsuid correctly returns false. 49 + * 50 + * @test: KUnit test context 51 + */ 52 + static void test_vfsuid_root_in_currentns_invalid(struct kunit *test) 53 + { 54 + vfsuid_t invalid_vfsuid; 55 + 56 + /* Use the predefined invalid vfsuid */ 57 + invalid_vfsuid = INVALID_VFSUID; 58 + 59 + /* Invalid vfsuid should return false */ 60 + KUNIT_EXPECT_FALSE(test, vfsuid_root_in_currentns(invalid_vfsuid)); 61 + } 62 + 63 + /** 64 + * test_vfsuid_root_in_currentns_nonzero - Test vfsuid_root_in_currentns with non-zero UID 65 + * 66 + * Verifies that a non-zero UID correctly returns false. 67 + * 68 + * @test: KUnit test context 69 + */ 70 + static void test_vfsuid_root_in_currentns_nonzero(struct kunit *test) 71 + { 72 + vfsuid_t vfsuid; 73 + kuid_t kuid; 74 + 75 + /* Create a non-zero UID */ 76 + kuid = KUIDT_INIT(1000); 77 + vfsuid = VFSUIDT_INIT(kuid); 78 + 79 + /* Non-zero UID should return false */ 80 + KUNIT_EXPECT_FALSE(test, vfsuid_root_in_currentns(vfsuid)); 81 + } 82 + 83 + /** 84 + * test_kuid_root_in_ns_init_ns_uid0 - Test kuid_root_in_ns with init namespace and UID 0 85 + * 86 + * Verifies that kuid_root_in_ns correctly identifies UID 0 in init namespace. 87 + * This tests the core namespace traversal logic. In init namespace, UID 0 88 + * maps to itself, so it should own the namespace. 89 + * 90 + * @test: KUnit test context 91 + */ 92 + static void test_kuid_root_in_ns_init_ns_uid0(struct kunit *test) 93 + { 94 + kuid_t kuid; 95 + struct user_namespace *init_ns; 96 + 97 + kuid = KUIDT_INIT(0); 98 + init_ns = &init_user_ns; 99 + 100 + /* UID 0 should own init namespace */ 101 + KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(kuid, init_ns)); 102 + } 103 + 104 + /** 105 + * test_kuid_root_in_ns_init_ns_nonzero - Test kuid_root_in_ns with init namespace and non-zero UID 106 + * 107 + * Verifies that kuid_root_in_ns correctly rejects non-zero UIDs in init namespace. 108 + * Only UID 0 should own a namespace. 109 + * 110 + * @test: KUnit test context 111 + */ 112 + static void test_kuid_root_in_ns_init_ns_nonzero(struct kunit *test) 113 + { 114 + kuid_t kuid; 115 + struct user_namespace *init_ns; 116 + 117 + kuid = KUIDT_INIT(1000); 118 + init_ns = &init_user_ns; 119 + 120 + /* Non-zero UID should not own namespace */ 121 + KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(kuid, init_ns)); 122 + } 123 + 124 + /** 125 + * create_test_user_ns_with_mapping - Create a mock user namespace with UID mapping 126 + * 127 + * Creates a minimal user namespace structure for testing where uid 0 in the 128 + * namespace maps to a specific kuid in the parent namespace. 129 + * 130 + * @test: KUnit test context 131 + * @parent_ns: Parent namespace (typically init_user_ns) 132 + * @mapped_kuid: The kuid that uid 0 in this namespace maps to in parent 133 + * 134 + * Returns: Pointer to allocated namespace, or NULL on failure 135 + */ 136 + static struct user_namespace *create_test_user_ns_with_mapping(struct kunit *test, 137 + struct user_namespace *parent_ns, 138 + kuid_t mapped_kuid) 139 + { 140 + struct user_namespace *ns; 141 + struct uid_gid_extent extent; 142 + 143 + /* Allocate a test namespace - use kzalloc to zero all fields */ 144 + ns = kunit_kzalloc(test, sizeof(*ns), GFP_KERNEL); 145 + if (!ns) 146 + return NULL; 147 + 148 + /* Initialize basic namespace structure fields */ 149 + ns->parent = parent_ns; 150 + ns->level = parent_ns ? parent_ns->level + 1 : 0; 151 + ns->owner = mapped_kuid; 152 + ns->group = KGIDT_INIT(0); 153 + 154 + /* Initialize ns_common structure */ 155 + refcount_set(&ns->ns.__ns_ref, 1); 156 + ns->ns.inum = 0; /* Mock inum */ 157 + 158 + /* Set up uid mapping: uid 0 in this namespace maps to mapped_kuid in parent 159 + * Format: first (uid in ns) : lower_first (kuid in parent) : count 160 + * So: uid 0 in ns -> kuid mapped_kuid in parent 161 + * This means from_kuid(ns, mapped_kuid) returns 0 162 + */ 163 + extent.first = 0; /* uid 0 in this namespace */ 164 + extent.lower_first = __kuid_val(mapped_kuid); /* maps to this kuid in parent */ 165 + extent.count = 1; 166 + 167 + ns->uid_map.extent[0] = extent; 168 + ns->uid_map.nr_extents = 1; 169 + 170 + /* Set up gid mapping: gid 0 maps to gid 0 in parent (simplified) */ 171 + extent.first = 0; 172 + extent.lower_first = 0; 173 + extent.count = 1; 174 + 175 + ns->gid_map.extent[0] = extent; 176 + ns->gid_map.nr_extents = 1; 177 + 178 + return ns; 179 + } 180 + 181 + /** 182 + * test_kuid_root_in_ns_with_mapping - Test kuid_root_in_ns with namespace where uid 0 183 + * maps to different kuid 184 + * 185 + * Creates a user namespace where uid 0 maps to kuid 1000 in the parent namespace. 186 + * Verifies that kuid_root_in_ns correctly identifies kuid 1000 as owning the namespace. 187 + * 188 + * Note: kuid_root_in_ns walks up the namespace hierarchy, so it checks the current 189 + * namespace first, then parent, then parent's parent, etc. So: 190 + * - kuid 1000 owns test_ns because from_kuid(test_ns, 1000) == 0 191 + * - kuid 0 also owns test_ns because from_kuid(init_user_ns, 0) == 0 192 + * (checked in parent) 193 + * 194 + * This tests the actual functionality as requested: creating namespaces with 195 + * different values for the namespace's uid 0. 196 + * 197 + * @test: KUnit test context 198 + */ 199 + static void test_kuid_root_in_ns_with_mapping(struct kunit *test) 200 + { 201 + struct user_namespace *test_ns; 202 + struct user_namespace *parent_ns; 203 + kuid_t mapped_kuid, other_kuid; 204 + 205 + parent_ns = &init_user_ns; 206 + mapped_kuid = KUIDT_INIT(1000); 207 + other_kuid = KUIDT_INIT(2000); 208 + 209 + test_ns = create_test_user_ns_with_mapping(test, parent_ns, mapped_kuid); 210 + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, test_ns); 211 + 212 + /* kuid 1000 should own test_ns because it maps to uid 0 in test_ns */ 213 + KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(mapped_kuid, test_ns)); 214 + 215 + /* kuid 0 should also own test_ns (checked via parent init_user_ns) */ 216 + KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), test_ns)); 217 + 218 + /* Other kuids should not own test_ns */ 219 + KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(other_kuid, test_ns)); 220 + KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(500), test_ns)); 221 + } 222 + 223 + /** 224 + * test_kuid_root_in_ns_with_different_mappings - Test with multiple namespaces 225 + * 226 + * Creates multiple user namespaces with different UID mappings to verify 227 + * that kuid_root_in_ns correctly distinguishes between namespaces. 228 + * 229 + * Each namespace maps uid 0 to a different kuid, and we verify that each 230 + * kuid only owns its corresponding namespace (plus kuid 0 owns all via 231 + * init_user_ns parent). 232 + * 233 + * @test: KUnit test context 234 + */ 235 + static void test_kuid_root_in_ns_with_different_mappings(struct kunit *test) 236 + { 237 + struct user_namespace *ns1, *ns2, *ns3; 238 + 239 + /* Create three independent namespaces, each mapping uid 0 to different kuids */ 240 + ns1 = create_test_user_ns_with_mapping(test, &init_user_ns, KUIDT_INIT(1000)); 241 + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns1); 242 + 243 + ns2 = create_test_user_ns_with_mapping(test, &init_user_ns, KUIDT_INIT(2000)); 244 + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns2); 245 + 246 + ns3 = create_test_user_ns_with_mapping(test, &init_user_ns, KUIDT_INIT(3000)); 247 + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns3); 248 + 249 + /* Test ns1: kuid 1000 owns it, kuid 0 owns it (via parent), others do not */ 250 + KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(1000), ns1)); 251 + KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), ns1)); 252 + KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(2000), ns1)); 253 + KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(3000), ns1)); 254 + 255 + /* Test ns2: kuid 2000 owns it, kuid 0 owns it (via parent), others do not */ 256 + KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(2000), ns2)); 257 + KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), ns2)); 258 + KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(1000), ns2)); 259 + KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(3000), ns2)); 260 + 261 + /* Test ns3: kuid 3000 owns it, kuid 0 owns it (via parent), others do not */ 262 + KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(3000), ns3)); 263 + KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), ns3)); 264 + KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(1000), ns3)); 265 + KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(2000), ns3)); 266 + } 267 + 268 + static struct kunit_case commoncap_test_cases[] = { 269 + KUNIT_CASE(test_vfsuid_root_in_currentns_init_ns), 270 + KUNIT_CASE(test_vfsuid_root_in_currentns_invalid), 271 + KUNIT_CASE(test_vfsuid_root_in_currentns_nonzero), 272 + KUNIT_CASE(test_kuid_root_in_ns_init_ns_uid0), 273 + KUNIT_CASE(test_kuid_root_in_ns_init_ns_nonzero), 274 + KUNIT_CASE(test_kuid_root_in_ns_with_mapping), 275 + KUNIT_CASE(test_kuid_root_in_ns_with_different_mappings), 276 + {} 277 + }; 278 + 279 + static struct kunit_suite commoncap_test_suite = { 280 + .name = "commoncap", 281 + .test_cases = commoncap_test_cases, 282 + }; 283 + 284 + kunit_test_suite(commoncap_test_suite); 285 + 286 + MODULE_LICENSE("GPL"); 287 + 288 + #endif /* CONFIG_SECURITY_COMMONCAP_KUNIT_TEST */