1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Landlock tests - Enforcing the same restrictions across multiple threads 4 * 5 * Copyright © 2025 Günther Noack <gnoack3000@gmail.com> 6 */ 7 8 #define _GNU_SOURCE 9 #include <pthread.h> 10 #include <sys/prctl.h> 11 #include <linux/landlock.h> 12 13 #include "common.h" 14 15 /* create_ruleset - Create a simple ruleset FD common to all tests */ 16 static int create_ruleset(struct __test_metadata *const _metadata) 17 { 18 struct landlock_ruleset_attr ruleset_attr = { 19 .handled_access_fs = (LANDLOCK_ACCESS_FS_WRITE_FILE | 20 LANDLOCK_ACCESS_FS_TRUNCATE), 21 }; 22 const int ruleset_fd = 23 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 24 25 ASSERT_LE(0, ruleset_fd) 26 { 27 TH_LOG("landlock_create_ruleset: %s", strerror(errno)); 28 } 29 return ruleset_fd; 30 } 31 32 TEST(single_threaded_success) 33 { 34 const int ruleset_fd = create_ruleset(_metadata); 35 36 disable_caps(_metadata); 37 38 ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 39 ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 40 LANDLOCK_RESTRICT_SELF_TSYNC)); 41 42 EXPECT_EQ(0, close(ruleset_fd)); 43 } 44 45 static void store_no_new_privs(void *data) 46 { 47 bool *nnp = data; 48 49 if (!nnp) 50 return; 51 *nnp = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0); 52 } 53 54 static void *idle(void *data) 55 { 56 pthread_cleanup_push(store_no_new_privs, data); 57 58 while (true) 59 sleep(1); 60 61 pthread_cleanup_pop(1); 62 } 63 64 TEST(multi_threaded_success) 65 { 66 pthread_t t1, t2; 67 bool no_new_privs1, no_new_privs2; 68 const int ruleset_fd = create_ruleset(_metadata); 69 70 disable_caps(_metadata); 71 72 ASSERT_EQ(0, pthread_create(&t1, NULL, idle, &no_new_privs1)); 73 ASSERT_EQ(0, pthread_create(&t2, NULL, idle, &no_new_privs2)); 74 75 ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 76 77 EXPECT_EQ(0, landlock_restrict_self(ruleset_fd, 78 LANDLOCK_RESTRICT_SELF_TSYNC)); 79 80 ASSERT_EQ(0, pthread_cancel(t1)); 81 ASSERT_EQ(0, pthread_cancel(t2)); 82 ASSERT_EQ(0, pthread_join(t1, NULL)); 83 ASSERT_EQ(0, pthread_join(t2, NULL)); 84 85 /* The no_new_privs flag was implicitly enabled on all threads. */ 86 EXPECT_TRUE(no_new_privs1); 87 EXPECT_TRUE(no_new_privs2); 88 89 EXPECT_EQ(0, close(ruleset_fd)); 90 } 91 92 TEST(multi_threaded_success_despite_diverging_domains) 93 { 94 pthread_t t1, t2; 95 const int ruleset_fd = create_ruleset(_metadata); 96 97 disable_caps(_metadata); 98 99 ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 100 101 ASSERT_EQ(0, pthread_create(&t1, NULL, idle, NULL)); 102 ASSERT_EQ(0, pthread_create(&t2, NULL, idle, NULL)); 103 104 /* 105 * The main thread enforces a ruleset, 106 * thereby bringing the threads' Landlock domains out of sync. 107 */ 108 EXPECT_EQ(0, landlock_restrict_self(ruleset_fd, 0)); 109 110 /* Still, TSYNC succeeds, bringing the threads in sync again. */ 111 EXPECT_EQ(0, landlock_restrict_self(ruleset_fd, 112 LANDLOCK_RESTRICT_SELF_TSYNC)); 113 114 ASSERT_EQ(0, pthread_cancel(t1)); 115 ASSERT_EQ(0, pthread_cancel(t2)); 116 ASSERT_EQ(0, pthread_join(t1, NULL)); 117 ASSERT_EQ(0, pthread_join(t2, NULL)); 118 EXPECT_EQ(0, close(ruleset_fd)); 119 } 120 121 struct thread_restrict_data { 122 pthread_t t; 123 int ruleset_fd; 124 int result; 125 }; 126 127 static void *thread_restrict(void *data) 128 { 129 struct thread_restrict_data *d = data; 130 131 d->result = landlock_restrict_self(d->ruleset_fd, 132 LANDLOCK_RESTRICT_SELF_TSYNC); 133 return NULL; 134 } 135 136 TEST(competing_enablement) 137 { 138 const int ruleset_fd = create_ruleset(_metadata); 139 struct thread_restrict_data d[] = { 140 { .ruleset_fd = ruleset_fd }, 141 { .ruleset_fd = ruleset_fd }, 142 }; 143 144 disable_caps(_metadata); 145 146 ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 147 ASSERT_EQ(0, pthread_create(&d[0].t, NULL, thread_restrict, &d[0])); 148 ASSERT_EQ(0, pthread_create(&d[1].t, NULL, thread_restrict, &d[1])); 149 150 /* Wait for threads to finish. */ 151 ASSERT_EQ(0, pthread_join(d[0].t, NULL)); 152 ASSERT_EQ(0, pthread_join(d[1].t, NULL)); 153 154 /* Expect that both succeeded. */ 155 EXPECT_EQ(0, d[0].result); 156 EXPECT_EQ(0, d[1].result); 157 158 EXPECT_EQ(0, close(ruleset_fd)); 159 } 160 161 TEST_HARNESS_MAIN 162