1*d9d2a68eSMickaël Salaün // SPDX-License-Identifier: GPL-2.0-only 2*d9d2a68eSMickaël Salaün /* 3*d9d2a68eSMickaël Salaün * Landlock - Unique identification number generator 4*d9d2a68eSMickaël Salaün * 5*d9d2a68eSMickaël Salaün * Copyright © 2024-2025 Microsoft Corporation 6*d9d2a68eSMickaël Salaün */ 7*d9d2a68eSMickaël Salaün 8*d9d2a68eSMickaël Salaün #include <kunit/test.h> 9*d9d2a68eSMickaël Salaün #include <linux/atomic.h> 10*d9d2a68eSMickaël Salaün #include <linux/random.h> 11*d9d2a68eSMickaël Salaün #include <linux/spinlock.h> 12*d9d2a68eSMickaël Salaün 13*d9d2a68eSMickaël Salaün #include "common.h" 14*d9d2a68eSMickaël Salaün #include "id.h" 15*d9d2a68eSMickaël Salaün 16*d9d2a68eSMickaël Salaün #define COUNTER_PRE_INIT 0 17*d9d2a68eSMickaël Salaün 18*d9d2a68eSMickaël Salaün static atomic64_t next_id = ATOMIC64_INIT(COUNTER_PRE_INIT); 19*d9d2a68eSMickaël Salaün 20*d9d2a68eSMickaël Salaün static void __init init_id(atomic64_t *const counter, const u32 random_32bits) 21*d9d2a68eSMickaël Salaün { 22*d9d2a68eSMickaël Salaün u64 init; 23*d9d2a68eSMickaël Salaün 24*d9d2a68eSMickaël Salaün /* 25*d9d2a68eSMickaël Salaün * Ensures sure 64-bit values are always used by user space (or may 26*d9d2a68eSMickaël Salaün * fail with -EOVERFLOW), and makes this testable. 27*d9d2a68eSMickaël Salaün */ 28*d9d2a68eSMickaël Salaün init = 1ULL << 32; 29*d9d2a68eSMickaël Salaün 30*d9d2a68eSMickaël Salaün /* 31*d9d2a68eSMickaël Salaün * Makes a large (2^32) boot-time value to limit ID collision in logs 32*d9d2a68eSMickaël Salaün * from different boots, and to limit info leak about the number of 33*d9d2a68eSMickaël Salaün * initially (relative to the reader) created elements (e.g. domains). 34*d9d2a68eSMickaël Salaün */ 35*d9d2a68eSMickaël Salaün init += random_32bits; 36*d9d2a68eSMickaël Salaün 37*d9d2a68eSMickaël Salaün /* Sets first or ignores. This will be the first ID. */ 38*d9d2a68eSMickaël Salaün atomic64_cmpxchg(counter, COUNTER_PRE_INIT, init); 39*d9d2a68eSMickaël Salaün } 40*d9d2a68eSMickaël Salaün 41*d9d2a68eSMickaël Salaün #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST 42*d9d2a68eSMickaël Salaün 43*d9d2a68eSMickaël Salaün static void __init test_init_min(struct kunit *const test) 44*d9d2a68eSMickaël Salaün { 45*d9d2a68eSMickaël Salaün atomic64_t counter = ATOMIC64_INIT(COUNTER_PRE_INIT); 46*d9d2a68eSMickaël Salaün 47*d9d2a68eSMickaël Salaün init_id(&counter, 0); 48*d9d2a68eSMickaël Salaün KUNIT_EXPECT_EQ(test, atomic64_read(&counter), 1ULL + U32_MAX); 49*d9d2a68eSMickaël Salaün } 50*d9d2a68eSMickaël Salaün 51*d9d2a68eSMickaël Salaün static void __init test_init_max(struct kunit *const test) 52*d9d2a68eSMickaël Salaün { 53*d9d2a68eSMickaël Salaün atomic64_t counter = ATOMIC64_INIT(COUNTER_PRE_INIT); 54*d9d2a68eSMickaël Salaün 55*d9d2a68eSMickaël Salaün init_id(&counter, ~0); 56*d9d2a68eSMickaël Salaün KUNIT_EXPECT_EQ(test, atomic64_read(&counter), 1 + (2ULL * U32_MAX)); 57*d9d2a68eSMickaël Salaün } 58*d9d2a68eSMickaël Salaün 59*d9d2a68eSMickaël Salaün static void __init test_init_once(struct kunit *const test) 60*d9d2a68eSMickaël Salaün { 61*d9d2a68eSMickaël Salaün const u64 first_init = 1ULL + U32_MAX; 62*d9d2a68eSMickaël Salaün atomic64_t counter = ATOMIC64_INIT(COUNTER_PRE_INIT); 63*d9d2a68eSMickaël Salaün 64*d9d2a68eSMickaël Salaün init_id(&counter, 0); 65*d9d2a68eSMickaël Salaün KUNIT_EXPECT_EQ(test, atomic64_read(&counter), first_init); 66*d9d2a68eSMickaël Salaün 67*d9d2a68eSMickaël Salaün init_id(&counter, ~0); 68*d9d2a68eSMickaël Salaün KUNIT_EXPECT_EQ_MSG( 69*d9d2a68eSMickaël Salaün test, atomic64_read(&counter), first_init, 70*d9d2a68eSMickaël Salaün "Should still have the same value after the subsequent init_id()"); 71*d9d2a68eSMickaël Salaün } 72*d9d2a68eSMickaël Salaün 73*d9d2a68eSMickaël Salaün #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ 74*d9d2a68eSMickaël Salaün 75*d9d2a68eSMickaël Salaün void __init landlock_init_id(void) 76*d9d2a68eSMickaël Salaün { 77*d9d2a68eSMickaël Salaün return init_id(&next_id, get_random_u32()); 78*d9d2a68eSMickaël Salaün } 79*d9d2a68eSMickaël Salaün 80*d9d2a68eSMickaël Salaün /* 81*d9d2a68eSMickaël Salaün * It's not worth it to try to hide the monotonic counter because it can still 82*d9d2a68eSMickaël Salaün * be inferred (with N counter ranges), and if we are allowed to read the inode 83*d9d2a68eSMickaël Salaün * number we should also be allowed to read the time creation anyway, and it 84*d9d2a68eSMickaël Salaün * can be handy to store and sort domain IDs for user space. 85*d9d2a68eSMickaël Salaün * 86*d9d2a68eSMickaël Salaün * Returns the value of next_id and increment it to let some space for the next 87*d9d2a68eSMickaël Salaün * one. 88*d9d2a68eSMickaël Salaün */ 89*d9d2a68eSMickaël Salaün static u64 get_id_range(size_t number_of_ids, atomic64_t *const counter, 90*d9d2a68eSMickaël Salaün u8 random_4bits) 91*d9d2a68eSMickaël Salaün { 92*d9d2a68eSMickaël Salaün u64 id, step; 93*d9d2a68eSMickaël Salaün 94*d9d2a68eSMickaël Salaün /* 95*d9d2a68eSMickaël Salaün * We should return at least 1 ID, and we may need a set of consecutive 96*d9d2a68eSMickaël Salaün * ones (e.g. to generate a set of inodes). 97*d9d2a68eSMickaël Salaün */ 98*d9d2a68eSMickaël Salaün if (WARN_ON_ONCE(number_of_ids <= 0)) 99*d9d2a68eSMickaël Salaün number_of_ids = 1; 100*d9d2a68eSMickaël Salaün 101*d9d2a68eSMickaël Salaün /* 102*d9d2a68eSMickaël Salaün * Blurs the next ID guess with 1/16 ratio. We get 2^(64 - 4) - 103*d9d2a68eSMickaël Salaün * (2 * 2^32), so a bit less than 2^60 available IDs, which should be 104*d9d2a68eSMickaël Salaün * much more than enough considering the number of CPU cycles required 105*d9d2a68eSMickaël Salaün * to get a new ID (e.g. a full landlock_restrict_self() call), and the 106*d9d2a68eSMickaël Salaün * cost of draining all available IDs during the system's uptime. 107*d9d2a68eSMickaël Salaün */ 108*d9d2a68eSMickaël Salaün random_4bits = random_4bits % (1 << 4); 109*d9d2a68eSMickaël Salaün step = number_of_ids + random_4bits; 110*d9d2a68eSMickaël Salaün 111*d9d2a68eSMickaël Salaün /* It is safe to cast a signed atomic to an unsigned value. */ 112*d9d2a68eSMickaël Salaün id = atomic64_fetch_add(step, counter); 113*d9d2a68eSMickaël Salaün 114*d9d2a68eSMickaël Salaün /* Warns if landlock_init_id() was not called. */ 115*d9d2a68eSMickaël Salaün WARN_ON_ONCE(id == COUNTER_PRE_INIT); 116*d9d2a68eSMickaël Salaün return id; 117*d9d2a68eSMickaël Salaün } 118*d9d2a68eSMickaël Salaün 119*d9d2a68eSMickaël Salaün #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST 120*d9d2a68eSMickaël Salaün 121*d9d2a68eSMickaël Salaün static void test_range1_rand0(struct kunit *const test) 122*d9d2a68eSMickaël Salaün { 123*d9d2a68eSMickaël Salaün atomic64_t counter; 124*d9d2a68eSMickaël Salaün u64 init; 125*d9d2a68eSMickaël Salaün 126*d9d2a68eSMickaël Salaün init = get_random_u32(); 127*d9d2a68eSMickaël Salaün atomic64_set(&counter, init); 128*d9d2a68eSMickaël Salaün KUNIT_EXPECT_EQ(test, get_id_range(1, &counter, 0), init); 129*d9d2a68eSMickaël Salaün KUNIT_EXPECT_EQ( 130*d9d2a68eSMickaël Salaün test, get_id_range(get_random_u8(), &counter, get_random_u8()), 131*d9d2a68eSMickaël Salaün init + 1); 132*d9d2a68eSMickaël Salaün } 133*d9d2a68eSMickaël Salaün 134*d9d2a68eSMickaël Salaün static void test_range1_rand1(struct kunit *const test) 135*d9d2a68eSMickaël Salaün { 136*d9d2a68eSMickaël Salaün atomic64_t counter; 137*d9d2a68eSMickaël Salaün u64 init; 138*d9d2a68eSMickaël Salaün 139*d9d2a68eSMickaël Salaün init = get_random_u32(); 140*d9d2a68eSMickaël Salaün atomic64_set(&counter, init); 141*d9d2a68eSMickaël Salaün KUNIT_EXPECT_EQ(test, get_id_range(1, &counter, 1), init); 142*d9d2a68eSMickaël Salaün KUNIT_EXPECT_EQ( 143*d9d2a68eSMickaël Salaün test, get_id_range(get_random_u8(), &counter, get_random_u8()), 144*d9d2a68eSMickaël Salaün init + 2); 145*d9d2a68eSMickaël Salaün } 146*d9d2a68eSMickaël Salaün 147*d9d2a68eSMickaël Salaün static void test_range1_rand16(struct kunit *const test) 148*d9d2a68eSMickaël Salaün { 149*d9d2a68eSMickaël Salaün atomic64_t counter; 150*d9d2a68eSMickaël Salaün u64 init; 151*d9d2a68eSMickaël Salaün 152*d9d2a68eSMickaël Salaün init = get_random_u32(); 153*d9d2a68eSMickaël Salaün atomic64_set(&counter, init); 154*d9d2a68eSMickaël Salaün KUNIT_EXPECT_EQ(test, get_id_range(1, &counter, 16), init); 155*d9d2a68eSMickaël Salaün KUNIT_EXPECT_EQ( 156*d9d2a68eSMickaël Salaün test, get_id_range(get_random_u8(), &counter, get_random_u8()), 157*d9d2a68eSMickaël Salaün init + 1); 158*d9d2a68eSMickaël Salaün } 159*d9d2a68eSMickaël Salaün 160*d9d2a68eSMickaël Salaün static void test_range2_rand0(struct kunit *const test) 161*d9d2a68eSMickaël Salaün { 162*d9d2a68eSMickaël Salaün atomic64_t counter; 163*d9d2a68eSMickaël Salaün u64 init; 164*d9d2a68eSMickaël Salaün 165*d9d2a68eSMickaël Salaün init = get_random_u32(); 166*d9d2a68eSMickaël Salaün atomic64_set(&counter, init); 167*d9d2a68eSMickaël Salaün KUNIT_EXPECT_EQ(test, get_id_range(2, &counter, 0), init); 168*d9d2a68eSMickaël Salaün KUNIT_EXPECT_EQ( 169*d9d2a68eSMickaël Salaün test, get_id_range(get_random_u8(), &counter, get_random_u8()), 170*d9d2a68eSMickaël Salaün init + 2); 171*d9d2a68eSMickaël Salaün } 172*d9d2a68eSMickaël Salaün 173*d9d2a68eSMickaël Salaün static void test_range2_rand1(struct kunit *const test) 174*d9d2a68eSMickaël Salaün { 175*d9d2a68eSMickaël Salaün atomic64_t counter; 176*d9d2a68eSMickaël Salaün u64 init; 177*d9d2a68eSMickaël Salaün 178*d9d2a68eSMickaël Salaün init = get_random_u32(); 179*d9d2a68eSMickaël Salaün atomic64_set(&counter, init); 180*d9d2a68eSMickaël Salaün KUNIT_EXPECT_EQ(test, get_id_range(2, &counter, 1), init); 181*d9d2a68eSMickaël Salaün KUNIT_EXPECT_EQ( 182*d9d2a68eSMickaël Salaün test, get_id_range(get_random_u8(), &counter, get_random_u8()), 183*d9d2a68eSMickaël Salaün init + 3); 184*d9d2a68eSMickaël Salaün } 185*d9d2a68eSMickaël Salaün 186*d9d2a68eSMickaël Salaün static void test_range2_rand2(struct kunit *const test) 187*d9d2a68eSMickaël Salaün { 188*d9d2a68eSMickaël Salaün atomic64_t counter; 189*d9d2a68eSMickaël Salaün u64 init; 190*d9d2a68eSMickaël Salaün 191*d9d2a68eSMickaël Salaün init = get_random_u32(); 192*d9d2a68eSMickaël Salaün atomic64_set(&counter, init); 193*d9d2a68eSMickaël Salaün KUNIT_EXPECT_EQ(test, get_id_range(2, &counter, 2), init); 194*d9d2a68eSMickaël Salaün KUNIT_EXPECT_EQ( 195*d9d2a68eSMickaël Salaün test, get_id_range(get_random_u8(), &counter, get_random_u8()), 196*d9d2a68eSMickaël Salaün init + 4); 197*d9d2a68eSMickaël Salaün } 198*d9d2a68eSMickaël Salaün 199*d9d2a68eSMickaël Salaün static void test_range2_rand16(struct kunit *const test) 200*d9d2a68eSMickaël Salaün { 201*d9d2a68eSMickaël Salaün atomic64_t counter; 202*d9d2a68eSMickaël Salaün u64 init; 203*d9d2a68eSMickaël Salaün 204*d9d2a68eSMickaël Salaün init = get_random_u32(); 205*d9d2a68eSMickaël Salaün atomic64_set(&counter, init); 206*d9d2a68eSMickaël Salaün KUNIT_EXPECT_EQ(test, get_id_range(2, &counter, 16), init); 207*d9d2a68eSMickaël Salaün KUNIT_EXPECT_EQ( 208*d9d2a68eSMickaël Salaün test, get_id_range(get_random_u8(), &counter, get_random_u8()), 209*d9d2a68eSMickaël Salaün init + 2); 210*d9d2a68eSMickaël Salaün } 211*d9d2a68eSMickaël Salaün 212*d9d2a68eSMickaël Salaün #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ 213*d9d2a68eSMickaël Salaün 214*d9d2a68eSMickaël Salaün /** 215*d9d2a68eSMickaël Salaün * landlock_get_id_range - Get a range of unique IDs 216*d9d2a68eSMickaël Salaün * 217*d9d2a68eSMickaël Salaün * @number_of_ids: Number of IDs to hold. Must be greater than one. 218*d9d2a68eSMickaël Salaün * 219*d9d2a68eSMickaël Salaün * Returns: The first ID in the range. 220*d9d2a68eSMickaël Salaün */ 221*d9d2a68eSMickaël Salaün u64 landlock_get_id_range(size_t number_of_ids) 222*d9d2a68eSMickaël Salaün { 223*d9d2a68eSMickaël Salaün return get_id_range(number_of_ids, &next_id, get_random_u8()); 224*d9d2a68eSMickaël Salaün } 225*d9d2a68eSMickaël Salaün 226*d9d2a68eSMickaël Salaün #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST 227*d9d2a68eSMickaël Salaün 228*d9d2a68eSMickaël Salaün static struct kunit_case __refdata test_cases[] = { 229*d9d2a68eSMickaël Salaün /* clang-format off */ 230*d9d2a68eSMickaël Salaün KUNIT_CASE(test_init_min), 231*d9d2a68eSMickaël Salaün KUNIT_CASE(test_init_max), 232*d9d2a68eSMickaël Salaün KUNIT_CASE(test_init_once), 233*d9d2a68eSMickaël Salaün KUNIT_CASE(test_range1_rand0), 234*d9d2a68eSMickaël Salaün KUNIT_CASE(test_range1_rand1), 235*d9d2a68eSMickaël Salaün KUNIT_CASE(test_range1_rand16), 236*d9d2a68eSMickaël Salaün KUNIT_CASE(test_range2_rand0), 237*d9d2a68eSMickaël Salaün KUNIT_CASE(test_range2_rand1), 238*d9d2a68eSMickaël Salaün KUNIT_CASE(test_range2_rand2), 239*d9d2a68eSMickaël Salaün KUNIT_CASE(test_range2_rand16), 240*d9d2a68eSMickaël Salaün {} 241*d9d2a68eSMickaël Salaün /* clang-format on */ 242*d9d2a68eSMickaël Salaün }; 243*d9d2a68eSMickaël Salaün 244*d9d2a68eSMickaël Salaün static struct kunit_suite test_suite = { 245*d9d2a68eSMickaël Salaün .name = "landlock_id", 246*d9d2a68eSMickaël Salaün .test_cases = test_cases, 247*d9d2a68eSMickaël Salaün }; 248*d9d2a68eSMickaël Salaün 249*d9d2a68eSMickaël Salaün kunit_test_init_section_suite(test_suite); 250*d9d2a68eSMickaël Salaün 251*d9d2a68eSMickaël Salaün #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ 252