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