lib/libfuse/fuse.c was using a EINTR return from kevent() to detect when a signal had occurred, but in a multi-threaded process the signal may not have been delivered to the thread blocking on kevent(). This changes makes it so the signals are caught using EVFILT_SIGNAL filters so they can be detected even when they happen on another thread.

Index: lib/libfuse/fuse.c
===================================================================
RCS file: /cvs/src/lib/libfuse/fuse.c,v
retrieving revision 1.49
diff -u -p -u -p -r1.49 fuse.c
--- lib/libfuse/fuse.c  5 Jul 2018 10:57:31 -0000       1.49
+++ lib/libfuse/fuse.c  5 Oct 2018 22:13:41 -0000
@@ -32,8 +32,6 @@
 #include "fuse_private.h"
 #include "debug.h"

-static volatile sig_atomic_t sigraised = 0;
-static volatile sig_atomic_t signum = 0;
 static struct fuse_context *ictx = NULL;

 enum {
@@ -116,21 +114,10 @@ static struct fuse_opt fuse_mount_opts[]
 };

 static void
-ifuse_sighdlr(int num)
-{
-       if (!sigraised || (num == SIGCHLD)) {
-               sigraised = 1;
-               signum = num;
-       }
-}
-
-static void
 ifuse_try_unmount(struct fuse *f)
 {
        pid_t child;

-       signal(SIGCHLD, ifuse_sighdlr);
-
        /* unmount in another thread so fuse_loop() doesn't deadlock */
        child = fork();

@@ -152,7 +139,6 @@ ifuse_child_exit(const struct fuse *f)
 {
        int status;

-       signal(SIGCHLD, SIG_DFL);
        if (waitpid(WAIT_ANY, &status, WNOHANG) == -1)
                fprintf(stderr, "fuse: %s\n", strerror(errno));

@@ -160,7 +146,6 @@ ifuse_child_exit(const struct fuse *f)
                fprintf(stderr, "fuse: %s: %s\n",
                        f->fc->dir, strerror(WEXITSTATUS(status)));

-       sigraised = 0;
        return;
 }

@@ -170,6 +155,7 @@ fuse_loop(struct fuse *fuse)
        struct fusebuf fbuf;
        struct fuse_context ctx;
        struct fb_ioctl_xch ioexch;
+       struct kevent event[5];
        struct kevent ev;
        ssize_t n;
        int ret;
@@ -181,28 +167,39 @@ fuse_loop(struct fuse *fuse)
        if (fuse->fc->kq == -1)
                return (-1);

-       EV_SET(&fuse->fc->event, fuse->fc->fd, EVFILT_READ, EV_ADD |
+       EV_SET(&event[0], fuse->fc->fd, EVFILT_READ, EV_ADD |
            EV_ENABLE, 0, 0, 0);

+       /* signal events */
+       EV_SET(&event[1], SIGCHLD, EVFILT_SIGNAL, EV_ADD |
+           EV_ENABLE, 0, 0, 0);
+       EV_SET(&event[2], SIGHUP, EVFILT_SIGNAL, EV_ADD |
+           EV_ENABLE, 0, 0, 0);
+       EV_SET(&event[3], SIGINT, EVFILT_SIGNAL, EV_ADD |
+           EV_ENABLE, 0, 0, 0);
+       EV_SET(&event[4], SIGTERM, EVFILT_SIGNAL, EV_ADD |
+           EV_ENABLE, 0, 0, 0);
+
        while (!fuse->fc->dead) {
- ret = kevent(fuse->fc->kq, &fuse->fc->event, 1, &ev, 1, NULL);
+               ret = kevent(fuse->fc->kq, &event[0], 5, &ev, 1, NULL);
                if (ret == -1) {
-                       if (errno == EINTR) {
-                               switch (signum) {
-                               case SIGCHLD:
-                                       ifuse_child_exit(fuse);
-                                       break;
-                               case SIGHUP:
-                               case SIGINT:
-                               case SIGTERM:
-                                       ifuse_try_unmount(fuse);
-                                       break;
-                               default:
- fprintf(stderr, "%s: %s\n", __func__,
-                                           strsignal(signum));
-                               }
-                       } else
+                       if (errno != EINTR)
                                DPERROR(__func__);
+               } else if (ret > 0 && ev.filter == EVFILT_SIGNAL) {
+                       int signum = ev.ident;
+                       switch (signum) {
+                       case SIGCHLD:
+                               ifuse_child_exit(fuse);
+                               break;
+                       case SIGHUP:
+                       case SIGINT:
+                       case SIGTERM:
+                               ifuse_try_unmount(fuse);
+                               break;
+                       default:
+                               fprintf(stderr, "%s: %s\n", __func__,
+                                       strsignal(signum));
+                       }
                } else if (ret > 0) {
                        n = read(fuse->fc->fd, &fbuf, sizeof(fbuf));
                        if (n != sizeof(fbuf)) {
@@ -479,20 +476,24 @@ fuse_remove_signal_handlers(unused struc
        struct sigaction old_sa;

        if (sigaction(SIGHUP, NULL, &old_sa) == 0)
-               if (old_sa.sa_handler == ifuse_sighdlr)
+               if (old_sa.sa_handler == SIG_IGN)
                        signal(SIGHUP, SIG_DFL);

        if (sigaction(SIGINT, NULL, &old_sa) == 0)
-               if (old_sa.sa_handler == ifuse_sighdlr)
+               if (old_sa.sa_handler == SIG_IGN)
                        signal(SIGINT, SIG_DFL);

        if (sigaction(SIGTERM, NULL, &old_sa) == 0)
-               if (old_sa.sa_handler == ifuse_sighdlr)
+               if (old_sa.sa_handler == SIG_IGN)
                        signal(SIGTERM, SIG_DFL);

        if (sigaction(SIGPIPE, NULL, &old_sa) == 0)
                if (old_sa.sa_handler == SIG_IGN)
                        signal(SIGPIPE, SIG_DFL);
+
+       if (sigaction(SIGCHLD, NULL, &old_sa) == 0)
+               if (old_sa.sa_handler == SIG_IGN)
+                       signal(SIGCHLD, SIG_DFL);
 }
 DEF(fuse_remove_signal_handlers);

@@ -504,22 +505,27 @@ fuse_set_signal_handlers(unused struct f
        if (sigaction(SIGHUP, NULL, &old_sa) == -1)
                return (-1);
        if (old_sa.sa_handler == SIG_DFL)
-               signal(SIGHUP, ifuse_sighdlr);
+               signal(SIGHUP, SIG_IGN);

        if (sigaction(SIGINT, NULL, &old_sa) == -1)
                return (-1);
        if (old_sa.sa_handler == SIG_DFL)
-               signal(SIGINT, ifuse_sighdlr);
+               signal(SIGINT, SIG_IGN);

        if (sigaction(SIGTERM, NULL, &old_sa) == -1)
                return (-1);
        if (old_sa.sa_handler == SIG_DFL)
-               signal(SIGTERM, ifuse_sighdlr);
+               signal(SIGTERM, SIG_IGN);

        if (sigaction(SIGPIPE, NULL, &old_sa) == -1)
                return (-1);
        if (old_sa.sa_handler == SIG_DFL)
                signal(SIGPIPE, SIG_IGN);
+
+       if (sigaction(SIGCHLD, NULL, &old_sa) == -1)
+               return (-1);
+       if (old_sa.sa_handler == SIG_DFL)
+               signal(SIGCHLD, SIG_IGN);

        return (0);
 }
Index: lib/libfuse/fuse_private.h
===================================================================
RCS file: /cvs/src/lib/libfuse/fuse_private.h,v
retrieving revision 1.21
diff -u -p -u -p -r1.21 fuse_private.h
--- lib/libfuse/fuse_private.h  19 Jun 2018 13:01:34 -0000      1.21
+++ lib/libfuse/fuse_private.h  5 Oct 2018 22:13:41 -0000
@@ -69,7 +69,6 @@ struct fuse_chan {

        /* kqueue stuff */
        int kq;
-       struct kevent event;
 };

 struct fuse_config {

Reply via email to