[LTP] [PATCH] fanotify: Add test for permission event destruction

Amir Goldstein amir73il@gmail.com
Sat Jan 21 16:47:21 CET 2017


On Fri, Jan 20, 2017 at 3:29 PM, Jan Kara <jack@suse.cz> wrote:
> Test whether kernel's notification subsystem gets stuck when some
> fanotify permission events are not responded to. Also test destruction
> of fanotify instance while there are outstanding permission events. This
> can result in hanging of processes indefinitely in kernel or in kernel
> crashes.
>

Looks good to me, but I wonder about your phrasing "can result" and
"This may hang" below.

IIRC, Miklos' test case was reproducing the problem 100% on the times.
Your test may be as well, but it is not clear from using above wording?


> Kernel crashes should be fixed by commit 96d41019e3ac "fanotify: fix
> list corruption in fanotify_get_response()", kernel hangs by "fanotify:
> Release SRCU lock when waiting for userspace response" (not yet landed
> upstream).
>
> Signed-off-by: Jan Kara <jack@suse.cz>
> ---
>  runtest/syscalls                                |   1 +
>  testcases/kernel/syscalls/fanotify/fanotify07.c | 287 ++++++++++++++++++++++++
>  2 files changed, 288 insertions(+)
>  create mode 100644 testcases/kernel/syscalls/fanotify/fanotify07.c
>
> diff --git a/runtest/syscalls b/runtest/syscalls
> index 884ab800acaf..3c19fb58b79d 100644
> --- a/runtest/syscalls
> +++ b/runtest/syscalls
> @@ -469,6 +469,7 @@ fanotify03 fanotify03
>  fanotify04 fanotify04
>  fanotify05 fanotify05
>  fanotify06 fanotify06
> +fanotify07 fanotify07
>
>  ioperm01 ioperm01
>  ioperm02 ioperm02
> diff --git a/testcases/kernel/syscalls/fanotify/fanotify07.c b/testcases/kernel/syscalls/fanotify/fanotify07.c
> new file mode 100644
> index 000000000000..6c793071c9bc
> --- /dev/null
> +++ b/testcases/kernel/syscalls/fanotify/fanotify07.c
> @@ -0,0 +1,287 @@
> +/*
> + * Copyright (c) 2016 SUSE.  All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of version 2 of the GNU General Public License as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it would be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
> + *
> + * Further, this software is distributed without any warranty that it is
> + * free of the rightful claim of any third person regarding infringement
> + * or the like.  Any license provided herein, whether implied or
> + * otherwise, applies only to this software file.  Patent licenses, if
> + * any, provided herein do not apply to combinations of this program with
> + * other software, or any other product whatsoever.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + * Started by Jan Kara <jack@suse.cz>
> + *
> + * DESCRIPTION
> + *     Check that fanotify permission events are handled properly on instance
> + *     destruction.
> + */
> +#define _GNU_SOURCE
> +#include "config.h"
> +
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#include <sys/fcntl.h>
> +#include <sys/wait.h>
> +#include <errno.h>
> +#include <string.h>
> +#include <signal.h>
> +#include <sys/syscall.h>
> +#include "test.h"
> +#include "linux_syscall_numbers.h"
> +#include "fanotify.h"
> +#include "safe_macros.h"
> +
> +char *TCID = "fanotify07";
> +int TST_TOTAL = 2;
> +
> +#if defined(HAVE_SYS_FANOTIFY_H)
> +#include <sys/fanotify.h>
> +
> +static void setup(void);
> +static void cleanup(void);
> +
> +#define BUF_SIZE 256
> +static char fname[BUF_SIZE];
> +static char buf[BUF_SIZE];
> +static volatile int fd_notify;
> +
> +/* Number of children we start */
> +#define MAX_CHILDREN 16
> +static pid_t child_pid[MAX_CHILDREN];
> +
> +/* Number of children we don't respond to before stopping */
> +#define MAX_NOT_RESPONDED 4
> +
> +static void generate_events(void)
> +{
> +       int fd;
> +
> +       /*
> +        * generate sequence of events
> +        */
> +       if ((fd = open(fname, O_RDWR | O_CREAT, 0700)) == -1)
> +               exit(1);
> +
> +       /* Run until killed... */
> +       while (1) {
> +               lseek(fd, 0, SEEK_SET);
> +               if (read(fd, buf, BUF_SIZE) == -1)
> +                       exit(3);
> +       }
> +}
> +
> +static void run_children(void)
> +{
> +       int i;
> +
> +       for (i = 0; i < MAX_CHILDREN; i++) {
> +               switch (child_pid[i] = fork()) {
> +               case 0:
> +                       /* Child will generate events now */
> +                       close(fd_notify);
> +                       generate_events();
> +                       exit(0);
> +               case -1:
> +                       tst_brkm(TBROK | TERRNO, cleanup, "fork() failed");
> +               }
> +       }
> +}
> +
> +static int stop_children(void)
> +{
> +       int child_ret;
> +       int i, ret = 0;
> +
> +       for (i = 0; i < MAX_CHILDREN; i++)
> +               kill(child_pid[i], SIGKILL);
> +
> +       for (i = 0; i < MAX_CHILDREN; i++) {
> +               if (waitpid(child_pid[i], &child_ret, 0) < 0) {
> +                       tst_brkm(TBROK | TERRNO, cleanup,
> +                                "waitpid(-1, &child_ret, 0) failed");
> +               }
> +               if (!WIFSIGNALED(child_ret))
> +                       ret = 1;
> +       }
> +
> +       return ret;
> +}
> +
> +static int setup_instance(void)
> +{
> +       int fd;
> +
> +       if ((fd = fanotify_init(FAN_CLASS_CONTENT, O_RDONLY)) < 0) {
> +               if (errno == ENOSYS) {
> +                       tst_brkm(TCONF, cleanup,
> +                                "fanotify is not configured in this kernel.");
> +               } else {
> +                       tst_brkm(TBROK | TERRNO, cleanup,
> +                                "fanotify_init failed");
> +               }
> +       }
> +
> +       if (fanotify_mark(fd, FAN_MARK_ADD, FAN_ACCESS_PERM, AT_FDCWD,
> +                         fname) < 0) {
> +               close(fd);
> +               if (errno == EINVAL) {
> +                       tst_brkm(TCONF | TERRNO, cleanup,
> +                                "CONFIG_FANOTIFY_ACCESS_PERMISSIONS not "
> +                                "configured in kernel?");
> +               } else {
> +                       tst_brkm(TBROK | TERRNO, cleanup,
> +                                "fanotify_mark (%d, FAN_MARK_ADD, FAN_ACCESS_PERM, "
> +                                "AT_FDCWD, %s) failed.", fd, fname);
> +               }
> +       }
> +
> +       return fd;
> +}
> +
> +static void lose_fanotify_events(void)
> +{
> +       int ret;
> +       int not_responded = 0;
> +
> +       /*
> +        * check events
> +        */
> +       while (not_responded < MAX_NOT_RESPONDED) {
> +               struct fanotify_event_metadata event;
> +               struct fanotify_response resp;
> +
> +               /* Get more events */
> +               ret = read(fd_notify, &event, sizeof(event));
> +               if (ret < 0) {
> +                       tst_brkm(TBROK, cleanup,
> +                                "read(%d, &event, %zu) failed",
> +                                fd_notify, sizeof(event));
> +               }
> +               if (ret == 0) {
> +                       tst_brkm(TBROK, cleanup,
> +                               "premature EOF while reading from "
> +                               "fanotify fd");
> +               }
> +
> +               if (event.mask != FAN_ACCESS_PERM) {
> +                       tst_resm(TFAIL,
> +                                "get event: mask=%llx (expected %llx) "
> +                                "pid=%u fd=%u",
> +                                (unsigned long long)event.mask,
> +                                (unsigned long long)FAN_ACCESS_PERM,
> +                                (unsigned)event.pid, event.fd);
> +                       break;
> +               }
> +
> +               /*
> +                * We respond to permission event with 95% percent
> +                * probability. */
> +               if (random() % 100 > 5) {
> +                       /* Write response to permission event */
> +                       resp.fd = event.fd;
> +                       resp.response = FAN_ALLOW;
> +                       SAFE_WRITE(cleanup, 1, fd_notify, &resp,
> +                                  sizeof(resp));
> +               } else {
> +                       not_responded++;
> +               }
> +               close(event.fd);
> +       }
> +}
> +
> +int main(int ac, char **av)
> +{
> +       int lc;
> +
> +       tst_parse_opts(ac, av, NULL, NULL);
> +
> +       setup();
> +
> +       for (lc = 0; TEST_LOOPING(lc); lc++) {
> +               int newfd;
> +               int ret;
> +
> +               tst_count = 0;
> +
> +               fd_notify = setup_instance();
> +               run_children();
> +               lose_fanotify_events();
> +
> +               /*
> +                * Create and destroy another instance. This may hang if
> +                * unanswered fanotify events block notification subsystem.
> +                */
> +               newfd = setup_instance();
> +               if (close(newfd)) {
> +                       tst_brkm(TBROK | TERRNO, cleanup,
> +                                "close(%d) failed", newfd);
> +               }
> +
> +               tst_resm(TPASS, "second instance destroyed successfully");
> +
> +               /*
> +                * Now destroy the fanotify instance while there are permission
> +                * events at various stages of processing. This may provoke
> +                * kernel hangs or crashes.
> +                */
> +               if (close(fd_notify)) {
> +                       tst_brkm(TBROK | TERRNO, cleanup,
> +                                "close(%d) failed", fd_notify);
> +               }
> +               fd_notify = -1;
> +
> +               ret = stop_children();
> +               if (ret)
> +                       tst_resm(TFAIL, "child exited for unexpected reason");
> +               else
> +                       tst_resm(TPASS, "all children exited successfully");
> +       }
> +
> +       cleanup();
> +       tst_exit();
> +}
> +
> +static void setup(void)
> +{
> +       int fd;
> +
> +       tst_sig(FORK, DEF_HANDLER, cleanup);
> +
> +       TEST_PAUSE;
> +
> +       tst_tmpdir();
> +       sprintf(fname, "fname_%d", getpid());
> +       fd = SAFE_OPEN(cleanup, fname, O_CREAT | O_RDWR, 0644);
> +       SAFE_WRITE(cleanup, 1, fd, fname, 1);
> +       SAFE_CLOSE(cleanup, fd);
> +}
> +
> +static void cleanup(void)
> +{
> +       if (fd_notify > 0 && close(fd_notify))
> +               tst_resm(TWARN | TERRNO, "close(%d) failed", fd_notify);
> +
> +       tst_rmdir();
> +}
> +
> +#else
> +
> +int main(void)
> +{
> +       tst_brkm(TCONF, NULL, "system doesn't have required fanotify support");
> +}
> +
> +#endif
> --
> 2.10.2
>


More information about the ltp mailing list