xref: /linux/tools/testing/selftests/filesystems/readdir_hold.h (revision 8e8cd9a4c6dc006e9bd51b8ad0079fb4576b48d3)
1*fa15a241SChristian Brauner /* SPDX-License-Identifier: GPL-2.0 */
2*fa15a241SChristian Brauner /*
3*fa15a241SChristian Brauner  * Hold a readdir of a directory in the page fault of its buffer and see
4*fa15a241SChristian Brauner  * whether a create and a lookup in that directory wait for it. For a
5*fa15a241SChristian Brauner  * directory that is permanently empty they must not.
6*fa15a241SChristian Brauner  */
7*fa15a241SChristian Brauner #ifndef __SELFTESTS_READDIR_HOLD_H
8*fa15a241SChristian Brauner #define __SELFTESTS_READDIR_HOLD_H
9*fa15a241SChristian Brauner 
10*fa15a241SChristian Brauner #include <errno.h>
11*fa15a241SChristian Brauner #include <fcntl.h>
12*fa15a241SChristian Brauner #include <poll.h>
13*fa15a241SChristian Brauner #include <pthread.h>
14*fa15a241SChristian Brauner #include <stdbool.h>
15*fa15a241SChristian Brauner #include <stdio.h>
16*fa15a241SChristian Brauner #include <stdlib.h>
17*fa15a241SChristian Brauner #include <string.h>
18*fa15a241SChristian Brauner #include <unistd.h>
19*fa15a241SChristian Brauner #include <linux/userfaultfd.h>
20*fa15a241SChristian Brauner #include <sys/ioctl.h>
21*fa15a241SChristian Brauner #include <sys/mman.h>
22*fa15a241SChristian Brauner #include <sys/stat.h>
23*fa15a241SChristian Brauner #include <sys/syscall.h>
24*fa15a241SChristian Brauner 
25*fa15a241SChristian Brauner #define HOLD_FAULT_MS	5000	/* for the reader to reach the fault */
26*fa15a241SChristian Brauner #define HOLD_QUEUE_MS	2000	/* for the create to queue up behind it */
27*fa15a241SChristian Brauner #define HOLD_LOOKUP_MS	5000	/* for the lookup to come back */
28*fa15a241SChristian Brauner 
29*fa15a241SChristian Brauner struct readdir_hold {
30*fa15a241SChristian Brauner 	int uffd;
31*fa15a241SChristian Brauner 	int taskdir;		/* /proc/self/task, from before any namespace change */
32*fa15a241SChristian Brauner 	long page_size;
33*fa15a241SChristian Brauner 	char *page;		/* faults until released */
34*fa15a241SChristian Brauner 	int dfd;
35*fa15a241SChristian Brauner 	pid_t creator_tid;
36*fa15a241SChristian Brauner 	int created;
37*fa15a241SChristian Brauner 	int done[2];		/* the finder writes a byte when it is back */
38*fa15a241SChristian Brauner };
39*fa15a241SChristian Brauner 
40*fa15a241SChristian Brauner /*
41*fa15a241SChristian Brauner  * The fault happens in the kernel, so the userfaultfd needs CAP_SYS_PTRACE
42*fa15a241SChristian Brauner  * in the initial user namespace or vm.unprivileged_userfaultfd. Call this
43*fa15a241SChristian Brauner  * before entering a user namespace.
44*fa15a241SChristian Brauner  */
readdir_hold_init(struct readdir_hold * h)45*fa15a241SChristian Brauner static inline int readdir_hold_init(struct readdir_hold *h)
46*fa15a241SChristian Brauner {
47*fa15a241SChristian Brauner 	struct uffdio_api api = { .api = UFFD_API };
48*fa15a241SChristian Brauner 
49*fa15a241SChristian Brauner 	h->page = MAP_FAILED;
50*fa15a241SChristian Brauner 	h->page_size = sysconf(_SC_PAGESIZE);
51*fa15a241SChristian Brauner 	h->taskdir = open("/proc/self/task", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
52*fa15a241SChristian Brauner 	h->uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
53*fa15a241SChristian Brauner 	if (h->uffd < 0 || h->taskdir < 0 || ioctl(h->uffd, UFFDIO_API, &api)) {
54*fa15a241SChristian Brauner 		if (h->uffd >= 0)
55*fa15a241SChristian Brauner 			close(h->uffd);
56*fa15a241SChristian Brauner 		if (h->taskdir >= 0)
57*fa15a241SChristian Brauner 			close(h->taskdir);
58*fa15a241SChristian Brauner 		h->uffd = h->taskdir = -1;
59*fa15a241SChristian Brauner 		return -1;
60*fa15a241SChristian Brauner 	}
61*fa15a241SChristian Brauner 	return 0;
62*fa15a241SChristian Brauner }
63*fa15a241SChristian Brauner 
readdir_hold_destroy(struct readdir_hold * h)64*fa15a241SChristian Brauner static inline void readdir_hold_destroy(struct readdir_hold *h)
65*fa15a241SChristian Brauner {
66*fa15a241SChristian Brauner 	if (h->uffd >= 0)
67*fa15a241SChristian Brauner 		close(h->uffd);
68*fa15a241SChristian Brauner 	if (h->taskdir >= 0)
69*fa15a241SChristian Brauner 		close(h->taskdir);
70*fa15a241SChristian Brauner 	h->uffd = h->taskdir = -1;
71*fa15a241SChristian Brauner }
72*fa15a241SChristian Brauner 
readdir_hold_reader(void * arg)73*fa15a241SChristian Brauner static inline void *readdir_hold_reader(void *arg)
74*fa15a241SChristian Brauner {
75*fa15a241SChristian Brauner 	struct readdir_hold *h = arg;
76*fa15a241SChristian Brauner 
77*fa15a241SChristian Brauner 	/* the first byte written to the buffer faults until released */
78*fa15a241SChristian Brauner 	syscall(__NR_getdents64, h->dfd, h->page, h->page_size);
79*fa15a241SChristian Brauner 	return NULL;
80*fa15a241SChristian Brauner }
81*fa15a241SChristian Brauner 
readdir_hold_creator(void * arg)82*fa15a241SChristian Brauner static inline void *readdir_hold_creator(void *arg)
83*fa15a241SChristian Brauner {
84*fa15a241SChristian Brauner 	struct readdir_hold *h = arg;
85*fa15a241SChristian Brauner 
86*fa15a241SChristian Brauner 	h->creator_tid = syscall(__NR_gettid);
87*fa15a241SChristian Brauner 	/* takes the directory lock exclusive before it fails */
88*fa15a241SChristian Brauner 	mkdirat(h->dfd, "x", 0755);
89*fa15a241SChristian Brauner 	__atomic_store_n(&h->created, 1, __ATOMIC_RELEASE);
90*fa15a241SChristian Brauner 	return NULL;
91*fa15a241SChristian Brauner }
92*fa15a241SChristian Brauner 
readdir_hold_finder(void * arg)93*fa15a241SChristian Brauner static inline void *readdir_hold_finder(void *arg)
94*fa15a241SChristian Brauner {
95*fa15a241SChristian Brauner 	struct readdir_hold *h = arg;
96*fa15a241SChristian Brauner 	int fd;
97*fa15a241SChristian Brauner 
98*fa15a241SChristian Brauner 	/* a lookup that misses the dcache takes the lock shared */
99*fa15a241SChristian Brauner 	fd = openat(h->dfd, "no_such_name", O_RDONLY | O_CLOEXEC);
100*fa15a241SChristian Brauner 	if (fd >= 0)
101*fa15a241SChristian Brauner 		close(fd);
102*fa15a241SChristian Brauner 	if (write(h->done[1], "x", 1) != 1)
103*fa15a241SChristian Brauner 		perror("readdir_hold: finder");
104*fa15a241SChristian Brauner 	return NULL;
105*fa15a241SChristian Brauner }
106*fa15a241SChristian Brauner 
107*fa15a241SChristian Brauner /* the creator is back, or waits in the kernel for the lock */
readdir_hold_creator_settled(struct readdir_hold * h)108*fa15a241SChristian Brauner static inline bool readdir_hold_creator_settled(struct readdir_hold *h)
109*fa15a241SChristian Brauner {
110*fa15a241SChristian Brauner 	char path[32], buf[256], *p;
111*fa15a241SChristian Brauner 	ssize_t n;
112*fa15a241SChristian Brauner 	int fd;
113*fa15a241SChristian Brauner 
114*fa15a241SChristian Brauner 	if (__atomic_load_n(&h->created, __ATOMIC_ACQUIRE))
115*fa15a241SChristian Brauner 		return true;
116*fa15a241SChristian Brauner 	if (!h->creator_tid)
117*fa15a241SChristian Brauner 		return false;
118*fa15a241SChristian Brauner 	snprintf(path, sizeof(path), "%d/stat", h->creator_tid);
119*fa15a241SChristian Brauner 	fd = openat(h->taskdir, path, O_RDONLY | O_CLOEXEC);
120*fa15a241SChristian Brauner 	if (fd < 0)
121*fa15a241SChristian Brauner 		return false;
122*fa15a241SChristian Brauner 	n = read(fd, buf, sizeof(buf) - 1);
123*fa15a241SChristian Brauner 	close(fd);
124*fa15a241SChristian Brauner 	if (n <= 0)
125*fa15a241SChristian Brauner 		return false;
126*fa15a241SChristian Brauner 	buf[n] = 0;
127*fa15a241SChristian Brauner 	/* "pid (comm) state ..." */
128*fa15a241SChristian Brauner 	p = strrchr(buf, ')');
129*fa15a241SChristian Brauner 	return p && p[1] == ' ' && p[2] == 'D';
130*fa15a241SChristian Brauner }
131*fa15a241SChristian Brauner 
readdir_hold_faulted(struct readdir_hold * h)132*fa15a241SChristian Brauner static inline bool readdir_hold_faulted(struct readdir_hold *h)
133*fa15a241SChristian Brauner {
134*fa15a241SChristian Brauner 	struct pollfd pfd = { .fd = h->uffd, .events = POLLIN };
135*fa15a241SChristian Brauner 	struct uffd_msg msg;
136*fa15a241SChristian Brauner 
137*fa15a241SChristian Brauner 	if (poll(&pfd, 1, HOLD_FAULT_MS) != 1)
138*fa15a241SChristian Brauner 		return false;
139*fa15a241SChristian Brauner 	if (read(h->uffd, &msg, sizeof(msg)) != sizeof(msg))
140*fa15a241SChristian Brauner 		return false;
141*fa15a241SChristian Brauner 	return msg.event == UFFD_EVENT_PAGEFAULT;
142*fa15a241SChristian Brauner }
143*fa15a241SChristian Brauner 
144*fa15a241SChristian Brauner /* let the reader go on */
readdir_hold_release(struct readdir_hold * h)145*fa15a241SChristian Brauner static inline void readdir_hold_release(struct readdir_hold *h)
146*fa15a241SChristian Brauner {
147*fa15a241SChristian Brauner 	struct uffdio_copy cp = {
148*fa15a241SChristian Brauner 		.dst = (unsigned long)h->page,
149*fa15a241SChristian Brauner 		.len = h->page_size,
150*fa15a241SChristian Brauner 	};
151*fa15a241SChristian Brauner 	void *zero;
152*fa15a241SChristian Brauner 
153*fa15a241SChristian Brauner 	zero = calloc(1, h->page_size);
154*fa15a241SChristian Brauner 	if (!zero)
155*fa15a241SChristian Brauner 		return;
156*fa15a241SChristian Brauner 	cp.src = (unsigned long)zero;
157*fa15a241SChristian Brauner 	if (ioctl(h->uffd, UFFDIO_COPY, &cp) && errno != EEXIST)
158*fa15a241SChristian Brauner 		perror("readdir_hold: UFFDIO_COPY");
159*fa15a241SChristian Brauner 	free(zero);
160*fa15a241SChristian Brauner }
161*fa15a241SChristian Brauner 
162*fa15a241SChristian Brauner /*
163*fa15a241SChristian Brauner  * A readdir of @dfd that sticks in the fault of its buffer, then a create
164*fa15a241SChristian Brauner  * and a lookup in @dfd. Whether the lookup came back while the readdir was
165*fa15a241SChristian Brauner  * still stuck goes to @stalled. Returns -1 when that could not be found
166*fa15a241SChristian Brauner  * out.
167*fa15a241SChristian Brauner  */
readdir_hold_check(struct readdir_hold * h,int dfd,bool * stalled)168*fa15a241SChristian Brauner static inline int readdir_hold_check(struct readdir_hold *h, int dfd,
169*fa15a241SChristian Brauner 				     bool *stalled)
170*fa15a241SChristian Brauner {
171*fa15a241SChristian Brauner 	pthread_t reader, creator, finder;
172*fa15a241SChristian Brauner 	struct uffdio_register reg = {};
173*fa15a241SChristian Brauner 	struct pollfd pfd;
174*fa15a241SChristian Brauner 	int ret = -1, i;
175*fa15a241SChristian Brauner 
176*fa15a241SChristian Brauner 	h->dfd = dfd;
177*fa15a241SChristian Brauner 	h->created = 0;
178*fa15a241SChristian Brauner 	h->creator_tid = 0;
179*fa15a241SChristian Brauner 	h->page = mmap(NULL, h->page_size, PROT_READ | PROT_WRITE,
180*fa15a241SChristian Brauner 		       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
181*fa15a241SChristian Brauner 	if (h->page == MAP_FAILED)
182*fa15a241SChristian Brauner 		return -1;
183*fa15a241SChristian Brauner 	reg.range.start = (unsigned long)h->page;
184*fa15a241SChristian Brauner 	reg.range.len = h->page_size;
185*fa15a241SChristian Brauner 	reg.mode = UFFDIO_REGISTER_MODE_MISSING;
186*fa15a241SChristian Brauner 	if (ioctl(h->uffd, UFFDIO_REGISTER, &reg) || pipe2(h->done, O_CLOEXEC))
187*fa15a241SChristian Brauner 		goto out_page;
188*fa15a241SChristian Brauner 
189*fa15a241SChristian Brauner 	if (pthread_create(&reader, NULL, readdir_hold_reader, h))
190*fa15a241SChristian Brauner 		goto out_pipe;
191*fa15a241SChristian Brauner 	if (!readdir_hold_faulted(h))
192*fa15a241SChristian Brauner 		goto out_reader;
193*fa15a241SChristian Brauner 	if (pthread_create(&creator, NULL, readdir_hold_creator, h))
194*fa15a241SChristian Brauner 		goto out_reader;
195*fa15a241SChristian Brauner 	for (i = 0; i < HOLD_QUEUE_MS / 10 && !readdir_hold_creator_settled(h); i++)
196*fa15a241SChristian Brauner 		usleep(10000);
197*fa15a241SChristian Brauner 	if (pthread_create(&finder, NULL, readdir_hold_finder, h))
198*fa15a241SChristian Brauner 		goto out_creator;
199*fa15a241SChristian Brauner 
200*fa15a241SChristian Brauner 	pfd.fd = h->done[0];
201*fa15a241SChristian Brauner 	pfd.events = POLLIN;
202*fa15a241SChristian Brauner 	*stalled = poll(&pfd, 1, HOLD_LOOKUP_MS) != 1;
203*fa15a241SChristian Brauner 	ret = 0;
204*fa15a241SChristian Brauner 
205*fa15a241SChristian Brauner 	readdir_hold_release(h);
206*fa15a241SChristian Brauner 	pthread_join(finder, NULL);
207*fa15a241SChristian Brauner out_creator:
208*fa15a241SChristian Brauner 	if (ret)
209*fa15a241SChristian Brauner 		readdir_hold_release(h);
210*fa15a241SChristian Brauner 	pthread_join(creator, NULL);
211*fa15a241SChristian Brauner out_reader:
212*fa15a241SChristian Brauner 	if (ret)
213*fa15a241SChristian Brauner 		readdir_hold_release(h);
214*fa15a241SChristian Brauner 	pthread_join(reader, NULL);
215*fa15a241SChristian Brauner out_pipe:
216*fa15a241SChristian Brauner 	close(h->done[0]);
217*fa15a241SChristian Brauner 	close(h->done[1]);
218*fa15a241SChristian Brauner out_page:
219*fa15a241SChristian Brauner 	munmap(h->page, h->page_size);
220*fa15a241SChristian Brauner 	h->page = MAP_FAILED;
221*fa15a241SChristian Brauner 	return ret;
222*fa15a241SChristian Brauner }
223*fa15a241SChristian Brauner 
224*fa15a241SChristian Brauner #endif /* __SELFTESTS_READDIR_HOLD_H */
225