Skip to content

Commit 6179d7a

Browse files
committed
Merge tag 'caps-pr-20260213' of git://git.kernel.org/pub/scm/linux/kernel/git/sergeh/linux
Pull capabilities updates from Serge Hallyn: - add KUnit tests for some core capabilities helpers - avoid emitting IPC audit messages when there's not actually a permission being denied * tag 'caps-pr-20260213' of git://git.kernel.org/pub/scm/linux/kernel/git/sergeh/linux: ipc: don't audit capability check in ipc_permissions() security: Add KUnit tests for kuid_root_in_ns and vfsuid_root_in_currentns
2 parents 986d555 + 0715881 commit 6179d7a

4 files changed

Lines changed: 310 additions & 0 deletions

File tree

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5734,6 +5734,7 @@ F: include/trace/events/capability.h
57345734
F: include/uapi/linux/capability.h
57355735
F: kernel/capability.c
57365736
F: security/commoncap.c
5737+
F: security/commoncap_test.c
57375738

57385739
CAPELLA MICROSYSTEMS LIGHT SENSOR DRIVER
57395740
M: Kevin Tsai <ktsai@capellamicro.com>

security/Kconfig

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,23 @@ config LSM
284284

285285
If unsure, leave this as the default.
286286

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+
287304
source "security/Kconfig.hardening"
288305

289306
endmenu

security/commoncap.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,3 +1521,7 @@ DEFINE_LSM(capability) = {
15211521
};
15221522

15231523
#endif /* CONFIG_SECURITY */
1524+
1525+
#ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST
1526+
#include "commoncap_test.c"
1527+
#endif

security/commoncap_test.c

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
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 */

0 commit comments

Comments
 (0)