Index: lib/sanitizer_common/sanitizer_common.h =================================================================== --- lib/sanitizer_common/sanitizer_common.h +++ lib/sanitizer_common/sanitizer_common.h @@ -279,6 +279,15 @@ char *FindPathToBinary(const char *name); bool IsPathSeparator(const char c); bool IsAbsolutePath(const char *path); +// Starts a subprocess and returs its pid. +// If *_fd parameters are >=0 their corresponding input/output +// streams would be redirect to the file. The files would always be closed +// in parent process even in case of an error. +int StartSubprocess(const char *program, char *const argv[], + fd_t std_in_fd = kInvalidFd, fd_t std_out_fd = kInvalidFd, + fd_t std_err_fd = kInvalidFd); +// Checks if specified process is still running +bool IsProcessRunning(int pid); u32 GetUid(); void ReExec(); @@ -748,6 +757,20 @@ void DisableReexec(); void MaybeReexec(); +template +struct AutoRunner { + explicit AutoRunner(Fn fn) : fn_(fn) {} + ~AutoRunner() { fn_(); } + Fn fn_; +}; + +// A simple scope guard. Usage: +// auto cleanup = at_scope_exit([]{ do_cleanup; }); +template +AutoRunner at_scope_exit(Fn fn) { + return AutoRunner(fn); +} + } // namespace __sanitizer inline void *operator new(__sanitizer::operator_new_size_type size, Index: lib/sanitizer_common/sanitizer_common.cc =================================================================== --- lib/sanitizer_common/sanitizer_common.cc +++ lib/sanitizer_common/sanitizer_common.cc @@ -423,6 +423,10 @@ static const char kPathSeparator = SANITIZER_WINDOWS ? ';' : ':'; char *FindPathToBinary(const char *name) { + if (FileExists(name)) { + return internal_strdup(name); + } + const char *path = GetEnv("PATH"); if (!path) return nullptr; Index: lib/sanitizer_common/sanitizer_linux.h =================================================================== --- lib/sanitizer_common/sanitizer_linux.h +++ lib/sanitizer_common/sanitizer_linux.h @@ -43,6 +43,8 @@ // internal_sigaction instead. int internal_sigaction_norestorer(int signum, const void *act, void *oldact); void internal_sigdelset(__sanitizer_sigset_t *set, int signum); +uptr internal_execve(const char *filename, char *const argv[], + char *const envp[]); #if defined(__x86_64__) || defined(__mips__) || defined(__aarch64__) \ || defined(__powerpc64__) uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, Index: lib/sanitizer_common/sanitizer_linux_libcdep.cc =================================================================== --- lib/sanitizer_common/sanitizer_linux_libcdep.cc +++ lib/sanitizer_common/sanitizer_linux_libcdep.cc @@ -34,6 +34,7 @@ #include #include #include +#include #if SANITIZER_FREEBSD #include @@ -546,6 +547,73 @@ WriteToSyslog(str); } +int StartSubprocess(const char *program, char *const argv[], fd_t std_in_fd, + fd_t std_out_fd, fd_t std_err_fd) { + auto file_closer = at_scope_exit([&] { + if (std_in_fd >= 0) { + internal_close(std_in_fd); + } + if (std_out_fd >= 0) { + internal_close(std_out_fd); + } + if (std_err_fd >= 0) { + internal_close(std_err_fd); + } + }); + + if (!FileExists(program)) { + Report("WARNING: Program %s not found!\n", program); + return -1; + } + + int pid = internal_fork(); + + if (pid < 0) { + int rverrno; + if (internal_iserror(pid, &rverrno)) { + Report("WARNING: failed to fork (errno %d)\n", rverrno); + } + return pid; + } + + if (pid == 0) { + // Child subprocess + if (std_in_fd >= 0) { + internal_close(STDIN_FILENO); + internal_dup2(std_in_fd, STDIN_FILENO); + internal_close(std_in_fd); + } + if (std_out_fd >= 0) { + internal_close(STDOUT_FILENO); + internal_dup2(std_out_fd, STDOUT_FILENO); + internal_close(std_out_fd); + } + if (std_err_fd >= 0) { + internal_close(STDERR_FILENO); + internal_dup2(std_err_fd, STDERR_FILENO); + internal_close(std_err_fd); + } + + for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--) internal_close(fd); + + internal_execve(program, argv, nullptr); + internal__exit(1); + } + + return pid; +} + +bool IsProcessRunning(int pid) { + int process_status; + uptr waitpid_status = internal_waitpid(pid, &process_status, WNOHANG); + int local_errno; + if (internal_iserror(waitpid_status, &local_errno)) { + VReport(1, "Waiting on the process failed (errno %d).\n", local_errno); + return false; + } + return waitpid_status == 0; +} + #endif // SANITIZER_LINUX } // namespace __sanitizer Index: lib/sanitizer_common/sanitizer_mac.cc =================================================================== --- lib/sanitizer_common/sanitizer_mac.cc +++ lib/sanitizer_common/sanitizer_mac.cc @@ -670,6 +670,17 @@ return *_NSGetArgv(); } +int StartSubprocess(const char *program, char *const argv[], fd_t std_in_fd, + fd_t std_out_fd, fd_t std_err_fd) { + // FIXME: implement on this platform. + return -1; +} + +bool IsProcessRunning(int pid) { + // FIXME: implement on this platform. + return false; +} + } // namespace __sanitizer #endif // SANITIZER_MAC Index: lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc =================================================================== --- lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc +++ lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc @@ -137,47 +137,23 @@ CHECK(infd); CHECK(outfd); - // Real fork() may call user callbacks registered with pthread_atfork(). - pid = internal_fork(); - if (pid == -1) { - // Fork() failed. + const char *argv[kArgVMax]; + GetArgV(path_, argv); + pid = StartSubprocess(path_, const_cast(&argv[0]), + outfd[0] /* stdin */, infd[1] /* stdout */); + if (pid < 0) { internal_close(infd[0]); - internal_close(infd[1]); internal_close(outfd[0]); - internal_close(outfd[1]); - Report("WARNING: failed to fork external symbolizer " - " (errno: %d)\n", errno); return false; - } else if (pid == 0) { - // Child subprocess. - internal_close(STDOUT_FILENO); - internal_close(STDIN_FILENO); - internal_dup2(outfd[0], STDIN_FILENO); - internal_dup2(infd[1], STDOUT_FILENO); - internal_close(outfd[0]); - internal_close(outfd[1]); - internal_close(infd[0]); - internal_close(infd[1]); - for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--) - internal_close(fd); - const char *argv[kArgVMax]; - GetArgV(path_, argv); - execv(path_, const_cast(&argv[0])); - internal__exit(1); } - // Continue execution in parent process. - internal_close(outfd[0]); - internal_close(infd[1]); input_fd_ = infd[0]; output_fd_ = outfd[1]; } // Check that symbolizer subprocess started successfully. - int pid_status; SleepForMillis(kSymbolizerStartupTimeMillis); - int exited_pid = waitpid(pid, &pid_status, WNOHANG); - if (exited_pid != 0) { + if (!IsProcessRunning(pid)) { // Either waitpid failed, or child has already exited. Report("WARNING: external symbolizer didn't start up correctly!\n"); return false; Index: lib/sanitizer_common/sanitizer_win.cc =================================================================== --- lib/sanitizer_common/sanitizer_win.cc +++ lib/sanitizer_common/sanitizer_win.cc @@ -775,6 +775,17 @@ return 0; } +int StartSubprocess(const char *program, char *const argv[], fd_t std_in_fd, + fd_t std_out_fd, fd_t std_err_fd) { + // FIXME: implement on this platform. + return -1; +} + +bool IsProcessRunning(int pid) { + // FIXME: implement on this platform. + return false; +} + } // namespace __sanitizer #endif // _WIN32 Index: lib/sanitizer_common/tests/sanitizer_linux_test.cc =================================================================== --- lib/sanitizer_common/tests/sanitizer_linux_test.cc +++ lib/sanitizer_common/tests/sanitizer_linux_test.cc @@ -263,6 +263,34 @@ } #endif +TEST(SanitizerCommon, StartSubprocessTest) { + int pipe_fds[2]; + ASSERT_EQ(0, pipe(pipe_fds)); + const char *argv[] = {"/bin/sh", "-c", "echo -n 'hello'"}; + int pid = StartSubprocess("/bin/sh", const_cast(&argv[0]), + kInvalidFd /* stdin */, pipe_fds[1] /* stdout */); + ASSERT_GT(pid, 0); + + // wait for process to finish. + while (IsProcessRunning(pid)) { + } + ASSERT_FALSE(IsProcessRunning(pid)); + + char buffer[256]; + char *ptr = buffer; + uptr bytes_read; + while (ReadFromFile(pipe_fds[0], ptr, 256, &bytes_read)) { + if (!bytes_read) { + break; + } + ptr += bytes_read; + } + ASSERT_EQ(5, ptr - buffer); + *ptr = 0; + ASSERT_EQ(0, strcmp(buffer, "hello")) << "Buffer: " << buffer; + internal_close(pipe_fds[0]); +} + } // namespace __sanitizer #endif // SANITIZER_LINUX