xref: /linux/tools/testing/selftests/filesystems/mount_cycle/locked_handle_test.c (revision 8e8cd9a4c6dc006e9bd51b8ad0079fb4576b48d3)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * may_decode_fh() refuses a file handle below a mount with locked children
4  * to root in a user namespace. That has to hold while the mount is lazily
5  * unmounted and its children, the locked ones too, are taken off it.
6  */
7 #define _GNU_SOURCE
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <pthread.h>
11 #include <sched.h>
12 #include <stdatomic.h>
13 #include <stdbool.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18 #include <sys/mount.h>
19 #include <sys/prctl.h>
20 #include <sys/stat.h>
21 #include <sys/syscall.h>
22 #include <sys/wait.h>
23 
24 #include "../../kselftest_harness.h"
25 
26 #ifndef OPEN_TREE_CLONE
27 #define OPEN_TREE_CLONE		1
28 #endif
29 #ifndef OPEN_TREE_CLOEXEC
30 #define OPEN_TREE_CLOEXEC	O_CLOEXEC
31 #endif
32 #ifndef AT_RECURSIVE
33 #define AT_RECURSIVE		0x8000
34 #endif
35 
36 #define DIR_LEN		64
37 #define PATH_LEN	128
38 
39 #define ROUNDS		100	/* lazy umounts raced per test */
40 #define RACERS		3	/* threads in open_by_handle_at() */
41 #define EXTRA_MOUNTS	64	/* make umount_tree() hold mount_lock longer */
42 #define MAX_DELAY_US	3000	/* before the umount */
43 #define TAIL_US		2000	/* after it */
44 
45 struct handle {
46 	struct file_handle fh;
47 	unsigned char buf[MAX_HANDLE_SZ];
48 };
49 
50 /* exit codes of the child */
51 enum {
52 	CHILD_OK,
53 	CHILD_SETUP,
54 	CHILD_DECODED,		/* decoded past a locked child */
55 	CHILD_MOUNTED,		/* not refused while mounted */
56 	CHILD_UNMOUNTED,	/* not refused once unmounted */
57 	CHILD_ALLOWED,		/* refused where nothing is locked */
58 	CHILD_NOUSERNS,		/* no user namespace to be had */
59 };
60 
write_file(const char * path,const char * s)61 static int write_file(const char *path, const char *s)
62 {
63 	ssize_t n = -1;
64 	int fd;
65 
66 	fd = open(path, O_WRONLY | O_CLOEXEC);
67 	if (fd >= 0) {
68 		n = write(fd, s, strlen(s));
69 		close(fd);
70 	}
71 	return n == (ssize_t)strlen(s) ? 0 : -1;
72 }
73 
74 /* Become root in a new user namespace with a private mount namespace. */
enter_userns(void)75 static int enter_userns(void)
76 {
77 	uid_t uid = getuid();
78 	gid_t gid = getgid();
79 	char map[32];
80 
81 	prctl(PR_SET_DUMPABLE, 1);
82 	/* EINVAL: no USER_NS, ENOSPC: user.max_user_namespaces is 0, EPERM: an LSM */
83 	if (unshare(CLONE_NEWUSER | CLONE_NEWNS))
84 		return errno == EINVAL || errno == ENOSPC || errno == EPERM ?
85 		       CHILD_NOUSERNS : CHILD_SETUP;
86 	if (write_file("/proc/self/setgroups", "deny") && errno != ENOENT)
87 		return CHILD_SETUP;
88 	snprintf(map, sizeof(map), "0 %d 1", uid);
89 	if (write_file("/proc/self/uid_map", map))
90 		return CHILD_SETUP;
91 	snprintf(map, sizeof(map), "0 %d 1", gid);
92 	if (write_file("/proc/self/gid_map", map))
93 		return CHILD_SETUP;
94 	if (setgid(0) || setuid(0))
95 		return CHILD_SETUP;
96 	if (mount("", "/", NULL, MS_REC | MS_PRIVATE, NULL))
97 		return CHILD_SETUP;
98 	return CHILD_OK;
99 }
100 
get_handle(const char * path,struct handle * h)101 static int get_handle(const char *path, struct handle *h)
102 {
103 	int mntid;
104 
105 	h->fh.handle_bytes = MAX_HANDLE_SZ;
106 	return name_to_handle_at(AT_FDCWD, path, &h->fh, &mntid, 0);
107 }
108 
decode(int dfd,struct handle * h)109 static int decode(int dfd, struct handle *h)
110 {
111 	return open_by_handle_at(dfd, &h->fh, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
112 }
113 
114 struct race {
115 	int dfd;
116 	struct handle *h;
117 	atomic_int go;
118 	atomic_int stop;
119 	atomic_int won;		/* the first decoded descriptor */
120 };
121 
racer(void * arg)122 static void *racer(void *arg)
123 {
124 	struct race *r = arg;
125 
126 	while (!atomic_load(&r->go))
127 		;
128 	while (!atomic_load(&r->stop)) {
129 		int none = -1;
130 		int fd;
131 
132 		fd = decode(r->dfd, r->h);
133 		if (fd < 0)
134 			continue;
135 		if (!atomic_compare_exchange_strong(&r->won, &none, fd))
136 			close(fd);
137 	}
138 	return NULL;
139 }
140 
141 /* stop the @n racers started so far and wait for them, they spin on @r */
stop_racers(struct race * r,pthread_t * th,int n)142 static void stop_racers(struct race *r, pthread_t *th, int n)
143 {
144 	int i;
145 
146 	atomic_store(&r->stop, 1);
147 	atomic_store(&r->go, 1);	/* one still waiting for the start sees stop next */
148 	for (i = 0; i < n; i++)
149 		pthread_join(th[i], NULL);
150 }
151 
152 /*
153  * Bind @h recursively at @w, or take a detached copy of it, and race
154  * open_by_handle_at() of @inner against the lazy umount. The decoded
155  * descriptor, if there was one, is left in @won.
156  */
race_round(const char * h,const char * w,struct handle * inner,bool dissolve,int * won)157 static int race_round(const char *h, const char *w, struct handle *inner,
158 		      bool dissolve, int *won)
159 {
160 	struct race r = { .h = inner, .won = -1 };
161 	pthread_t th[RACERS];
162 	int treefd = -1, fd, i;
163 
164 	if (dissolve) {
165 		treefd = syscall(__NR_open_tree, AT_FDCWD, h,
166 				 OPEN_TREE_CLONE | AT_RECURSIVE | OPEN_TREE_CLOEXEC);
167 		if (treefd < 0)
168 			return CHILD_SETUP;
169 		/* an ordinary descriptor on the copy, treefd's close dissolves it */
170 		r.dfd = openat(treefd, ".", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
171 	} else {
172 		if (mount(h, w, NULL, MS_BIND | MS_REC, NULL))
173 			return CHILD_SETUP;
174 		r.dfd = open(w, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
175 	}
176 	if (r.dfd < 0)
177 		return CHILD_SETUP;
178 
179 	fd = decode(r.dfd, inner);
180 	if (fd >= 0 || errno != EPERM) {
181 		if (fd >= 0)
182 			close(fd);
183 		return CHILD_MOUNTED;
184 	}
185 
186 	for (i = 0; i < RACERS; i++) {
187 		if (pthread_create(&th[i], NULL, racer, &r)) {
188 			stop_racers(&r, th, i);
189 			return CHILD_SETUP;
190 		}
191 	}
192 	atomic_store(&r.go, 1);
193 	usleep(rand() % MAX_DELAY_US);
194 	if (dissolve)
195 		close(treefd);
196 	else if (umount2(w, MNT_DETACH)) {
197 		stop_racers(&r, th, RACERS);
198 		return CHILD_SETUP;
199 	}
200 	usleep(TAIL_US);
201 	stop_racers(&r, th, RACERS);
202 
203 	fd = decode(r.dfd, inner);
204 	if (fd >= 0) {
205 		close(fd);
206 		return CHILD_UNMOUNTED;
207 	}
208 	close(r.dfd);
209 	*won = atomic_load(&r.won);
210 	return CHILD_OK;
211 }
212 
race_child(const char * h,const char * w,struct handle * inner,bool dissolve)213 static int race_child(const char *h, const char *w, struct handle *inner,
214 		      bool dissolve)
215 {
216 	int ret, won, i;
217 
218 	srand(getpid());
219 	ret = enter_userns();
220 	if (ret)
221 		return ret;
222 	for (i = 0; i < ROUNDS; i++) {
223 		ret = race_round(h, w, inner, dissolve, &won);
224 		if (ret)
225 			return ret;
226 		if (won >= 0)
227 			return CHILD_DECODED;
228 	}
229 	return CHILD_OK;
230 }
231 
232 /* a bind without locked children decodes while mounted and not after */
allowed_child(const char * plain,const char * w,struct handle * h)233 static int allowed_child(const char *plain, const char *w, struct handle *h)
234 {
235 	int dfd, fd, ret;
236 
237 	ret = enter_userns();
238 	if (ret)
239 		return ret;
240 	if (mount(plain, w, NULL, MS_BIND | MS_REC, NULL))
241 		return CHILD_SETUP;
242 	dfd = open(w, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
243 	if (dfd < 0)
244 		return CHILD_SETUP;
245 	fd = decode(dfd, h);
246 	if (fd < 0)
247 		return CHILD_ALLOWED;
248 	close(fd);
249 	if (umount2(w, MNT_DETACH))
250 		return CHILD_SETUP;
251 	fd = decode(dfd, h);
252 	if (fd >= 0) {
253 		close(fd);
254 		return CHILD_UNMOUNTED;
255 	}
256 	return errno == EPERM ? CHILD_OK : CHILD_UNMOUNTED;
257 }
258 
FIXTURE(locked_handle)259 FIXTURE(locked_handle) {
260 	char base[DIR_LEN];
261 	char h[PATH_LEN];		/* the tree with the covered directory */
262 	char plain[PATH_LEN];		/* a tree with no mount in it */
263 	char w[PATH_LEN];		/* where the child binds either */
264 	struct handle inner;		/* h/top/secret/inner, covered */
265 	struct handle uncovered;	/* plain/inner */
266 };
267 
FIXTURE_SETUP(locked_handle)268 FIXTURE_SETUP(locked_handle)
269 {
270 	char p[PATH_LEN], e[PATH_LEN];
271 	int i;
272 
273 	if (geteuid())
274 		SKIP(return, "test requires root");
275 
276 	snprintf(self->base, sizeof(self->base), "/tmp/locked_handle.XXXXXX");
277 	ASSERT_NE(mkdtemp(self->base), NULL);
278 	ASSERT_EQ(unshare(CLONE_NEWNS), 0);
279 	ASSERT_EQ(mount("", "/", NULL, MS_REC | MS_PRIVATE, NULL), 0);
280 	ASSERT_EQ(mount("tmpfs", self->base, "tmpfs", 0, NULL), 0);
281 
282 	snprintf(self->h, sizeof(self->h), "%s/h", self->base);
283 	snprintf(self->plain, sizeof(self->plain), "%s/plain", self->base);
284 	snprintf(self->w, sizeof(self->w), "%s/w", self->base);
285 	ASSERT_EQ(mkdir(self->h, 0755), 0);
286 	ASSERT_EQ(mkdir(self->plain, 0755), 0);
287 	ASSERT_EQ(mkdir(self->w, 0755), 0);
288 	snprintf(p, sizeof(p), "%s/plain/inner", self->base);
289 	ASSERT_EQ(mkdir(p, 0755), 0);
290 	ASSERT_EQ(get_handle(p, &self->uncovered), 0);
291 
292 	/* the handle is for this filesystem */
293 	ASSERT_EQ(mount("tmpfs", self->h, "tmpfs", 0, NULL), 0);
294 	snprintf(p, sizeof(p), "%s/h/top", self->base);
295 	ASSERT_EQ(mkdir(p, 0755), 0);
296 	snprintf(p, sizeof(p), "%s/h/top/secret", self->base);
297 	ASSERT_EQ(mkdir(p, 0755), 0);
298 	snprintf(p, sizeof(p), "%s/h/top/secret/inner", self->base);
299 	ASSERT_EQ(mkdir(p, 0755), 0);
300 	ASSERT_EQ(get_handle(p, &self->inner), 0);
301 	snprintf(e, sizeof(e), "%s/h/empty", self->base);
302 	ASSERT_EQ(mkdir(e, 0755), 0);
303 
304 	/* cover it, and some more so the umount takes longer */
305 	snprintf(p, sizeof(p), "%s/h/top/secret", self->base);
306 	ASSERT_EQ(mount("tmpfs", p, "tmpfs", 0, NULL), 0);
307 	for (i = 0; i < EXTRA_MOUNTS; i++) {
308 		snprintf(p, sizeof(p), "%s/h/c%d", self->base, i);
309 		ASSERT_EQ(mkdir(p, 0755), 0);
310 		ASSERT_EQ(mount(e, p, NULL, MS_BIND, NULL), 0);
311 	}
312 }
313 
FIXTURE_TEARDOWN(locked_handle)314 FIXTURE_TEARDOWN(locked_handle)
315 {
316 	umount2(self->base, MNT_DETACH);
317 	rmdir(self->base);
318 }
319 
wait_child(pid_t pid)320 static int wait_child(pid_t pid)
321 {
322 	int status;
323 
324 	if (waitpid(pid, &status, 0) != pid || !WIFEXITED(status))
325 		return -1;
326 	return WEXITSTATUS(status);
327 }
328 
TEST_F(locked_handle,lazy_umount)329 TEST_F(locked_handle, lazy_umount)
330 {
331 	pid_t pid;
332 	int ret;
333 
334 	pid = fork();
335 	ASSERT_GE(pid, 0);
336 	if (pid == 0)
337 		_exit(race_child(self->h, self->w, &self->inner, false));
338 	ret = wait_child(pid);
339 	TH_LOG("child exit code %d", ret);
340 	if (ret == CHILD_NOUSERNS)
341 		SKIP(return, "no user namespaces");
342 	EXPECT_EQ(ret, CHILD_OK);
343 }
344 
TEST_F(locked_handle,dissolve)345 TEST_F(locked_handle, dissolve)
346 {
347 	pid_t pid;
348 	int ret;
349 
350 	pid = fork();
351 	ASSERT_GE(pid, 0);
352 	if (pid == 0)
353 		_exit(race_child(self->h, self->w, &self->inner, true));
354 	ret = wait_child(pid);
355 	TH_LOG("child exit code %d", ret);
356 	if (ret == CHILD_NOUSERNS)
357 		SKIP(return, "no user namespaces");
358 	EXPECT_EQ(ret, CHILD_OK);
359 }
360 
TEST_F(locked_handle,allowed_use)361 TEST_F(locked_handle, allowed_use)
362 {
363 	pid_t pid;
364 	int ret;
365 
366 	pid = fork();
367 	ASSERT_GE(pid, 0);
368 	if (pid == 0)
369 		_exit(allowed_child(self->plain, self->w, &self->uncovered));
370 	ret = wait_child(pid);
371 	TH_LOG("child exit code %d", ret);
372 	if (ret == CHILD_NOUSERNS)
373 		SKIP(return, "no user namespaces");
374 	EXPECT_EQ(ret, CHILD_OK);
375 }
376 
377 TEST_HARNESS_MAIN
378