xref: /linux/tools/testing/selftests/landlock/tsync_test.c (revision 50c058e3eafe31a5197d4cffb599f2f5f165d4eb)
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