Home | History | Annotate | Download | only in fanotify
      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 (at) 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 
     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 
     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 
    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 
    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 
    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 
    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 
    214 static void setup(void)
    215 {
    216 	sprintf(fname, "fname_%d", getpid());
    217 	SAFE_FILE_PRINTF(fname, "%s", fname);
    218 }
    219 
    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