• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2013 SUSE.  All Rights Reserved.
4  *
5  * Started by Jan Kara <jack@suse.cz>
6  *
7  * DESCRIPTION
8  *     Check that fanotify permission events work
9  */
10 #define _GNU_SOURCE
11 #include "config.h"
12 
13 #include <stdio.h>
14 #include <unistd.h>
15 #include <sys/stat.h>
16 #include <sys/types.h>
17 #include <fcntl.h>
18 #include <sys/wait.h>
19 #include <errno.h>
20 #include <string.h>
21 #include <signal.h>
22 #include <sys/syscall.h>
23 #include <stdlib.h>
24 #include "tst_test.h"
25 #include "fanotify.h"
26 
27 #if defined(HAVE_SYS_FANOTIFY_H)
28 #include <sys/fanotify.h>
29 
30 #define EVENT_MAX 1024
31 /* size of the event structure, not counting name */
32 #define EVENT_SIZE  (sizeof (struct fanotify_event_metadata))
33 /* reasonable guess as to size of 1024 events */
34 #define EVENT_BUF_LEN        (EVENT_MAX * EVENT_SIZE)
35 /* Size large enough to hold a reasonable amount of expected event objects */
36 #define EVENT_SET_MAX 16
37 
38 #define BUF_SIZE 256
39 #define TST_TOTAL 3
40 #define TEST_APP "fanotify_child"
41 #define MOUNT_PATH "fs_mnt"
42 #define FILE_EXEC_PATH MOUNT_PATH"/"TEST_APP
43 
44 static char fname[BUF_SIZE];
45 static char buf[BUF_SIZE];
46 static volatile int fd_notify;
47 
48 static pid_t child_pid;
49 
50 static char event_buf[EVENT_BUF_LEN];
51 static int support_exec_events;
52 
53 struct event {
54 	unsigned long long mask;
55 	unsigned int response;
56 };
57 
58 /*
59  * Ensure to keep the first FAN_OPEN_EXEC_PERM test case before the first
60  * MARK_TYPE(FILESYSTEM) in order to allow for correct detection between
61  * exec events not supported and filesystem marks not supported.
62  */
63 static struct tcase {
64 	const char *tname;
65 	struct fanotify_mark_type mark;
66 	unsigned long long mask;
67 	int event_count;
68 	struct event event_set[EVENT_SET_MAX];
69 } tcases[] = {
70 	{
71 		"inode mark, FAN_OPEN_PERM | FAN_ACCESS_PERM events",
72 		INIT_FANOTIFY_MARK_TYPE(INODE),
73 		FAN_OPEN_PERM | FAN_ACCESS_PERM, 3,
74 		{
75 			{FAN_OPEN_PERM, FAN_ALLOW},
76 			{FAN_ACCESS_PERM, FAN_DENY},
77 			{FAN_OPEN_PERM, FAN_DENY}
78 		}
79 	},
80 	{
81 		"inode mark, FAN_ACCESS_PERM | FAN_OPEN_EXEC_PERM events",
82 		INIT_FANOTIFY_MARK_TYPE(INODE),
83 		FAN_ACCESS_PERM | FAN_OPEN_EXEC_PERM, 2,
84 		{
85 			{FAN_ACCESS_PERM, FAN_DENY},
86 			{FAN_OPEN_EXEC_PERM, FAN_DENY}
87 		}
88 	},
89 	{
90 		"mount mark, FAN_OPEN_PERM | FAN_ACCESS_PERM events",
91 		INIT_FANOTIFY_MARK_TYPE(MOUNT),
92 		FAN_OPEN_PERM | FAN_ACCESS_PERM, 3,
93 		{
94 			{FAN_OPEN_PERM, FAN_ALLOW},
95 			{FAN_ACCESS_PERM, FAN_DENY},
96 			{FAN_OPEN_PERM, FAN_DENY}
97 		}
98 	},
99 	{
100 		"mount mark, FAN_ACCESS_PERM | FAN_OPEN_EXEC_PERM events",
101 		INIT_FANOTIFY_MARK_TYPE(MOUNT),
102 		FAN_ACCESS_PERM | FAN_OPEN_EXEC_PERM, 2,
103 		{
104 			{FAN_ACCESS_PERM, FAN_DENY},
105 			{FAN_OPEN_EXEC_PERM, FAN_DENY}
106 		}
107 	},
108 	{
109 		"filesystem mark, FAN_OPEN_PERM | FAN_ACCESS_PERM events",
110 		INIT_FANOTIFY_MARK_TYPE(FILESYSTEM),
111 		FAN_OPEN_PERM | FAN_ACCESS_PERM, 3,
112 		{
113 			{FAN_OPEN_PERM, FAN_ALLOW},
114 			{FAN_ACCESS_PERM, FAN_DENY},
115 			{FAN_OPEN_PERM, FAN_DENY}
116 		}
117 	},
118 	{
119 		"filesystem mark, FAN_ACCESS_PERM | FAN_OPEN_EXEC_PERM events",
120 		INIT_FANOTIFY_MARK_TYPE(FILESYSTEM),
121 		FAN_ACCESS_PERM | FAN_OPEN_EXEC_PERM, 2,
122 		{
123 			{FAN_ACCESS_PERM, FAN_DENY},
124 			{FAN_OPEN_EXEC_PERM, FAN_DENY}
125 		}
126 	},
127 };
128 
generate_events(void)129 static void generate_events(void)
130 {
131 	int fd;
132 	char *const argv[] = {FILE_EXEC_PATH, NULL};
133 
134 	/*
135 	 * Generate sequence of events
136 	 */
137 	if ((fd = open(fname, O_RDWR | O_CREAT, 0700)) == -1)
138 		exit(1);
139 	if (write(fd, fname, 1) == -1)
140 		exit(2);
141 
142 	lseek(fd, 0, SEEK_SET);
143 	if (read(fd, buf, BUF_SIZE) != -1)
144 		exit(3);
145 
146 	if (close(fd) == -1)
147 		exit(4);
148 
149 	if (execve(FILE_EXEC_PATH, argv, environ) != -1)
150 		exit(5);
151 }
152 
child_handler(int tmp)153 static void child_handler(int tmp)
154 {
155 	(void)tmp;
156 	/*
157 	 * Close notification fd so that we cannot block while reading
158 	 * from it
159 	 */
160 	close(fd_notify);
161 	fd_notify = -1;
162 }
163 
run_child(void)164 static void run_child(void)
165 {
166 	struct sigaction child_action;
167 
168 	child_action.sa_handler = child_handler;
169 	sigemptyset(&child_action.sa_mask);
170 	child_action.sa_flags = SA_NOCLDSTOP;
171 
172 	if (sigaction(SIGCHLD, &child_action, NULL) < 0) {
173 		tst_brk(TBROK | TERRNO,
174 			"sigaction(SIGCHLD, &child_action, NULL) failed");
175 	}
176 
177 	child_pid = SAFE_FORK();
178 
179 	if (child_pid == 0) {
180 		/* Child will generate events now */
181 		close(fd_notify);
182 		generate_events();
183 		exit(0);
184 	}
185 }
186 
check_child(void)187 static void check_child(void)
188 {
189 	struct sigaction child_action;
190 	int child_ret;
191 
192 	child_action.sa_handler = SIG_IGN;
193 	sigemptyset(&child_action.sa_mask);
194 	child_action.sa_flags = SA_NOCLDSTOP;
195 	if (sigaction(SIGCHLD, &child_action, NULL) < 0) {
196 		tst_brk(TBROK | TERRNO,
197 			"sigaction(SIGCHLD, &child_action, NULL) failed");
198 	}
199 	SAFE_WAITPID(-1, &child_ret, 0);
200 
201 	if (WIFEXITED(child_ret) && WEXITSTATUS(child_ret) == 0)
202 		tst_res(TPASS, "child exited correctly");
203 	else
204 		tst_res(TFAIL, "child %s", tst_strstatus(child_ret));
205 }
206 
setup_mark(unsigned int n)207 static int setup_mark(unsigned int n)
208 {
209 	unsigned int i = 0;
210 	struct tcase *tc = &tcases[n];
211 	struct fanotify_mark_type *mark = &tc->mark;
212 	char *const files[] = {fname, FILE_EXEC_PATH};
213 
214 	tst_res(TINFO, "Test #%d: %s", n, tc->tname);
215 	fd_notify = SAFE_FANOTIFY_INIT(FAN_CLASS_CONTENT, O_RDONLY);
216 
217 	for (; i < ARRAY_SIZE(files); i++) {
218 		if (fanotify_mark(fd_notify, FAN_MARK_ADD | mark->flag,
219 				  tc->mask, AT_FDCWD, files[i]) < 0) {
220 			if (errno == EINVAL &&
221 				(tc->mask & FAN_OPEN_EXEC_PERM &&
222 				 !support_exec_events)) {
223 				tst_res(TCONF,
224 					"FAN_OPEN_EXEC_PERM not supported in "
225 					"kernel?");
226 				return -1;
227 			} else if (errno == EINVAL &&
228 					mark->flag == FAN_MARK_FILESYSTEM) {
229 				tst_res(TCONF,
230 					"FAN_MARK_FILESYSTEM not supported in "
231 					"kernel?");
232 				return -1;
233 			} else if (errno == EINVAL) {
234 				tst_brk(TCONF | TERRNO,
235 					"CONFIG_FANOTIFY_ACCESS_PERMISSIONS "
236 					"not configured in kernel?");
237 			} else {
238 				tst_brk(TBROK | TERRNO,
239 					"fanotify_mark(%d, FAN_MARK_ADD | %s, "
240 					"FAN_ACCESS_PERM | FAN_OPEN_PERM, "
241 					"AT_FDCWD, %s) failed.",
242 					fd_notify, mark->name, fname);
243 			}
244 		} else {
245 			/*
246 			 * To distinguish between perm not supported, exec
247 			 * events not supported and filesystem mark not
248 			 * supported.
249 			 */
250 			if (tc->mask & FAN_OPEN_EXEC_PERM)
251 				support_exec_events = 1;
252 		}
253 	}
254 
255 	return 0;
256 }
257 
test_fanotify(unsigned int n)258 static void test_fanotify(unsigned int n)
259 {
260 	int ret, len = 0, i = 0, test_num = 0;
261 	struct tcase *tc = &tcases[n];
262 	struct event *event_set = tc->event_set;
263 
264 	if (setup_mark(n) != 0)
265 		return;
266 
267 	run_child();
268 
269 	/*
270 	 * Process events
271 	 *
272 	 * tc->count + 1 is to accommodate for checking the child process
273 	 * return value
274 	 */
275 	while (test_num < tc->event_count + 1 && fd_notify != -1) {
276 		struct fanotify_event_metadata *event;
277 
278 		if (i == len) {
279 			/* Get more events */
280 			ret = read(fd_notify, event_buf + len,
281 				   EVENT_BUF_LEN - len);
282 			if (fd_notify == -1)
283 				break;
284 			if (ret < 0) {
285 				tst_brk(TBROK,
286 					"read(%d, buf, %zu) failed",
287 					fd_notify, EVENT_BUF_LEN);
288 			}
289 			len += ret;
290 		}
291 
292 		event = (struct fanotify_event_metadata *)&event_buf[i];
293 		/* Permission events cannot be merged, so the event mask
294 		 * reported should exactly match the event mask within the
295 		 * event set.
296 		 */
297 		if (event->mask != event_set[test_num].mask) {
298 			tst_res(TFAIL,
299 				"got event: mask=%llx (expected %llx) "
300 				"pid=%u fd=%d",
301 				(unsigned long long)event->mask,
302 				event_set[test_num].mask,
303 				(unsigned)event->pid, event->fd);
304 		} else if (event->pid != child_pid) {
305 			tst_res(TFAIL,
306 				"got event: mask=%llx pid=%u "
307 				"(expected %u) fd=%d",
308 				(unsigned long long)event->mask,
309 				(unsigned)event->pid,
310 				(unsigned)child_pid,
311 				event->fd);
312 		} else {
313 			tst_res(TPASS,
314 				"got event: mask=%llx pid=%u fd=%d",
315 				(unsigned long long)event->mask,
316 				(unsigned)event->pid, event->fd);
317 		}
318 
319 		/* Write response to the permission event */
320 		if (event_set[test_num].mask & LTP_ALL_PERM_EVENTS) {
321 			struct fanotify_response resp;
322 
323 			resp.fd = event->fd;
324 			resp.response = event_set[test_num].response;
325 			SAFE_WRITE(1, fd_notify, &resp, sizeof(resp));
326 		}
327 
328 		i += event->event_len;
329 
330 		if (event->fd != FAN_NOFD)
331 			SAFE_CLOSE(event->fd);
332 
333 		test_num++;
334 	}
335 
336 	for (; test_num < tc->event_count; test_num++) {
337 		tst_res(TFAIL, "didn't get event: mask=%llx",
338 			event_set[test_num].mask);
339 
340 	}
341 
342 	check_child();
343 
344 	if (fd_notify > 0)
345 		SAFE_CLOSE(fd_notify);
346 }
347 
setup(void)348 static void setup(void)
349 {
350 	sprintf(fname, MOUNT_PATH"/fname_%d", getpid());
351 	SAFE_FILE_PRINTF(fname, "1");
352 
353 	SAFE_CP(TEST_APP, FILE_EXEC_PATH);
354 }
355 
cleanup(void)356 static void cleanup(void)
357 {
358 	if (fd_notify > 0)
359 		SAFE_CLOSE(fd_notify);
360 }
361 
362 static const char *const resource_files[] = {
363 	TEST_APP,
364 	NULL
365 };
366 
367 static struct tst_test test = {
368 	.test = test_fanotify,
369 	.tcnt = ARRAY_SIZE(tcases),
370 	.setup = setup,
371 	.cleanup = cleanup,
372 	.forks_child = 1,
373 	.needs_root = 1,
374 	.mount_device = 1,
375 	.mntpoint = MOUNT_PATH,
376 	.resource_files = resource_files
377 };
378 
379 #else
380 	TST_TEST_TCONF("system doesn't have required fanotify support");
381 #endif
382