• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2017 SUSE.  All Rights Reserved.
4  *
5  * Started by Jan Kara <jack@suse.cz>
6  *
7  * DESCRIPTION
8  *     Check that fanotify permission events are handled properly on instance
9  *     destruction.
10  *
11  * Kernel crashes should be fixed by:
12  *  96d41019e3ac "fanotify: fix list corruption in fanotify_get_response()"
13  *
14  * Kernel hangs should be fixed by:
15  *  05f0e38724e8 "fanotify: Release SRCU lock when waiting for userspace response"
16  */
17 #define _GNU_SOURCE
18 #include "config.h"
19 
20 #include <stdio.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <sys/fcntl.h>
26 #include <sys/wait.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <signal.h>
30 #include <sys/syscall.h>
31 #include "tst_test.h"
32 #include "lapi/syscalls.h"
33 #include "fanotify.h"
34 
35 #if defined(HAVE_SYS_FANOTIFY_H)
36 #include <sys/fanotify.h>
37 
38 #define BUF_SIZE 256
39 static char fname[BUF_SIZE];
40 static char buf[BUF_SIZE];
41 static volatile int fd_notify;
42 
43 /* Number of children we start */
44 #define MAX_CHILDREN 16
45 static pid_t child_pid[MAX_CHILDREN];
46 
47 /* Number of children we don't respond to before stopping */
48 #define MAX_NOT_RESPONDED 4
49 
generate_events(void)50 static void generate_events(void)
51 {
52 	int fd;
53 
54 	/*
55 	 * generate sequence of events
56 	 */
57 	if ((fd = open(fname, O_RDWR | O_CREAT, 0700)) == -1)
58 		exit(1);
59 
60 	/* Run until killed... */
61 	while (1) {
62 		lseek(fd, 0, SEEK_SET);
63 		if (read(fd, buf, BUF_SIZE) == -1)
64 			exit(3);
65 	}
66 }
67 
run_children(void)68 static void run_children(void)
69 {
70 	int i;
71 
72 	for (i = 0; i < MAX_CHILDREN; i++) {
73 		child_pid[i] = SAFE_FORK();
74 		if (!child_pid[i]) {
75 			/* Child will generate events now */
76 			close(fd_notify);
77 			generate_events();
78 			exit(0);
79 		}
80 	}
81 }
82 
stop_children(void)83 static int stop_children(void)
84 {
85 	int child_ret;
86 	int i, ret = 0;
87 
88 	for (i = 0; i < MAX_CHILDREN; i++)
89 		SAFE_KILL(child_pid[i], SIGKILL);
90 
91 	for (i = 0; i < MAX_CHILDREN; i++) {
92 		SAFE_WAITPID(child_pid[i], &child_ret, 0);
93 		if (!WIFSIGNALED(child_ret))
94 			ret = 1;
95 	}
96 
97 	return ret;
98 }
99 
setup_instance(void)100 static int setup_instance(void)
101 {
102 	int fd;
103 
104 	fd = SAFE_FANOTIFY_INIT(FAN_CLASS_CONTENT, O_RDONLY);
105 
106 	if (fanotify_mark(fd, FAN_MARK_ADD, FAN_ACCESS_PERM, AT_FDCWD,
107 			  fname) < 0) {
108 		close(fd);
109 		if (errno == EINVAL) {
110 			tst_brk(TCONF | TERRNO,
111 				"CONFIG_FANOTIFY_ACCESS_PERMISSIONS not "
112 				"configured in kernel?");
113 		} else {
114 			tst_brk(TBROK | TERRNO,
115 				"fanotify_mark (%d, FAN_MARK_ADD, FAN_ACCESS_PERM, "
116 				"AT_FDCWD, %s) failed.", fd, fname);
117 		}
118 	}
119 
120 	return fd;
121 }
122 
loose_fanotify_events(void)123 static void loose_fanotify_events(void)
124 {
125 	int not_responded = 0;
126 
127 	/*
128 	 * check events
129 	 */
130 	while (not_responded < MAX_NOT_RESPONDED) {
131 		struct fanotify_event_metadata event;
132 		struct fanotify_response resp;
133 
134 		/* Get more events */
135 		SAFE_READ(1, fd_notify, &event, sizeof(event));
136 
137 		if (event.mask != FAN_ACCESS_PERM) {
138 			tst_res(TFAIL,
139 				"got event: mask=%llx (expected %llx) "
140 				"pid=%u fd=%d",
141 				(unsigned long long)event.mask,
142 				(unsigned long long)FAN_ACCESS_PERM,
143 				(unsigned)event.pid, event.fd);
144 			break;
145 		}
146 
147 		/*
148 		 * We respond to permission event with 95% percent
149 		 * probability. */
150 		if (random() % 100 > 5) {
151 			/* Write response to permission event */
152 			resp.fd = event.fd;
153 			resp.response = FAN_ALLOW;
154 			SAFE_WRITE(1, fd_notify, &resp, sizeof(resp));
155 		} else {
156 			not_responded++;
157 		}
158 		SAFE_CLOSE(event.fd);
159 	}
160 }
161 
test_fanotify(void)162 static void test_fanotify(void)
163 {
164 	int newfd;
165 	int ret;
166 
167 	fd_notify = setup_instance();
168 	run_children();
169 	loose_fanotify_events();
170 
171 	/*
172 	 * Create and destroy another instance. This may hang if
173 	 * unanswered fanotify events block notification subsystem.
174 	 */
175 	newfd = setup_instance();
176 	if (close(newfd)) {
177 		tst_brk(TBROK | TERRNO, "close(%d) failed", newfd);
178 	}
179 
180 	tst_res(TPASS, "second instance destroyed successfully");
181 
182 	/*
183 	 * Now destroy the fanotify instance while there are permission
184 	 * events at various stages of processing. This may provoke
185 	 * kernel hangs or crashes.
186 	 */
187 	SAFE_CLOSE(fd_notify);
188 
189 	ret = stop_children();
190 	if (ret)
191 		tst_res(TFAIL, "child exited for unexpected reason");
192 	else
193 		tst_res(TPASS, "all children exited successfully");
194 }
195 
setup(void)196 static void setup(void)
197 {
198 	sprintf(fname, "fname_%d", getpid());
199 	SAFE_FILE_PRINTF(fname, "%s", fname);
200 }
201 
cleanup(void)202 static void cleanup(void)
203 {
204 	if (fd_notify > 0)
205 		SAFE_CLOSE(fd_notify);
206 }
207 
208 static struct tst_test test = {
209 	.test_all = test_fanotify,
210 	.setup = setup,
211 	.cleanup = cleanup,
212 	.needs_tmpdir = 1,
213 	.forks_child = 1,
214 	.needs_root = 1,
215 };
216 
217 #else
218 	TST_TEST_TCONF("system doesn't have required fanotify support");
219 #endif
220