diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_file.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_file.cpp index 9236a458cdb0e..7cfade2bc681f 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_file.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_file.cpp @@ -36,9 +36,17 @@ void RawWrite(const char *buffer) { void ReportFile::ReopenIfNecessary() { mu->CheckLocked(); - if (fd == kStdoutFd || fd == kStderrFd) return; - uptr pid = internal_getpid(); + if (fallbackToStderrActive && fd_pid != pid) { + // If fallbackToStderrActive is set then we fellback to stderr. If this is a + // new process, mark fd as invalid so we attempt to open again. + CHECK_EQ(fd, kStderrFd); + fd = kInvalidFd; + fallbackToStderrActive = false; + } + if (fd == kStdoutFd || fd == kStderrFd) + return; + // If in tracer, use the parent's file. if (pid == stoptheworld_tracer_pid) pid = stoptheworld_tracer_ppid; @@ -48,8 +56,7 @@ void ReportFile::ReopenIfNecessary() { // process, close it now. if (fd_pid == pid) return; - else - CloseFile(fd); + CloseFile(fd); } const char *exe_name = GetProcessName(); @@ -65,18 +72,24 @@ void ReportFile::ReopenIfNecessary() { error_t err; fd = OpenFile(full_path, WrOnly, &err); if (fd == kInvalidFd) { - const char *ErrorMsgPrefix = "ERROR: Can't open file: "; + bool fallback = common_flags()->log_fallback_to_stderr; + const char *ErrorMsgPrefix = + fallback ? "WARNING: Can't open file, falling back to stderr: " + : "ERROR: Can't open file: "; WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix)); WriteToFile(kStderrFd, full_path, internal_strlen(full_path)); char errmsg[100]; internal_snprintf(errmsg, sizeof(errmsg), " (reason: %d)\n", err); WriteToFile(kStderrFd, errmsg, internal_strlen(errmsg)); - Die(); + if (!fallback) + Die(); + fallbackToStderrActive = true; + fd = kStderrFd; } fd_pid = pid; } -static void RecursiveCreateParentDirs(char *path) { +static void RecursiveCreateParentDirs(char *path, fd_t &fd) { if (path[0] == '\0') return; for (int i = 1; path[i] != '\0'; ++i) { @@ -85,12 +98,19 @@ static void RecursiveCreateParentDirs(char *path) { continue; path[i] = '\0'; if (!DirExists(path) && !CreateDir(path)) { - const char *ErrorMsgPrefix = "ERROR: Can't create directory: "; + bool fallback = common_flags()->log_fallback_to_stderr; + const char *ErrorMsgPrefix = + fallback ? "WARNING: Can't create directory, falling back to stderr: " + : "ERROR: Can't create directory: "; WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix)); WriteToFile(kStderrFd, path, internal_strlen(path)); const char *ErrorMsgSuffix = "\n"; WriteToFile(kStderrFd, ErrorMsgSuffix, internal_strlen(ErrorMsgSuffix)); - Die(); + if (!fallback) + Die(); + path[i] = save; + fd = kStderrFd; + return; } path[i] = save; } @@ -161,12 +181,17 @@ void ReportFile::SetReportPath(const char *path) { if (path) { uptr len = internal_strlen(path); if (len > sizeof(path_prefix) - 100) { - const char *message = "ERROR: Path is too long: "; + bool fallback = common_flags()->log_fallback_to_stderr; + const char *message = + fallback ? "WARNING: Path is too long, falling back to stderr: " + : "ERROR: Path is too long: "; WriteToFile(kStderrFd, message, internal_strlen(message)); WriteToFile(kStderrFd, path, 8); message = "...\n"; WriteToFile(kStderrFd, message, internal_strlen(message)); - Die(); + if (!fallback) + Die(); + path = "stderr"; } } @@ -180,7 +205,7 @@ void ReportFile::SetReportPath(const char *path) { fd = kStdoutFd; } else { ParseAndSetPath(path, path_prefix, kMaxPathLength); - RecursiveCreateParentDirs(path_prefix); + RecursiveCreateParentDirs(path_prefix, fd); } } diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_file.h b/compiler-rt/lib/sanitizer_common/sanitizer_file.h index bef2c842d9f24..b3a5fed922da9 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_file.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_file.h @@ -43,6 +43,9 @@ struct ReportFile { // PID of the process that opened fd. If a fork() occurs, // the PID of child will be different from fd_pid. uptr fd_pid; + // Set to true if the last attempt to open the logfile failed, perhaps due to + // permission errors + bool fallbackToStderrActive = false; private: void ReopenIfNecessary(); diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc b/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc index c1e3530618c20..650a4580bbcf0 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc @@ -65,6 +65,8 @@ COMMON_FLAG( bool, log_to_syslog, (bool)SANITIZER_ANDROID || (bool)SANITIZER_APPLE, "Write all sanitizer output to syslog in addition to other means of " "logging.") +COMMON_FLAG(bool, log_fallback_to_stderr, false, + "When set, fallback to stderr if we are unable to open log path.") COMMON_FLAG( int, verbosity, 0, "Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).") diff --git a/compiler-rt/test/sanitizer_common/TestCases/Posix/sanitizer_set_report_path_fail.cpp b/compiler-rt/test/sanitizer_common/TestCases/Posix/sanitizer_set_report_path_fail.cpp index af5187a0d3265..819678e956d37 100644 --- a/compiler-rt/test/sanitizer_common/TestCases/Posix/sanitizer_set_report_path_fail.cpp +++ b/compiler-rt/test/sanitizer_common/TestCases/Posix/sanitizer_set_report_path_fail.cpp @@ -2,9 +2,11 @@ // Case 1: Try setting a path that is an invalid/inaccessible directory. // RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=ERROR1 +// RUN: %env_tool_opts=log_fallback_to_stderr=true %run %t 2>&1 | FileCheck %s --check-prefixes=ERROR1,FALLBACK // Case 2: Try setting a path that is too large. // RUN: not %run %t A 2>&1 | FileCheck %s --check-prefix=ERROR2 +// RUN: %env_tool_opts=log_fallback_to_stderr=true %run %t A 2>&1 | FileCheck %s --check-prefixes=ERROR2,FALLBACK #include #include @@ -14,11 +16,12 @@ int main(int argc, char **argv) { if (argc == 1) { // Case 1 sprintf(buff, "%s/report", argv[0]); - // ERROR1: Can't create directory: {{.*}} + // ERROR1: Can't create directory } else { // Case 2 snprintf(buff, sizeof(buff), "%04095d", 42); - // ERROR2: Path is too long: 00000000... + // ERROR2: Path is too long } __sanitizer_set_report_path(buff); } +// FALLBACK: falling back to stderr