1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (c) 2021 Google. All Rights Reserved.
4f08c3bdfSopenharmony_ci *
5f08c3bdfSopenharmony_ci * Started by Matthew Bobrowski <repnop@google.com>
6f08c3bdfSopenharmony_ci */
7f08c3bdfSopenharmony_ci
8f08c3bdfSopenharmony_ci/*\
9f08c3bdfSopenharmony_ci * [Description]
10f08c3bdfSopenharmony_ci *
11f08c3bdfSopenharmony_ci * A test which verifies whether the returned struct
12f08c3bdfSopenharmony_ci * fanotify_event_info_pidfd in FAN_REPORT_PIDFD mode contains the
13f08c3bdfSopenharmony_ci * expected set of information.
14f08c3bdfSopenharmony_ci *
15f08c3bdfSopenharmony_ci * NOTE: FAN_REPORT_PIDFD support was added in v5.15-rc1 in af579beb666a
16f08c3bdfSopenharmony_ci * ("fanotify: add pidfd support to the fanotify API").
17f08c3bdfSopenharmony_ci */
18f08c3bdfSopenharmony_ci
19f08c3bdfSopenharmony_ci#define _GNU_SOURCE
20f08c3bdfSopenharmony_ci#include <stdio.h>
21f08c3bdfSopenharmony_ci#include <ctype.h>
22f08c3bdfSopenharmony_ci#include <stdlib.h>
23f08c3bdfSopenharmony_ci#include <string.h>
24f08c3bdfSopenharmony_ci#include "tst_test.h"
25f08c3bdfSopenharmony_ci#include "tst_safe_stdio.h"
26f08c3bdfSopenharmony_ci#include "tst_safe_macros.h"
27f08c3bdfSopenharmony_ci#include "lapi/pidfd.h"
28f08c3bdfSopenharmony_ci
29f08c3bdfSopenharmony_ci#ifdef HAVE_SYS_FANOTIFY_H
30f08c3bdfSopenharmony_ci#include "fanotify.h"
31f08c3bdfSopenharmony_ci
32f08c3bdfSopenharmony_ci#define BUF_SZ		4096
33f08c3bdfSopenharmony_ci#define MOUNT_PATH	"fs_mnt"
34f08c3bdfSopenharmony_ci#define TEST_FILE	MOUNT_PATH "/testfile"
35f08c3bdfSopenharmony_ci
36f08c3bdfSopenharmony_cistruct pidfd_fdinfo_t {
37f08c3bdfSopenharmony_ci	int pos;
38f08c3bdfSopenharmony_ci	int flags;
39f08c3bdfSopenharmony_ci	int mnt_id;
40f08c3bdfSopenharmony_ci	int pid;
41f08c3bdfSopenharmony_ci	int ns_pid;
42f08c3bdfSopenharmony_ci};
43f08c3bdfSopenharmony_ci
44f08c3bdfSopenharmony_cistatic struct test_case_t {
45f08c3bdfSopenharmony_ci	char *name;
46f08c3bdfSopenharmony_ci	int fork;
47f08c3bdfSopenharmony_ci	int want_pidfd_err;
48f08c3bdfSopenharmony_ci} test_cases[] = {
49f08c3bdfSopenharmony_ci	{
50f08c3bdfSopenharmony_ci		"return a valid pidfd for event created by self",
51f08c3bdfSopenharmony_ci		0,
52f08c3bdfSopenharmony_ci		0,
53f08c3bdfSopenharmony_ci	},
54f08c3bdfSopenharmony_ci	{
55f08c3bdfSopenharmony_ci		"return invalid pidfd for event created by terminated child",
56f08c3bdfSopenharmony_ci		1,
57f08c3bdfSopenharmony_ci		FAN_NOPIDFD,
58f08c3bdfSopenharmony_ci	},
59f08c3bdfSopenharmony_ci};
60f08c3bdfSopenharmony_ci
61f08c3bdfSopenharmony_cistatic int fanotify_fd;
62f08c3bdfSopenharmony_cistatic char event_buf[BUF_SZ];
63f08c3bdfSopenharmony_cistatic struct pidfd_fdinfo_t *self_pidfd_fdinfo;
64f08c3bdfSopenharmony_ci
65f08c3bdfSopenharmony_cistatic struct pidfd_fdinfo_t *read_pidfd_fdinfo(int pidfd)
66f08c3bdfSopenharmony_ci{
67f08c3bdfSopenharmony_ci	char *fdinfo_path;
68f08c3bdfSopenharmony_ci	struct pidfd_fdinfo_t *pidfd_fdinfo;
69f08c3bdfSopenharmony_ci
70f08c3bdfSopenharmony_ci	pidfd_fdinfo = SAFE_MALLOC(sizeof(struct pidfd_fdinfo_t));
71f08c3bdfSopenharmony_ci
72f08c3bdfSopenharmony_ci	SAFE_ASPRINTF(&fdinfo_path, "/proc/self/fdinfo/%d", pidfd);
73f08c3bdfSopenharmony_ci	SAFE_FILE_LINES_SCANF(fdinfo_path, "pos: %d", &pidfd_fdinfo->pos);
74f08c3bdfSopenharmony_ci	SAFE_FILE_LINES_SCANF(fdinfo_path, "flags: %d", &pidfd_fdinfo->flags);
75f08c3bdfSopenharmony_ci	SAFE_FILE_LINES_SCANF(fdinfo_path, "mnt_id: %d", &pidfd_fdinfo->mnt_id);
76f08c3bdfSopenharmony_ci	SAFE_FILE_LINES_SCANF(fdinfo_path, "Pid: %d", &pidfd_fdinfo->pid);
77f08c3bdfSopenharmony_ci	SAFE_FILE_LINES_SCANF(fdinfo_path, "NSpid: %d", &pidfd_fdinfo->ns_pid);
78f08c3bdfSopenharmony_ci
79f08c3bdfSopenharmony_ci	free(fdinfo_path);
80f08c3bdfSopenharmony_ci
81f08c3bdfSopenharmony_ci	return pidfd_fdinfo;
82f08c3bdfSopenharmony_ci}
83f08c3bdfSopenharmony_ci
84f08c3bdfSopenharmony_cistatic void generate_event(void)
85f08c3bdfSopenharmony_ci{
86f08c3bdfSopenharmony_ci	int fd;
87f08c3bdfSopenharmony_ci
88f08c3bdfSopenharmony_ci	/* Generate a single FAN_OPEN event on the watched object. */
89f08c3bdfSopenharmony_ci	fd = SAFE_OPEN(TEST_FILE, O_RDONLY);
90f08c3bdfSopenharmony_ci	SAFE_CLOSE(fd);
91f08c3bdfSopenharmony_ci}
92f08c3bdfSopenharmony_ci
93f08c3bdfSopenharmony_cistatic void do_fork(void)
94f08c3bdfSopenharmony_ci{
95f08c3bdfSopenharmony_ci	int status;
96f08c3bdfSopenharmony_ci	pid_t child;
97f08c3bdfSopenharmony_ci
98f08c3bdfSopenharmony_ci	child = SAFE_FORK();
99f08c3bdfSopenharmony_ci	if (child == 0) {
100f08c3bdfSopenharmony_ci		SAFE_CLOSE(fanotify_fd);
101f08c3bdfSopenharmony_ci		generate_event();
102f08c3bdfSopenharmony_ci		exit(EXIT_SUCCESS);
103f08c3bdfSopenharmony_ci	}
104f08c3bdfSopenharmony_ci
105f08c3bdfSopenharmony_ci	SAFE_WAITPID(child, &status, 0);
106f08c3bdfSopenharmony_ci	if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
107f08c3bdfSopenharmony_ci		tst_brk(TBROK,
108f08c3bdfSopenharmony_ci			"child process terminated incorrectly");
109f08c3bdfSopenharmony_ci}
110f08c3bdfSopenharmony_ci
111f08c3bdfSopenharmony_cistatic void do_setup(void)
112f08c3bdfSopenharmony_ci{
113f08c3bdfSopenharmony_ci	int pidfd;
114f08c3bdfSopenharmony_ci
115f08c3bdfSopenharmony_ci	SAFE_TOUCH(TEST_FILE, 0666, NULL);
116f08c3bdfSopenharmony_ci
117f08c3bdfSopenharmony_ci	/*
118f08c3bdfSopenharmony_ci	 * An explicit check for FAN_REPORT_PIDFD is performed early
119f08c3bdfSopenharmony_ci	 * on in the test initialization as it's a prerequisite for
120f08c3bdfSopenharmony_ci	 * all test cases.
121f08c3bdfSopenharmony_ci	 */
122f08c3bdfSopenharmony_ci	REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_BY_KERNEL(FAN_REPORT_PIDFD);
123f08c3bdfSopenharmony_ci
124f08c3bdfSopenharmony_ci	fanotify_fd = SAFE_FANOTIFY_INIT(FAN_REPORT_PIDFD, O_RDONLY);
125f08c3bdfSopenharmony_ci	SAFE_FANOTIFY_MARK(fanotify_fd, FAN_MARK_ADD, FAN_OPEN, AT_FDCWD,
126f08c3bdfSopenharmony_ci			   TEST_FILE);
127f08c3bdfSopenharmony_ci
128f08c3bdfSopenharmony_ci	pidfd = SAFE_PIDFD_OPEN(getpid(), 0);
129f08c3bdfSopenharmony_ci
130f08c3bdfSopenharmony_ci	self_pidfd_fdinfo = read_pidfd_fdinfo(pidfd);
131f08c3bdfSopenharmony_ci	if (self_pidfd_fdinfo == NULL) {
132f08c3bdfSopenharmony_ci		tst_brk(TBROK,
133f08c3bdfSopenharmony_ci			"pidfd=%d, failed to read pidfd fdinfo",
134f08c3bdfSopenharmony_ci			pidfd);
135f08c3bdfSopenharmony_ci	}
136f08c3bdfSopenharmony_ci}
137f08c3bdfSopenharmony_ci
138f08c3bdfSopenharmony_cistatic void do_test(unsigned int num)
139f08c3bdfSopenharmony_ci{
140f08c3bdfSopenharmony_ci	int i = 0, len;
141f08c3bdfSopenharmony_ci	struct test_case_t *tc = &test_cases[num];
142f08c3bdfSopenharmony_ci
143f08c3bdfSopenharmony_ci	tst_res(TINFO, "Test #%d: %s", num, tc->name);
144f08c3bdfSopenharmony_ci
145f08c3bdfSopenharmony_ci	/*
146f08c3bdfSopenharmony_ci	 * Generate the event in either self or a child process. Event
147f08c3bdfSopenharmony_ci	 * generation in a child process is done so that the FAN_NOPIDFD case
148f08c3bdfSopenharmony_ci	 * can be verified.
149f08c3bdfSopenharmony_ci	 */
150f08c3bdfSopenharmony_ci	if (tc->fork)
151f08c3bdfSopenharmony_ci		do_fork();
152f08c3bdfSopenharmony_ci	else
153f08c3bdfSopenharmony_ci		generate_event();
154f08c3bdfSopenharmony_ci
155f08c3bdfSopenharmony_ci	/*
156f08c3bdfSopenharmony_ci	 * Read all of the queued events into the provided event
157f08c3bdfSopenharmony_ci	 * buffer.
158f08c3bdfSopenharmony_ci	 */
159f08c3bdfSopenharmony_ci	len = SAFE_READ(0, fanotify_fd, event_buf, sizeof(event_buf));
160f08c3bdfSopenharmony_ci	while (i < len) {
161f08c3bdfSopenharmony_ci		struct fanotify_event_metadata *event;
162f08c3bdfSopenharmony_ci		struct fanotify_event_info_pidfd *info;
163f08c3bdfSopenharmony_ci		struct pidfd_fdinfo_t *event_pidfd_fdinfo = NULL;
164f08c3bdfSopenharmony_ci
165f08c3bdfSopenharmony_ci		event = (struct fanotify_event_metadata *)&event_buf[i];
166f08c3bdfSopenharmony_ci		info = (struct fanotify_event_info_pidfd *)(event + 1);
167f08c3bdfSopenharmony_ci
168f08c3bdfSopenharmony_ci		/*
169f08c3bdfSopenharmony_ci		 * Checks ensuring that pidfd information record object header
170f08c3bdfSopenharmony_ci		 * fields are set correctly.
171f08c3bdfSopenharmony_ci		 */
172f08c3bdfSopenharmony_ci		if (info->hdr.info_type != FAN_EVENT_INFO_TYPE_PIDFD) {
173f08c3bdfSopenharmony_ci			tst_res(TFAIL,
174f08c3bdfSopenharmony_ci				"unexpected info_type received in info "
175f08c3bdfSopenharmony_ci				"header (expected: %d, got: %d",
176f08c3bdfSopenharmony_ci				FAN_EVENT_INFO_TYPE_PIDFD,
177f08c3bdfSopenharmony_ci				info->hdr.info_type);
178f08c3bdfSopenharmony_ci			info = NULL;
179f08c3bdfSopenharmony_ci			goto next_event;
180f08c3bdfSopenharmony_ci		} else if (info->hdr.len !=
181f08c3bdfSopenharmony_ci			   sizeof(struct fanotify_event_info_pidfd)) {
182f08c3bdfSopenharmony_ci			tst_res(TFAIL,
183f08c3bdfSopenharmony_ci				"unexpected info object length "
184f08c3bdfSopenharmony_ci				"(expected: %lu, got: %d",
185f08c3bdfSopenharmony_ci				sizeof(struct fanotify_event_info_pidfd),
186f08c3bdfSopenharmony_ci				info->hdr.len);
187f08c3bdfSopenharmony_ci			info = NULL;
188f08c3bdfSopenharmony_ci			goto next_event;
189f08c3bdfSopenharmony_ci		}
190f08c3bdfSopenharmony_ci
191f08c3bdfSopenharmony_ci		/*
192f08c3bdfSopenharmony_ci		 * Check if pidfd information object reported any errors during
193f08c3bdfSopenharmony_ci		 * creation and whether they're expected.
194f08c3bdfSopenharmony_ci		 */
195f08c3bdfSopenharmony_ci		if (info->pidfd < 0 && !tc->want_pidfd_err) {
196f08c3bdfSopenharmony_ci			tst_res(TFAIL,
197f08c3bdfSopenharmony_ci				"pidfd creation failed for pid: %u with pidfd error value "
198f08c3bdfSopenharmony_ci				"set to: %d",
199f08c3bdfSopenharmony_ci				(unsigned int)event->pid,
200f08c3bdfSopenharmony_ci				info->pidfd);
201f08c3bdfSopenharmony_ci			goto next_event;
202f08c3bdfSopenharmony_ci		} else if (tc->want_pidfd_err &&
203f08c3bdfSopenharmony_ci			   info->pidfd != tc->want_pidfd_err) {
204f08c3bdfSopenharmony_ci			tst_res(TFAIL,
205f08c3bdfSopenharmony_ci				"pidfd set to an unexpected error: %d for pid: %u",
206f08c3bdfSopenharmony_ci				info->pidfd,
207f08c3bdfSopenharmony_ci				(unsigned int)event->pid);
208f08c3bdfSopenharmony_ci			goto next_event;
209f08c3bdfSopenharmony_ci		} else if (tc->want_pidfd_err &&
210f08c3bdfSopenharmony_ci			   info->pidfd == tc->want_pidfd_err) {
211f08c3bdfSopenharmony_ci			tst_res(TPASS,
212f08c3bdfSopenharmony_ci				"pid: %u terminated before pidfd was created, "
213f08c3bdfSopenharmony_ci				"pidfd set to the value of: %d, as expected",
214f08c3bdfSopenharmony_ci				(unsigned int)event->pid,
215f08c3bdfSopenharmony_ci				FAN_NOPIDFD);
216f08c3bdfSopenharmony_ci			goto next_event;
217f08c3bdfSopenharmony_ci		}
218f08c3bdfSopenharmony_ci
219f08c3bdfSopenharmony_ci		/*
220f08c3bdfSopenharmony_ci		 * No pidfd errors occurred, continue with verifying pidfd
221f08c3bdfSopenharmony_ci		 * fdinfo validity.
222f08c3bdfSopenharmony_ci		 */
223f08c3bdfSopenharmony_ci		event_pidfd_fdinfo = read_pidfd_fdinfo(info->pidfd);
224f08c3bdfSopenharmony_ci		if (event_pidfd_fdinfo == NULL) {
225f08c3bdfSopenharmony_ci			tst_brk(TBROK,
226f08c3bdfSopenharmony_ci				"reading fdinfo for pidfd: %d "
227f08c3bdfSopenharmony_ci				"describing pid: %u failed",
228f08c3bdfSopenharmony_ci				info->pidfd,
229f08c3bdfSopenharmony_ci				(unsigned int)event->pid);
230f08c3bdfSopenharmony_ci			goto next_event;
231f08c3bdfSopenharmony_ci		} else if (event_pidfd_fdinfo->pid != event->pid) {
232f08c3bdfSopenharmony_ci			tst_res(TFAIL,
233f08c3bdfSopenharmony_ci				"pidfd provided for incorrect pid "
234f08c3bdfSopenharmony_ci				"(expected pidfd for pid: %u, got pidfd for "
235f08c3bdfSopenharmony_ci				"pid: %u)",
236f08c3bdfSopenharmony_ci				(unsigned int)event->pid,
237f08c3bdfSopenharmony_ci				(unsigned int)event_pidfd_fdinfo->pid);
238f08c3bdfSopenharmony_ci			goto next_event;
239f08c3bdfSopenharmony_ci		} else if (memcmp(event_pidfd_fdinfo, self_pidfd_fdinfo,
240f08c3bdfSopenharmony_ci				  sizeof(struct pidfd_fdinfo_t))) {
241f08c3bdfSopenharmony_ci			tst_res(TFAIL,
242f08c3bdfSopenharmony_ci				"pidfd fdinfo values for self and event differ "
243f08c3bdfSopenharmony_ci				"(expected pos: %d, flags: %x, mnt_id: %d, "
244f08c3bdfSopenharmony_ci				"pid: %d, ns_pid: %d, got pos: %d, "
245f08c3bdfSopenharmony_ci				"flags: %x, mnt_id: %d, pid: %d, ns_pid: %d",
246f08c3bdfSopenharmony_ci				self_pidfd_fdinfo->pos,
247f08c3bdfSopenharmony_ci				self_pidfd_fdinfo->flags,
248f08c3bdfSopenharmony_ci				self_pidfd_fdinfo->mnt_id,
249f08c3bdfSopenharmony_ci				self_pidfd_fdinfo->pid,
250f08c3bdfSopenharmony_ci				self_pidfd_fdinfo->ns_pid,
251f08c3bdfSopenharmony_ci				event_pidfd_fdinfo->pos,
252f08c3bdfSopenharmony_ci				event_pidfd_fdinfo->flags,
253f08c3bdfSopenharmony_ci				event_pidfd_fdinfo->mnt_id,
254f08c3bdfSopenharmony_ci				event_pidfd_fdinfo->pid,
255f08c3bdfSopenharmony_ci				event_pidfd_fdinfo->ns_pid);
256f08c3bdfSopenharmony_ci			goto next_event;
257f08c3bdfSopenharmony_ci		} else {
258f08c3bdfSopenharmony_ci			tst_res(TPASS,
259f08c3bdfSopenharmony_ci				"got an event with a valid pidfd info record, "
260f08c3bdfSopenharmony_ci				"mask: %lld, pid: %u, fd: %d, "
261f08c3bdfSopenharmony_ci				"pidfd: %d, info_type: %d, info_len: %d",
262f08c3bdfSopenharmony_ci				(unsigned long long)event->mask,
263f08c3bdfSopenharmony_ci				(unsigned int)event->pid,
264f08c3bdfSopenharmony_ci				event->fd,
265f08c3bdfSopenharmony_ci				info->pidfd,
266f08c3bdfSopenharmony_ci				info->hdr.info_type,
267f08c3bdfSopenharmony_ci				info->hdr.len);
268f08c3bdfSopenharmony_ci		}
269f08c3bdfSopenharmony_ci
270f08c3bdfSopenharmony_cinext_event:
271f08c3bdfSopenharmony_ci		i += event->event_len;
272f08c3bdfSopenharmony_ci		if (event->fd >= 0)
273f08c3bdfSopenharmony_ci			SAFE_CLOSE(event->fd);
274f08c3bdfSopenharmony_ci
275f08c3bdfSopenharmony_ci		if (info && info->pidfd >= 0)
276f08c3bdfSopenharmony_ci			SAFE_CLOSE(info->pidfd);
277f08c3bdfSopenharmony_ci
278f08c3bdfSopenharmony_ci		if (event_pidfd_fdinfo)
279f08c3bdfSopenharmony_ci			free(event_pidfd_fdinfo);
280f08c3bdfSopenharmony_ci	}
281f08c3bdfSopenharmony_ci}
282f08c3bdfSopenharmony_ci
283f08c3bdfSopenharmony_cistatic void do_cleanup(void)
284f08c3bdfSopenharmony_ci{
285f08c3bdfSopenharmony_ci	if (fanotify_fd >= 0)
286f08c3bdfSopenharmony_ci		SAFE_CLOSE(fanotify_fd);
287f08c3bdfSopenharmony_ci
288f08c3bdfSopenharmony_ci	if (self_pidfd_fdinfo)
289f08c3bdfSopenharmony_ci		free(self_pidfd_fdinfo);
290f08c3bdfSopenharmony_ci}
291f08c3bdfSopenharmony_ci
292f08c3bdfSopenharmony_cistatic struct tst_test test = {
293f08c3bdfSopenharmony_ci	.setup = do_setup,
294f08c3bdfSopenharmony_ci	.test = do_test,
295f08c3bdfSopenharmony_ci	.tcnt = ARRAY_SIZE(test_cases),
296f08c3bdfSopenharmony_ci	.cleanup = do_cleanup,
297f08c3bdfSopenharmony_ci	.all_filesystems = 1,
298f08c3bdfSopenharmony_ci	.needs_root = 1,
299f08c3bdfSopenharmony_ci	.mntpoint = MOUNT_PATH,
300f08c3bdfSopenharmony_ci	.forks_child = 1,
301f08c3bdfSopenharmony_ci};
302f08c3bdfSopenharmony_ci
303f08c3bdfSopenharmony_ci#else
304f08c3bdfSopenharmony_ci	TST_TEST_TCONF("system doesn't have required fanotify support");
305f08c3bdfSopenharmony_ci#endif /* HAVE_SYS_FANOTIFY_H */
306