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