xref: /aosp_15_r20/external/ltp/testcases/kernel/syscalls/fanotify/fanotify14.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2018 Matthew Bobrowski. All Rights Reserved.
4  * Copyright (c) Linux Test Project, 2020-2022
5  *
6  * Started by Matthew Bobrowski <[email protected]>
7  */
8 
9 /*\
10  * [Description]
11  * This test file has been designed to ensure that the fanotify
12  * system calls fanotify_init(2) and fanotify_mark(2) return the
13  * correct error code to the calling process when an invalid flag or
14  * mask value has been specified in conjunction with FAN_REPORT_FID.
15  */
16 
17 /*
18  * The ENOTDIR test cases are regression tests for commits:
19  *
20  *     ceaf69f8eadc fanotify: do not allow setting dirent events in mask of non-dir
21  *     8698e3bab4dd fanotify: refine the validation checks on non-dir inode mask
22  *
23  * The pipes test cases are regression tests for commit:
24  *     69562eb0bd3e fanotify: disallow mount/sb marks on kernel internal pseudo fs
25  */
26 
27 #define _GNU_SOURCE
28 #include "tst_test.h"
29 #include <errno.h>
30 
31 #ifdef HAVE_SYS_FANOTIFY_H
32 #include "fanotify.h"
33 
34 #define MNTPOINT "mntpoint"
35 #define FILE1 MNTPOINT"/file1"
36 
37 /*
38  * List of inode events that are only available when notification group is
39  * set to report fid.
40  */
41 #define INODE_EVENTS (FAN_ATTRIB | FAN_CREATE | FAN_DELETE | FAN_MOVE | \
42 		      FAN_DELETE_SELF | FAN_MOVE_SELF)
43 
44 #define FLAGS_DESC(flags) {(flags), (#flags)}
45 
46 static int pipes[2] = {-1, -1};
47 static int fanotify_fd;
48 static int ignore_mark_unsupported;
49 static int filesystem_mark_unsupported;
50 static int se_enforcing;
51 static unsigned int supported_init_flags;
52 
53 struct test_case_flags_t {
54 	unsigned long long flags;
55 	const char *desc;
56 };
57 
58 /*
59  * Each test case has been designed in a manner whereby the values defined
60  * within should result in the interface to return an error to the calling
61  * process.
62  */
63 static struct test_case_t {
64 	struct test_case_flags_t init;
65 	struct test_case_flags_t mark;
66 	/* when mask.flags == 0, fanotify_init() is expected to fail */
67 	struct test_case_flags_t mask;
68 	int expected_errno;
69 	int *pfd;
70 } test_cases[] = {
71 	/* FAN_REPORT_FID without class FAN_CLASS_NOTIF is not valid */
72 	{
73 		.init = FLAGS_DESC(FAN_CLASS_CONTENT | FAN_REPORT_FID),
74 		.expected_errno = EINVAL,
75 	},
76 
77 	/* FAN_REPORT_FID without class FAN_CLASS_NOTIF is not valid */
78 	{
79 		.init = FLAGS_DESC(FAN_CLASS_PRE_CONTENT | FAN_REPORT_FID),
80 		.expected_errno = EINVAL,
81 	},
82 
83 	/* INODE_EVENTS in mask without class FAN_REPORT_FID are not valid */
84 	{
85 		.init = FLAGS_DESC(FAN_CLASS_NOTIF),
86 		.mark = FLAGS_DESC(FAN_MARK_INODE),
87 		.mask = FLAGS_DESC(INODE_EVENTS),
88 		.expected_errno = EINVAL,
89 	},
90 
91 	/* INODE_EVENTS in mask with FAN_MARK_MOUNT are not valid */
92 	{
93 		.init = FLAGS_DESC(FAN_CLASS_NOTIF | FAN_REPORT_FID),
94 		.mark = FLAGS_DESC(FAN_MARK_MOUNT),
95 		.mask = FLAGS_DESC(INODE_EVENTS),
96 		.expected_errno = EINVAL,
97 	},
98 
99 	/* FAN_REPORT_NAME without FAN_REPORT_DIR_FID is not valid */
100 	{
101 		.init = FLAGS_DESC(FAN_CLASS_NOTIF | FAN_REPORT_NAME),
102 		.expected_errno = EINVAL,
103 	},
104 
105 	/* FAN_REPORT_NAME without FAN_REPORT_DIR_FID is not valid */
106 	{
107 		.init = FLAGS_DESC(FAN_CLASS_NOTIF | FAN_REPORT_FID | FAN_REPORT_NAME),
108 		.expected_errno = EINVAL,
109 	},
110 
111 	/* FAN_REPORT_TARGET_FID without FAN_REPORT_FID is not valid */
112 	{
113 		.init = FLAGS_DESC(FAN_CLASS_NOTIF | FAN_REPORT_TARGET_FID | FAN_REPORT_DFID_NAME),
114 		.expected_errno = EINVAL,
115 	},
116 
117 	/* FAN_REPORT_TARGET_FID without FAN_REPORT_NAME is not valid */
118 	{
119 		.init = FLAGS_DESC(FAN_CLASS_NOTIF | FAN_REPORT_TARGET_FID | FAN_REPORT_DFID_FID),
120 		.expected_errno = EINVAL,
121 	},
122 
123 	/* FAN_RENAME without FAN_REPORT_NAME is not valid */
124 	{
125 		.init = FLAGS_DESC(FAN_CLASS_NOTIF | FAN_REPORT_DFID_FID),
126 		.mark = FLAGS_DESC(FAN_MARK_INODE),
127 		.mask = FLAGS_DESC(FAN_RENAME),
128 		.expected_errno = EINVAL,
129 	},
130 
131 	/* With FAN_MARK_ONLYDIR on non-dir is not valid */
132 	{
133 		.init = FLAGS_DESC(FAN_CLASS_NOTIF),
134 		.mark = FLAGS_DESC(FAN_MARK_ONLYDIR),
135 		.mask = FLAGS_DESC(FAN_OPEN),
136 		.expected_errno = ENOTDIR,
137 	},
138 
139 	/* With FAN_REPORT_TARGET_FID, FAN_DELETE on non-dir is not valid */
140 	{
141 		.init = FLAGS_DESC(FAN_CLASS_NOTIF | FAN_REPORT_DFID_NAME_TARGET),
142 		.mark = FLAGS_DESC(FAN_MARK_INODE),
143 		.mask = FLAGS_DESC(FAN_DELETE),
144 		.expected_errno = ENOTDIR,
145 	},
146 
147 	/* With FAN_REPORT_TARGET_FID, FAN_RENAME on non-dir is not valid */
148 	{
149 		.init = FLAGS_DESC(FAN_CLASS_NOTIF | FAN_REPORT_DFID_NAME_TARGET),
150 		.mark = FLAGS_DESC(FAN_MARK_INODE),
151 		.mask = FLAGS_DESC(FAN_RENAME),
152 		.expected_errno = ENOTDIR,
153 	},
154 
155 	/* With FAN_REPORT_TARGET_FID, FAN_ONDIR on non-dir is not valid */
156 	{
157 		.init = FLAGS_DESC(FAN_CLASS_NOTIF | FAN_REPORT_DFID_NAME_TARGET),
158 		.mark = FLAGS_DESC(FAN_MARK_INODE),
159 		.mask = FLAGS_DESC(FAN_OPEN | FAN_ONDIR),
160 		.expected_errno = ENOTDIR,
161 	},
162 
163 	/* With FAN_REPORT_TARGET_FID, FAN_EVENT_ON_CHILD on non-dir is not valid */
164 	{
165 		.init = FLAGS_DESC(FAN_CLASS_NOTIF | FAN_REPORT_DFID_NAME_TARGET),
166 		.mark = FLAGS_DESC(FAN_MARK_INODE),
167 		.mask = FLAGS_DESC(FAN_OPEN | FAN_EVENT_ON_CHILD),
168 		.expected_errno = ENOTDIR,
169 	},
170 
171 	/* FAN_MARK_IGNORE_SURV with FAN_DELETE on non-dir is not valid */
172 	{
173 		.init = FLAGS_DESC(FAN_CLASS_NOTIF | FAN_REPORT_DFID_NAME),
174 		.mark = FLAGS_DESC(FAN_MARK_IGNORE_SURV),
175 		.mask = FLAGS_DESC(FAN_DELETE),
176 		.expected_errno = ENOTDIR,
177 	},
178 
179 	/* FAN_MARK_IGNORE_SURV with FAN_RENAME on non-dir is not valid */
180 	{
181 		.init = FLAGS_DESC(FAN_CLASS_NOTIF | FAN_REPORT_DFID_NAME),
182 		.mark = FLAGS_DESC(FAN_MARK_IGNORE_SURV),
183 		.mask = FLAGS_DESC(FAN_RENAME),
184 		.expected_errno = ENOTDIR,
185 	},
186 
187 	/* FAN_MARK_IGNORE_SURV with FAN_ONDIR on non-dir is not valid */
188 	{
189 		.init = FLAGS_DESC(FAN_CLASS_NOTIF | FAN_REPORT_DFID_NAME),
190 		.mark = FLAGS_DESC(FAN_MARK_IGNORE_SURV),
191 		.mask = FLAGS_DESC(FAN_OPEN | FAN_ONDIR),
192 		.expected_errno = ENOTDIR,
193 	},
194 
195 	/* FAN_MARK_IGNORE_SURV with FAN_EVENT_ON_CHILD on non-dir is not valid */
196 	{
197 		.init = FLAGS_DESC(FAN_CLASS_NOTIF | FAN_REPORT_DFID_NAME),
198 		.mark = FLAGS_DESC(FAN_MARK_IGNORE_SURV),
199 		.mask = FLAGS_DESC(FAN_OPEN | FAN_EVENT_ON_CHILD),
200 		.expected_errno = ENOTDIR,
201 	},
202 
203 	/* FAN_MARK_IGNORE without FAN_MARK_IGNORED_SURV_MODIFY on directory is not valid */
204 	{
205 		.init = FLAGS_DESC(FAN_CLASS_NOTIF),
206 		.mark = FLAGS_DESC(FAN_MARK_IGNORE),
207 		.mask = FLAGS_DESC(FAN_OPEN),
208 		.expected_errno = EISDIR,
209 	},
210 
211 	/* FAN_MARK_IGNORE without FAN_MARK_IGNORED_SURV_MODIFY on mount mark is not valid */
212 	{
213 		.init = FLAGS_DESC(FAN_CLASS_NOTIF),
214 		.mark = FLAGS_DESC(FAN_MARK_MOUNT | FAN_MARK_IGNORE),
215 		.mask = FLAGS_DESC(FAN_OPEN),
216 		.expected_errno = EINVAL,
217 	},
218 
219 	/* FAN_MARK_IGNORE without FAN_MARK_IGNORED_SURV_MODIFY on filesystem mark is not valid */
220 	{
221 		.init = FLAGS_DESC(FAN_CLASS_NOTIF),
222 		.mark = FLAGS_DESC(FAN_MARK_FILESYSTEM | FAN_MARK_IGNORE),
223 		.mask = FLAGS_DESC(FAN_OPEN),
224 		.expected_errno = EINVAL,
225 	},
226 	/* mount mark on anonymous pipe is not valid */
227 	{
228 		.init = FLAGS_DESC(FAN_CLASS_NOTIF),
229 		.mark = FLAGS_DESC(FAN_MARK_MOUNT),
230 		.mask = { FAN_ACCESS, "anonymous pipe"},
231 		.pfd = pipes,
232 		.expected_errno = EINVAL,
233 	},
234 	/* filesystem mark on anonymous pipe is not valid */
235 	{
236 		.init = FLAGS_DESC(FAN_CLASS_NOTIF),
237 		.mark = FLAGS_DESC(FAN_MARK_FILESYSTEM),
238 		.mask = { FAN_ACCESS, "anonymous pipe"},
239 		.pfd = pipes,
240 		.expected_errno = EINVAL,
241 	},
242 };
243 
do_test(unsigned int number)244 static void do_test(unsigned int number)
245 {
246 	struct test_case_t *tc = &test_cases[number];
247 
248 	tst_res(TINFO, "Test case %d: fanotify_init(%s, O_RDONLY)", number,
249 		tc->init.desc);
250 
251 	if (tc->init.flags & ~supported_init_flags) {
252 		tst_res(TCONF, "Unsupported init flags");
253 		return;
254 	}
255 
256 	if (ignore_mark_unsupported && tc->mark.flags & FAN_MARK_IGNORE) {
257 		tst_res(TCONF, "FAN_MARK_IGNORE not supported in kernel?");
258 		return;
259 	}
260 
261 	if (!tc->mask.flags && tc->expected_errno) {
262 		TST_EXP_FAIL(fanotify_init(tc->init.flags, O_RDONLY),
263 			tc->expected_errno);
264 	} else {
265 		TST_EXP_FD(fanotify_init(tc->init.flags, O_RDONLY));
266 	}
267 
268 	fanotify_fd = TST_RET;
269 
270 	if (fanotify_fd < 0)
271 		return;
272 
273 	if (!tc->mask.flags)
274 		goto out;
275 
276 	/* Set mark on non-dir only when expecting error ENOTDIR */
277 	const char *path = tc->expected_errno == ENOTDIR ? FILE1 : MNTPOINT;
278 	const int exp_errs[] = {tc->expected_errno, EACCES};
279 	int dirfd = AT_FDCWD;
280 
281 	if (tc->pfd) {
282 		dirfd = tc->pfd[0];
283 		path = NULL;
284 	}
285 
286 	tst_res(TINFO, "Testing %s with %s",
287 		tc->mark.desc, tc->mask.desc);
288 
289 	TST_EXP_FAIL_ARR(fanotify_mark(fanotify_fd, FAN_MARK_ADD | tc->mark.flags,
290 			 tc->mask.flags, dirfd, path), exp_errs, se_enforcing ? 2 : 1);
291 
292 	/*
293 	 * ENOTDIR are errors for events/flags not allowed on a non-dir inode.
294 	 * Try to set an inode mark on a directory and it should succeed.
295 	 * Try to set directory events in filesystem mark mask on non-dir
296 	 * and it should succeed.
297 	 */
298 	if (TST_PASS && tc->expected_errno == ENOTDIR) {
299 		SAFE_FANOTIFY_MARK(fanotify_fd, FAN_MARK_ADD | tc->mark.flags,
300 				   tc->mask.flags, AT_FDCWD, MNTPOINT);
301 		tst_res(TPASS,
302 			"Adding an inode mark on directory did not fail with "
303 			"ENOTDIR error as on non-dir inode");
304 
305 		if (!(tc->mark.flags & FAN_MARK_ONLYDIR) && !filesystem_mark_unsupported) {
306 			SAFE_FANOTIFY_MARK(fanotify_fd, FAN_MARK_ADD | tc->mark.flags |
307 					   FAN_MARK_FILESYSTEM, tc->mask.flags,
308 					   AT_FDCWD, FILE1);
309 			tst_res(TPASS,
310 				"Adding a filesystem mark on non-dir did not fail with "
311 				"ENOTDIR error as with an inode mark");
312 		}
313 	}
314 
315 out:
316 	if (fanotify_fd > 0)
317 		SAFE_CLOSE(fanotify_fd);
318 }
319 
do_setup(void)320 static void do_setup(void)
321 {
322 	unsigned int all_init_flags = FAN_REPORT_DFID_NAME_TARGET |
323 		FAN_CLASS_NOTIF | FAN_CLASS_CONTENT | FAN_CLASS_PRE_CONTENT;
324 
325 	/* Require FAN_REPORT_FID support for all tests to simplify per test case requirements */
326 	REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_ON_FS(FAN_REPORT_FID, MNTPOINT);
327 	supported_init_flags = fanotify_get_supported_init_flags(all_init_flags, MNTPOINT);
328 
329 	ignore_mark_unsupported = fanotify_mark_supported_on_fs(FAN_MARK_IGNORE_SURV,
330 								MNTPOINT);
331 	filesystem_mark_unsupported =
332 		fanotify_flags_supported_on_fs(FAN_REPORT_FID, FAN_MARK_FILESYSTEM, FAN_OPEN,
333 						MNTPOINT);
334 
335 	/* Create temporary test file to place marks on */
336 	SAFE_FILE_PRINTF(FILE1, "0");
337 	/* Create anonymous pipes to place marks on */
338 	SAFE_PIPE2(pipes, O_CLOEXEC);
339 
340 	se_enforcing = tst_selinux_enforcing();
341 }
342 
do_cleanup(void)343 static void do_cleanup(void)
344 {
345 	if (fanotify_fd > 0)
346 		SAFE_CLOSE(fanotify_fd);
347 	if (pipes[0] != -1)
348 		SAFE_CLOSE(pipes[0]);
349 	if (pipes[1] != -1)
350 		SAFE_CLOSE(pipes[1]);
351 }
352 
353 static struct tst_test test = {
354 	.needs_root = 1,
355 	.test = do_test,
356 	.tcnt = ARRAY_SIZE(test_cases),
357 	.setup = do_setup,
358 	.cleanup = do_cleanup,
359 	.mount_device = 1,
360 	.mntpoint = MNTPOINT,
361 	.all_filesystems = 1,
362 	.tags = (const struct tst_tag[]) {
363 		{"linux-git", "ceaf69f8eadc"},
364 		{"linux-git", "8698e3bab4dd"},
365 		{"linux-git", "69562eb0bd3e"},
366 		{}
367 	}
368 };
369 
370 #else
371 	TST_TEST_TCONF("System does not have required fanotify support");
372 #endif
373