diff options
| author | Anton Lindqvist <anton@basename.se> | 2020-09-03 22:18:31 +0200 |
|---|---|---|
| committer | Anton Lindqvist <anton@basename.se> | 2020-09-12 13:21:21 +0200 |
| commit | ce441f065b6eebb166bb006dfd28ea0c6b730384 (patch) | |
| tree | 787f2cd6417a3529bfbbd726d4b08cb148409dbf | |
| parent | 7b0683780a0f5f05e602bb3fe9d97c66f2c956e4 (diff) | |
executor: improve opendir(3) error handling
While investigating an OpenBSD reproducer[1][2] I discovered the
following:
* All threads are stuck on the last `sleep(1000000)` syscall in main(),
hence no output for the test machine.
* Each executor process created in loop() performs one iteration but
exits abnormally during the call to remove_dir().
* Calling remove_dir() will eventually invoke itself recursively since
one of the executed syscall is `mkdir("./file0", 0)` meaning that it
will try to remove the directory created by execute_one(). However,
`opendir(3)` fails with `EACCES` due to the permissions passed to
`mkdir(2)` is zero.
Instead of exiting, trying to remove the problematic directory in a best
effort manner makes the reproducer continue executing the generated
syscalls. This work around might be considered to narrow. Another option
would be to replace the `sleep(1000000)` with `waitpid(-1, NULL, 0)`
until ECHILD is hit.
[1] https://syzkaller.appspot.com/bug?id=6f7ce2a0536580a94f65f44e478732ec505e88af
[2] https://syzkaller.appspot.com/text?tag=ReproC&x=10fd1a71900000
| -rw-r--r-- | executor/common.h | 14 | ||||
| -rw-r--r-- | pkg/csource/generated.go | 9 |
2 files changed, 21 insertions, 2 deletions
diff --git a/executor/common.h b/executor/common.h index 81ddefd01..490595ce2 100644 --- a/executor/common.h +++ b/executor/common.h @@ -199,6 +199,7 @@ static void use_temporary_dir(void) #if GOOS_akaros || GOOS_netbsd || GOOS_freebsd || GOOS_openbsd || GOOS_test #if (SYZ_EXECUTOR || SYZ_REPEAT) && SYZ_EXECUTOR_USES_FORK_SERVER && (SYZ_EXECUTOR || SYZ_USE_TMP_DIR) #include <dirent.h> +#include <errno.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> @@ -207,8 +208,19 @@ static void use_temporary_dir(void) static void remove_dir(const char* dir) { DIR* dp = opendir(dir); - if (dp == NULL) + if (dp == NULL) { + if (errno == EACCES) { + // We could end up here in a recursive call to remove_dir() below. + // One of executed syscall could end up creating a directory rooted + // in the current working directory created by loop() with zero + // permissions. Try to perform a best effort removal of the + // directory. + if (rmdir(dir)) + exitf("rmdir(%s) failed", dir); + return; + } exitf("opendir(%s) failed", dir); + } struct dirent* ep = 0; while ((ep = readdir(dp))) { if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0) diff --git a/pkg/csource/generated.go b/pkg/csource/generated.go index 84ef284a9..65abca7e7 100644 --- a/pkg/csource/generated.go +++ b/pkg/csource/generated.go @@ -179,6 +179,7 @@ static void use_temporary_dir(void) #if GOOS_akaros || GOOS_netbsd || GOOS_freebsd || GOOS_openbsd || GOOS_test #if (SYZ_EXECUTOR || SYZ_REPEAT) && SYZ_EXECUTOR_USES_FORK_SERVER && (SYZ_EXECUTOR || SYZ_USE_TMP_DIR) #include <dirent.h> +#include <errno.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> @@ -187,8 +188,14 @@ static void use_temporary_dir(void) static void remove_dir(const char* dir) { DIR* dp = opendir(dir); - if (dp == NULL) + if (dp == NULL) { + if (errno == EACCES) { + if (rmdir(dir)) + exitf("rmdir(%s) failed", dir); + return; + } exitf("opendir(%s) failed", dir); + } struct dirent* ep = 0; while ((ep = readdir(dp))) { if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0) |
