aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2018-08-04 17:50:58 +0200
committerDmitry Vyukov <dvyukov@google.com>2018-08-04 17:50:58 +0200
commit3a7200e49b4e697ed93fb88178180ab6171d3f17 (patch)
tree555c10f070c2b40e4b929f6f34f48fd694190c0f
parentbf45aa5363c1f8db29b7c6fe9d086560857b0db4 (diff)
executor: abort fuse connection
If the test process is not dying after 100ms, abort all fuse connections in the system. This gets rid at least of simple fuse deadlocks, let's see how well this works in all cases.
-rw-r--r--executor/common.h18
-rw-r--r--executor/common_linux.h60
-rw-r--r--pkg/csource/generated.go71
-rw-r--r--sys/linux/test/fuse_deadlock13
4 files changed, 143 insertions, 19 deletions
diff --git a/executor/common.h b/executor/common.h
index 5419599da..6ba88ea3b 100644
--- a/executor/common.h
+++ b/executor/common.h
@@ -116,6 +116,17 @@ static void install_segv_handler()
#endif
#endif
+#if !GOOS_linux
+#if SYZ_EXECUTOR || SYZ_REPEAT && SYZ_EXECUTOR_USES_FORK_SERVER
+static void kill_and_wait(int pid, int* status)
+{
+ kill(pid, SIGKILL);
+ while (waitpid(-1, status, 0) != pid) {
+ }
+}
+#endif
+#endif
+
#if !GOOS_windows
#if SYZ_EXECUTOR || SYZ_THREADED || SYZ_REPEAT && SYZ_EXECUTOR_USES_FORK_SERVER
static void sleep_ms(uint64 ms)
@@ -573,12 +584,7 @@ static void loop()
continue;
#endif
debug("killing\n");
-#if GOOS_linux
- kill(-pid, SIGKILL);
-#endif
- kill(pid, SIGKILL);
- while (waitpid(-1, &status, WAIT_FLAGS) != pid) {
- }
+ kill_and_wait(pid, &status);
break;
}
#if SYZ_EXECUTOR
diff --git a/executor/common_linux.h b/executor/common_linux.h
index 3c6aadd31..610586021 100644
--- a/executor/common_linux.h
+++ b/executor/common_linux.h
@@ -1424,8 +1424,14 @@ static void setup_binfmt_misc()
#endif
#if SYZ_EXECUTOR || SYZ_SANDBOX_NONE || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NAMESPACE
+#include <errno.h>
+#include <sys/mount.h>
+
static void setup_common()
{
+ if (mount(0, "/sys/fs/fuse/connections", "fusectl", 0, 0)) {
+ debug("mount(fusectl) failed: %d\n", errno);
+ }
#if SYZ_EXECUTOR || SYZ_ENABLE_CGROUPS
setup_cgroups();
setup_binfmt_misc();
@@ -1853,6 +1859,60 @@ static int fault_injected(int fail_fd)
}
#endif
+#if SYZ_EXECUTOR || SYZ_REPEAT
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+static void kill_and_wait(int pid, int* status)
+{
+ kill(-pid, SIGKILL);
+ kill(pid, SIGKILL);
+ int i;
+ // First, give it up to 100 ms to surrender.
+ for (i = 0; i < 100; i++) {
+ if (waitpid(-1, status, WNOHANG | __WALL) == pid)
+ return;
+ usleep(1000);
+ }
+ // Now, try to abort fuse connections as they cause deadlocks,
+ // see Documentation/filesystems/fuse.txt for details.
+ // There is no good way to figure out the right connections
+ // provided that the process could use unshare(CLONE_NEWNS),
+ // so we abort all.
+ debug("kill is not working\n");
+ DIR* dir = opendir("/sys/fs/fuse/connections");
+ if (dir) {
+ for (;;) {
+ struct dirent* ent = readdir(dir);
+ if (!ent)
+ break;
+ if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
+ continue;
+ char abort[300];
+ snprintf(abort, sizeof(abort), "/sys/fs/fuse/connections/%s/abort", ent->d_name);
+ int fd = open(abort, O_WRONLY);
+ if (fd == -1) {
+ debug("failed to open %s: %d\n", abort, errno);
+ continue;
+ }
+ debug("aborting fuse conn %s\n", ent->d_name);
+ write(fd, abort, 1);
+ close(fd);
+ }
+ closedir(dir);
+ } else {
+ debug("failed to open /sys/fs/fuse/connections: %d\n", errno);
+ }
+ // Now, just wait, no other options.
+ while (waitpid(-1, status, __WALL) != pid) {
+ }
+}
+#endif
+
#if SYZ_EXECUTOR || SYZ_REPEAT && SYZ_ENABLE_CGROUPS
#include <fcntl.h>
#include <sys/ioctl.h>
diff --git a/pkg/csource/generated.go b/pkg/csource/generated.go
index b4ffc279e..8f1584596 100644
--- a/pkg/csource/generated.go
+++ b/pkg/csource/generated.go
@@ -101,6 +101,17 @@ static void install_segv_handler()
#endif
#endif
+#if !GOOS_linux
+#if SYZ_EXECUTOR || SYZ_REPEAT && SYZ_EXECUTOR_USES_FORK_SERVER
+static void kill_and_wait(int pid, int* status)
+{
+ kill(pid, SIGKILL);
+ while (waitpid(-1, status, 0) != pid) {
+ }
+}
+#endif
+#endif
+
#if !GOOS_windows
#if SYZ_EXECUTOR || SYZ_THREADED || SYZ_REPEAT && SYZ_EXECUTOR_USES_FORK_SERVER
static void sleep_ms(uint64 ms)
@@ -2919,8 +2930,14 @@ static void setup_binfmt_misc()
#endif
#if SYZ_EXECUTOR || SYZ_SANDBOX_NONE || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NAMESPACE
+#include <errno.h>
+#include <sys/mount.h>
+
static void setup_common()
{
+ if (mount(0, "/sys/fs/fuse/connections", "fusectl", 0, 0)) {
+ debug("mount(fusectl) failed: %d\n", errno);
+ }
#if SYZ_EXECUTOR || SYZ_ENABLE_CGROUPS
setup_cgroups();
setup_binfmt_misc();
@@ -3305,6 +3322,53 @@ static int fault_injected(int fail_fd)
}
#endif
+#if SYZ_EXECUTOR || SYZ_REPEAT
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+static void kill_and_wait(int pid, int* status)
+{
+ kill(-pid, SIGKILL);
+ kill(pid, SIGKILL);
+ int i;
+ for (i = 0; i < 100; i++) {
+ if (waitpid(-1, status, WNOHANG | __WALL) == pid)
+ return;
+ usleep(1000);
+ }
+ debug("kill is not working\n");
+ DIR* dir = opendir("/sys/fs/fuse/connections");
+ if (dir) {
+ for (;;) {
+ struct dirent* ent = readdir(dir);
+ if (!ent)
+ break;
+ if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
+ continue;
+ char abort[300];
+ snprintf(abort, sizeof(abort), "/sys/fs/fuse/connections/%s/abort", ent->d_name);
+ int fd = open(abort, O_WRONLY);
+ if (fd == -1) {
+ debug("failed to open %s: %d\n", abort, errno);
+ continue;
+ }
+ debug("aborting fuse conn %s\n", ent->d_name);
+ write(fd, abort, 1);
+ close(fd);
+ }
+ closedir(dir);
+ } else {
+ debug("failed to open /sys/fs/fuse/connections: %d\n", errno);
+ }
+ while (waitpid(-1, status, __WALL) != pid) {
+ }
+}
+#endif
+
#if SYZ_EXECUTOR || SYZ_REPEAT && SYZ_ENABLE_CGROUPS
#include <fcntl.h>
#include <sys/ioctl.h>
@@ -3820,12 +3884,7 @@ static void loop()
continue;
#endif
debug("killing\n");
-#if GOOS_linux
- kill(-pid, SIGKILL);
-#endif
- kill(pid, SIGKILL);
- while (waitpid(-1, &status, WAIT_FLAGS) != pid) {
- }
+ kill_and_wait(pid, &status);
break;
}
#if SYZ_EXECUTOR
diff --git a/sys/linux/test/fuse_deadlock b/sys/linux/test/fuse_deadlock
index 2c24cf53a..fee10ac39 100644
--- a/sys/linux/test/fuse_deadlock
+++ b/sys/linux/test/fuse_deadlock
@@ -1,11 +1,10 @@
-# Test for deadlock in fuse.
-# Some calls are commented out for now, because it actually deadlocks kernel.
+# Test how we avoid fuse deadlocks in kill_and_wait.
mkdirat(0xffffffffffffff9c, &(0x7f0000000000)='./file0\x00', 0x0)
r0 = openat$fuse(0xffffffffffffff9c, &(0x7f0000000640)='/dev/fuse\x00', 0x2, 0x0)
mount$fuse(0x0, &(0x7f0000000200)='./file0\x00', &(0x7f0000000300)='fuse\x00', 0x0, &(0x7f0000000400)={{'fd', 0x3d, r0}, 0x2c, {'rootmode', 0x3d, 0x4000}, 0x2c, {'user_id', 0x3d}, 0x2c, {'group_id', 0x3d}, 0x2c})
-#read$FUSE(r0, &(0x7f0000002000), 0x1000)
-#pread64(r0, &(0x7f0000000540)=""/236, 0xec, 0x0)
-#write$FUSE_INIT(r0, &(0x7f0000000100)={0x50, 0x0, 0x1, {0x7, 0x1b}}, 0x50)
-#mkdirat(0xffffffffffffff9c, &(0x7f0000000500)='./file0/file0\x00', 0x0)
-#write$FUSE_NOTIFY_INVAL_ENTRY(r0, &(0x7f00000000c0)={0x29, 0x3, 0x0, {0x1, 0x8, 0x0, 'group_id'}}, 0x29)
+read$FUSE(r0, &(0x7f0000002000), 0x1000)
+pread64(r0, &(0x7f0000000540)=""/236, 0xec, 0x0)
+write$FUSE_INIT(r0, &(0x7f0000000100)={0x50, 0x0, 0x1, {0x7, 0x1b}}, 0x50)
+mkdirat(0xffffffffffffff9c, &(0x7f0000000500)='./file0/file0\x00', 0x0)
+write$FUSE_NOTIFY_INVAL_ENTRY(r0, &(0x7f00000000c0)={0x29, 0x3, 0x0, {0x1, 0x8, 0x0, 'group_id'}}, 0x29)