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