1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (c) 2017 SUSE.  All Rights Reserved.
4f08c3bdfSopenharmony_ci *
5f08c3bdfSopenharmony_ci * Started by Jan Kara <jack@suse.cz>
6f08c3bdfSopenharmony_ci */
7f08c3bdfSopenharmony_ci
8f08c3bdfSopenharmony_ci/*\
9f08c3bdfSopenharmony_ci * [Description]
10f08c3bdfSopenharmony_ci * Check that fanotify permission events are handled properly on instance destruction.
11f08c3bdfSopenharmony_ci */
12f08c3bdfSopenharmony_ci
13f08c3bdfSopenharmony_ci/*
14f08c3bdfSopenharmony_ci * Kernel crashes should be fixed by:
15f08c3bdfSopenharmony_ci *  96d41019e3ac "fanotify: fix list corruption in fanotify_get_response()"
16f08c3bdfSopenharmony_ci *
17f08c3bdfSopenharmony_ci * Kernel hangs should be fixed by:
18f08c3bdfSopenharmony_ci *  05f0e38724e8 "fanotify: Release SRCU lock when waiting for userspace response"
19f08c3bdfSopenharmony_ci */
20f08c3bdfSopenharmony_ci
21f08c3bdfSopenharmony_ci#define _GNU_SOURCE
22f08c3bdfSopenharmony_ci#include "config.h"
23f08c3bdfSopenharmony_ci
24f08c3bdfSopenharmony_ci#include <stdio.h>
25f08c3bdfSopenharmony_ci#include <unistd.h>
26f08c3bdfSopenharmony_ci#include <stdlib.h>
27f08c3bdfSopenharmony_ci#include <sys/stat.h>
28f08c3bdfSopenharmony_ci#include <sys/types.h>
29f08c3bdfSopenharmony_ci#include <sys/wait.h>
30f08c3bdfSopenharmony_ci#include <errno.h>
31f08c3bdfSopenharmony_ci#include <string.h>
32f08c3bdfSopenharmony_ci#include <signal.h>
33f08c3bdfSopenharmony_ci#include <sys/syscall.h>
34f08c3bdfSopenharmony_ci#include "tst_test.h"
35f08c3bdfSopenharmony_ci#include "lapi/syscalls.h"
36f08c3bdfSopenharmony_ci
37f08c3bdfSopenharmony_ci#ifdef HAVE_SYS_FANOTIFY_H
38f08c3bdfSopenharmony_ci#include "fanotify.h"
39f08c3bdfSopenharmony_ci
40f08c3bdfSopenharmony_ci#define BUF_SIZE 256
41f08c3bdfSopenharmony_cistatic char fname[BUF_SIZE];
42f08c3bdfSopenharmony_cistatic char buf[BUF_SIZE];
43f08c3bdfSopenharmony_cistatic volatile int fd_notify;
44f08c3bdfSopenharmony_ci
45f08c3bdfSopenharmony_ci/* Number of children we start */
46f08c3bdfSopenharmony_ci#define MAX_CHILDREN 16
47f08c3bdfSopenharmony_cistatic pid_t child_pid[MAX_CHILDREN];
48f08c3bdfSopenharmony_ci
49f08c3bdfSopenharmony_ci/* Number of children we don't respond to before stopping */
50f08c3bdfSopenharmony_ci#define MAX_NOT_RESPONDED 4
51f08c3bdfSopenharmony_ci
52f08c3bdfSopenharmony_cistatic void generate_events(void)
53f08c3bdfSopenharmony_ci{
54f08c3bdfSopenharmony_ci	int fd;
55f08c3bdfSopenharmony_ci
56f08c3bdfSopenharmony_ci	/*
57f08c3bdfSopenharmony_ci	 * generate sequence of events
58f08c3bdfSopenharmony_ci	 */
59f08c3bdfSopenharmony_ci	fd = SAFE_OPEN(fname, O_RDWR | O_CREAT, 0700);
60f08c3bdfSopenharmony_ci
61f08c3bdfSopenharmony_ci	/* Run until killed... */
62f08c3bdfSopenharmony_ci	while (1) {
63f08c3bdfSopenharmony_ci		SAFE_LSEEK(fd, 0, SEEK_SET);
64f08c3bdfSopenharmony_ci		SAFE_READ(0, fd, buf, BUF_SIZE);
65f08c3bdfSopenharmony_ci	}
66f08c3bdfSopenharmony_ci}
67f08c3bdfSopenharmony_ci
68f08c3bdfSopenharmony_cistatic void run_children(void)
69f08c3bdfSopenharmony_ci{
70f08c3bdfSopenharmony_ci	int i;
71f08c3bdfSopenharmony_ci
72f08c3bdfSopenharmony_ci	for (i = 0; i < MAX_CHILDREN; i++) {
73f08c3bdfSopenharmony_ci		child_pid[i] = SAFE_FORK();
74f08c3bdfSopenharmony_ci		if (!child_pid[i]) {
75f08c3bdfSopenharmony_ci			/* Child will generate events now */
76f08c3bdfSopenharmony_ci			SAFE_CLOSE(fd_notify);
77f08c3bdfSopenharmony_ci			generate_events();
78f08c3bdfSopenharmony_ci			exit(0);
79f08c3bdfSopenharmony_ci		}
80f08c3bdfSopenharmony_ci	}
81f08c3bdfSopenharmony_ci}
82f08c3bdfSopenharmony_ci
83f08c3bdfSopenharmony_cistatic int stop_children(void)
84f08c3bdfSopenharmony_ci{
85f08c3bdfSopenharmony_ci	int child_ret;
86f08c3bdfSopenharmony_ci	int i, ret = 0;
87f08c3bdfSopenharmony_ci
88f08c3bdfSopenharmony_ci	for (i = 0; i < MAX_CHILDREN; i++) {
89f08c3bdfSopenharmony_ci		if (!child_pid[i])
90f08c3bdfSopenharmony_ci			continue;
91f08c3bdfSopenharmony_ci
92f08c3bdfSopenharmony_ci		SAFE_KILL(child_pid[i], SIGKILL);
93f08c3bdfSopenharmony_ci	}
94f08c3bdfSopenharmony_ci
95f08c3bdfSopenharmony_ci	for (i = 0; i < MAX_CHILDREN; i++) {
96f08c3bdfSopenharmony_ci		if (!child_pid[i])
97f08c3bdfSopenharmony_ci			continue;
98f08c3bdfSopenharmony_ci
99f08c3bdfSopenharmony_ci		SAFE_WAITPID(child_pid[i], &child_ret, 0);
100f08c3bdfSopenharmony_ci
101f08c3bdfSopenharmony_ci		if (!WIFSIGNALED(child_ret))
102f08c3bdfSopenharmony_ci			ret = 1;
103f08c3bdfSopenharmony_ci
104f08c3bdfSopenharmony_ci		child_pid[i] = 0;
105f08c3bdfSopenharmony_ci	}
106f08c3bdfSopenharmony_ci
107f08c3bdfSopenharmony_ci	return ret;
108f08c3bdfSopenharmony_ci}
109f08c3bdfSopenharmony_ci
110f08c3bdfSopenharmony_cistatic int setup_instance(void)
111f08c3bdfSopenharmony_ci{
112f08c3bdfSopenharmony_ci	int fd;
113f08c3bdfSopenharmony_ci
114f08c3bdfSopenharmony_ci	fd = SAFE_FANOTIFY_INIT(FAN_CLASS_CONTENT, O_RDONLY);
115f08c3bdfSopenharmony_ci	SAFE_FANOTIFY_MARK(fd, FAN_MARK_ADD, FAN_ACCESS_PERM, AT_FDCWD, fname);
116f08c3bdfSopenharmony_ci
117f08c3bdfSopenharmony_ci	return fd;
118f08c3bdfSopenharmony_ci}
119f08c3bdfSopenharmony_ci
120f08c3bdfSopenharmony_cistatic void loose_fanotify_events(void)
121f08c3bdfSopenharmony_ci{
122f08c3bdfSopenharmony_ci	int not_responded = 0;
123f08c3bdfSopenharmony_ci
124f08c3bdfSopenharmony_ci	/*
125f08c3bdfSopenharmony_ci	 * check events
126f08c3bdfSopenharmony_ci	 */
127f08c3bdfSopenharmony_ci	while (not_responded < MAX_NOT_RESPONDED) {
128f08c3bdfSopenharmony_ci		struct fanotify_event_metadata event;
129f08c3bdfSopenharmony_ci		struct fanotify_response resp;
130f08c3bdfSopenharmony_ci
131f08c3bdfSopenharmony_ci		/* Get more events */
132f08c3bdfSopenharmony_ci		SAFE_READ(1, fd_notify, &event, sizeof(event));
133f08c3bdfSopenharmony_ci
134f08c3bdfSopenharmony_ci		if (event.mask != FAN_ACCESS_PERM) {
135f08c3bdfSopenharmony_ci			tst_res(TFAIL,
136f08c3bdfSopenharmony_ci				"got event: mask=%llx (expected %llx) "
137f08c3bdfSopenharmony_ci				"pid=%u fd=%d",
138f08c3bdfSopenharmony_ci				(unsigned long long)event.mask,
139f08c3bdfSopenharmony_ci				(unsigned long long)FAN_ACCESS_PERM,
140f08c3bdfSopenharmony_ci				(unsigned int)event.pid, event.fd);
141f08c3bdfSopenharmony_ci			break;
142f08c3bdfSopenharmony_ci		}
143f08c3bdfSopenharmony_ci
144f08c3bdfSopenharmony_ci		/* We respond to permission event with 95% percent probability. */
145f08c3bdfSopenharmony_ci		if (random() % 100 > 5) {
146f08c3bdfSopenharmony_ci			/* Write response to permission event */
147f08c3bdfSopenharmony_ci			resp.fd = event.fd;
148f08c3bdfSopenharmony_ci			resp.response = FAN_ALLOW;
149f08c3bdfSopenharmony_ci			SAFE_WRITE(SAFE_WRITE_ALL, fd_notify, &resp, sizeof(resp));
150f08c3bdfSopenharmony_ci		} else {
151f08c3bdfSopenharmony_ci			not_responded++;
152f08c3bdfSopenharmony_ci		}
153f08c3bdfSopenharmony_ci		SAFE_CLOSE(event.fd);
154f08c3bdfSopenharmony_ci	}
155f08c3bdfSopenharmony_ci}
156f08c3bdfSopenharmony_ci
157f08c3bdfSopenharmony_cistatic void test_fanotify(void)
158f08c3bdfSopenharmony_ci{
159f08c3bdfSopenharmony_ci	int newfd;
160f08c3bdfSopenharmony_ci	int ret;
161f08c3bdfSopenharmony_ci
162f08c3bdfSopenharmony_ci	fd_notify = setup_instance();
163f08c3bdfSopenharmony_ci	run_children();
164f08c3bdfSopenharmony_ci	loose_fanotify_events();
165f08c3bdfSopenharmony_ci
166f08c3bdfSopenharmony_ci	/*
167f08c3bdfSopenharmony_ci	 * Create and destroy another instance. This may hang if
168f08c3bdfSopenharmony_ci	 * unanswered fanotify events block notification subsystem.
169f08c3bdfSopenharmony_ci	 */
170f08c3bdfSopenharmony_ci	newfd = setup_instance();
171f08c3bdfSopenharmony_ci
172f08c3bdfSopenharmony_ci	SAFE_CLOSE(newfd);
173f08c3bdfSopenharmony_ci
174f08c3bdfSopenharmony_ci	tst_res(TPASS, "second instance destroyed successfully");
175f08c3bdfSopenharmony_ci
176f08c3bdfSopenharmony_ci	/*
177f08c3bdfSopenharmony_ci	 * Now destroy the fanotify instance while there are permission
178f08c3bdfSopenharmony_ci	 * events at various stages of processing. This may provoke
179f08c3bdfSopenharmony_ci	 * kernel hangs or crashes.
180f08c3bdfSopenharmony_ci	 */
181f08c3bdfSopenharmony_ci	SAFE_CLOSE(fd_notify);
182f08c3bdfSopenharmony_ci
183f08c3bdfSopenharmony_ci	ret = stop_children();
184f08c3bdfSopenharmony_ci	if (ret)
185f08c3bdfSopenharmony_ci		tst_res(TFAIL, "child exited for unexpected reason");
186f08c3bdfSopenharmony_ci	else
187f08c3bdfSopenharmony_ci		tst_res(TPASS, "all children exited successfully");
188f08c3bdfSopenharmony_ci}
189f08c3bdfSopenharmony_ci
190f08c3bdfSopenharmony_cistatic void setup(void)
191f08c3bdfSopenharmony_ci{
192f08c3bdfSopenharmony_ci	require_fanotify_access_permissions_supported_by_kernel();
193f08c3bdfSopenharmony_ci
194f08c3bdfSopenharmony_ci	sprintf(fname, "fname_%d", getpid());
195f08c3bdfSopenharmony_ci	SAFE_FILE_PRINTF(fname, "%s", fname);
196f08c3bdfSopenharmony_ci}
197f08c3bdfSopenharmony_ci
198f08c3bdfSopenharmony_cistatic void cleanup(void)
199f08c3bdfSopenharmony_ci{
200f08c3bdfSopenharmony_ci	stop_children();
201f08c3bdfSopenharmony_ci
202f08c3bdfSopenharmony_ci	if (fd_notify > 0)
203f08c3bdfSopenharmony_ci		SAFE_CLOSE(fd_notify);
204f08c3bdfSopenharmony_ci}
205f08c3bdfSopenharmony_ci
206f08c3bdfSopenharmony_cistatic struct tst_test test = {
207f08c3bdfSopenharmony_ci	.test_all = test_fanotify,
208f08c3bdfSopenharmony_ci	.setup = setup,
209f08c3bdfSopenharmony_ci	.cleanup = cleanup,
210f08c3bdfSopenharmony_ci	.needs_tmpdir = 1,
211f08c3bdfSopenharmony_ci	.forks_child = 1,
212f08c3bdfSopenharmony_ci	.needs_root = 1,
213f08c3bdfSopenharmony_ci};
214f08c3bdfSopenharmony_ci
215f08c3bdfSopenharmony_ci#else
216f08c3bdfSopenharmony_ci	TST_TEST_TCONF("system doesn't have required fanotify support");
217f08c3bdfSopenharmony_ci#endif
218