• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2018 Matthew Bobrowski. All Rights Reserved.
4  *
5  * Started by Matthew Bobrowski <mbobrowski@mbobrowski.org>
6  *
7  * DESCRIPTION
8  *	Validate that the newly introduced FAN_OPEN_EXEC mask functions as
9  *	expected. The idea is to generate a sequence of open related
10  *	actions to ensure that the correct event flags are being set
11  *	depending on what event mask was requested when the object was
12  *	marked.
13  */
14 #define _GNU_SOURCE
15 #include "config.h"
16 
17 #include <stdio.h>
18 #include <errno.h>
19 #include <stdlib.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <sys/wait.h>
23 #include "tst_test.h"
24 #include "fanotify.h"
25 
26 #if defined(HAVE_SYS_FANOTIFY_H)
27 #include <sys/fanotify.h>
28 
29 #define EVENT_MAX 1024
30 #define EVENT_SIZE (sizeof (struct fanotify_event_metadata))
31 #define EVENT_BUF_LEN (EVENT_MAX * EVENT_SIZE)
32 #define EVENT_SET_BUF 32
33 
34 #define BUF_SIZE 256
35 #define TEST_APP "fanotify_child"
36 
37 static pid_t child_pid;
38 static char fname[BUF_SIZE];
39 static volatile int fd_notify;
40 static volatile int complete;
41 static char event_buf[EVENT_BUF_LEN];
42 
43 static struct test_case_t {
44 	const char *tname;
45 	struct fanotify_mark_type mark;
46 	unsigned long long mask;
47 	unsigned long long ignore_mask;
48 	int event_count;
49 	unsigned long long event_set[EVENT_SET_BUF];
50 } test_cases[] = {
51 	{
52 		"inode mark, FAN_OPEN events",
53 		INIT_FANOTIFY_MARK_TYPE(INODE),
54 		FAN_OPEN,
55 		0,
56 		2,
57 		{FAN_OPEN, FAN_OPEN}
58 	},
59 	{
60 		"inode mark, FAN_OPEN_EXEC events",
61 		INIT_FANOTIFY_MARK_TYPE(INODE),
62 		FAN_OPEN_EXEC,
63 		0,
64 		1,
65 		{FAN_OPEN_EXEC}
66 	},
67 	{
68 		"inode mark, FAN_OPEN | FAN_OPEN_EXEC events",
69 		INIT_FANOTIFY_MARK_TYPE(INODE),
70 		FAN_OPEN | FAN_OPEN_EXEC,
71 		0,
72 		2,
73 		{FAN_OPEN, FAN_OPEN | FAN_OPEN_EXEC}
74 	},
75 	{
76 		"inode mark, FAN_OPEN events, ignore FAN_OPEN_EXEC",
77 		INIT_FANOTIFY_MARK_TYPE(INODE),
78 		FAN_OPEN,
79 		FAN_OPEN_EXEC,
80 		2,
81 		{FAN_OPEN, FAN_OPEN}
82 	},
83 	{
84 		"inode mark, FAN_OPEN_EXEC events, ignore FAN_OPEN",
85 		INIT_FANOTIFY_MARK_TYPE(INODE),
86 		FAN_OPEN_EXEC,
87 		FAN_OPEN,
88 		1,
89 		{FAN_OPEN_EXEC}
90 	},
91 	{
92 		"inode mark, FAN_OPEN | FAN_OPEN_EXEC events, ignore "
93 		"FAN_OPEN_EXEC",
94 		INIT_FANOTIFY_MARK_TYPE(INODE),
95 		FAN_OPEN | FAN_OPEN_EXEC,
96 		FAN_OPEN_EXEC,
97 		2,
98 		{FAN_OPEN, FAN_OPEN}
99 	}
100 };
101 
generate_events(void)102 static int generate_events(void)
103 {
104 	int fd, status;
105 
106 	child_pid = SAFE_FORK();
107 
108 	/*
109 	 * Generate a sequence of events
110 	 */
111 	if (child_pid == 0) {
112 		SAFE_CLOSE(fd_notify);
113 
114 		fd = SAFE_OPEN(fname, O_RDONLY);
115 
116 		if (fd > 0)
117 			SAFE_CLOSE(fd);
118 
119 		SAFE_EXECL(TEST_APP, TEST_APP, NULL);
120 		exit(1);
121 	}
122 
123 	SAFE_WAITPID(child_pid, &status, 0);
124 
125 	if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
126 		return 1;
127 	return 0;
128 }
129 
setup_mark(unsigned int n)130 static int setup_mark(unsigned int n)
131 {
132 	unsigned int i = 0;
133 	struct test_case_t *tc = &test_cases[n];
134 	struct fanotify_mark_type *mark = &tc->mark;
135 	const char *const files[] = {fname, TEST_APP};
136 
137 	tst_res(TINFO, "Test #%d: %s", n, tc->tname);
138 	fd_notify = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF, O_RDONLY);
139 
140 	for (; i < ARRAY_SIZE(files); i++) {
141 		/* Setup normal mark on object */
142 		if (fanotify_mark(fd_notify, FAN_MARK_ADD | mark->flag,
143 					tc->mask, AT_FDCWD, files[i]) < 0) {
144 			if (errno == EINVAL && tc->mask & FAN_OPEN_EXEC) {
145 				tst_res(TCONF,
146 					"FAN_OPEN_EXEC not supported in "
147 					"kernel?");
148 				return -1;
149 			} else if (errno == EINVAL) {
150 				tst_brk(TCONF | TERRNO,
151 					"CONFIG_FANOTIFY_ACCESS_PERMISSIONS "
152 					"not configured in kernel?");
153 			}else {
154 				tst_brk(TBROK | TERRNO,
155 					"fanotify_mark(%d, FAN_MARK_ADD | %s, "
156 					"%llx, AT_FDCWD, %s) failed",
157 					fd_notify,
158 					mark->name,
159 					tc->mask,
160 					files[i]);
161 			}
162 		}
163 
164 		/* Setup ignore mark on object */
165 		if (tc->ignore_mask) {
166 			if (fanotify_mark(fd_notify, FAN_MARK_ADD | mark->flag
167 						| FAN_MARK_IGNORED_MASK,
168 						tc->ignore_mask, AT_FDCWD,
169 						files[i]) < 0) {
170 				if (errno == EINVAL &&
171 					tc->ignore_mask & FAN_OPEN_EXEC) {
172 					tst_res(TCONF,
173 						"FAN_OPEN_EXEC not supported "
174 						"in kernel?");
175 					return -1;
176 				} else if (errno == EINVAL) {
177 					tst_brk(TCONF | TERRNO,
178 						"CONFIG_FANOTIFY_ACCESS_"
179 						"PERMISSIONS not configured in "
180 						"kernel?");
181 				} else {
182 					tst_brk(TBROK | TERRNO,
183 						"fanotify_mark (%d, "
184 						"FAN_MARK_ADD | %s "
185 						"| FAN_MARK_IGNORED_MASK, "
186 						"%llx, AT_FDCWD, %s) failed",
187 						fd_notify, mark->name,
188 						tc->ignore_mask, files[i]);
189 				}
190 			}
191 		}
192 	}
193 
194 	return 0;
195 }
196 
do_test(unsigned int n)197 static void do_test(unsigned int n)
198 {
199 	int len = 0, event_num = 0;
200 	struct test_case_t *tc = &test_cases[n];
201 	struct fanotify_event_metadata *event;
202 
203 	/* Place a mark on the object */
204 	if (setup_mark(n) != 0)
205 		goto cleanup;
206 
207 	/* Generate events in child process */
208 	if (!generate_events())
209 		goto cleanup;
210 
211 	/* Read available events into buffer */
212 	len = SAFE_READ(0, fd_notify, event_buf, EVENT_BUF_LEN);
213 
214 	event = (struct fanotify_event_metadata *) event_buf;
215 
216 	/* Process events */
217 	while (FAN_EVENT_OK(event, len) && event_num < tc->event_count) {
218 		if (event->mask != *(tc->event_set + event_num)) {
219 			tst_res(TFAIL,
220 				"Received event: mask=%llx (expected %llx, "
221 				"pid=%u, fd=%d",
222 				(unsigned long long) event->mask,
223 				*(tc->event_set + event_num),
224 				(unsigned) event->pid,
225 				event->fd);
226 		} else if (event->pid != child_pid) {
227 			tst_res(TFAIL,
228 				"Received event: mask=%llx, pid=%u (expected "
229 				"%u), fd=%d",
230 				(unsigned long long) event->mask,
231 				(unsigned) event->pid,
232 				(unsigned) child_pid,
233 				event->fd);
234 		} else {
235 			tst_res(TPASS,
236 				"Received event: mask=%llx, pid=%u, fd=%d",
237 				(unsigned long long) event->mask,
238 				(unsigned) event->pid,
239 				event->fd);
240 		}
241 
242 		if (event->fd != FAN_NOFD)
243 			SAFE_CLOSE(event->fd);
244 
245 		event_num++;
246 		event = FAN_EVENT_NEXT(event, len);
247 	}
248 
249 cleanup:
250 	if (fd_notify > 0)
251 		SAFE_CLOSE(fd_notify);
252 }
253 
do_setup(void)254 static void do_setup(void)
255 {
256 	sprintf(fname, "fname_%d", getpid());
257 	SAFE_FILE_PRINTF(fname, "1");
258 }
259 
do_cleanup(void)260 static void do_cleanup(void)
261 {
262 	if (fd_notify > 0)
263 		SAFE_CLOSE(fd_notify);
264 }
265 
266 static const char *const resource_files[] = {
267 	TEST_APP,
268 	NULL
269 };
270 
271 static struct tst_test test = {
272 	.setup = do_setup,
273 	.test = do_test,
274 	.tcnt = ARRAY_SIZE(test_cases),
275 	.cleanup = do_cleanup,
276 	.forks_child = 1,
277 	.needs_root = 1,
278 	.resource_files = resource_files
279 };
280 #else
281 	TST_TEST_TCONF("System does not contain required fanotify support");
282 #endif
283