346 lines · cpp
1//===-- sanitizer_file.cpp -----------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===---------------------------------------------------------------------===//8//9// This file is shared between AddressSanitizer and ThreadSanitizer10// run-time libraries. It defines filesystem-related interfaces. This11// is separate from sanitizer_common.cpp so that it's simpler to disable12// all the filesystem support code for a port that doesn't use it.13//14//===---------------------------------------------------------------------===//15 16#include "sanitizer_platform.h"17 18#if !SANITIZER_FUCHSIA19 20#include "sanitizer_common.h"21#include "sanitizer_file.h"22# include "sanitizer_interface_internal.h"23 24namespace __sanitizer {25 26void CatastrophicErrorWrite(const char *buffer, uptr length) {27 WriteToFile(kStderrFd, buffer, length);28}29 30StaticSpinMutex report_file_mu;31ReportFile report_file = {&report_file_mu, kStderrFd, "", "", 0};32 33void RawWrite(const char *buffer) {34 report_file.Write(buffer, internal_strlen(buffer));35}36 37void ReportFile::ReopenIfNecessary() {38 mu->CheckLocked();39 uptr pid = internal_getpid();40 if (fallbackToStderrActive && fd_pid != pid) {41 // If fallbackToStderrActive is set then we fellback to stderr. If this is a42 // new process, mark fd as invalid so we attempt to open again.43 CHECK_EQ(fd, kStderrFd);44 fd = kInvalidFd;45 fallbackToStderrActive = false;46 }47 if (fd == kStdoutFd || fd == kStderrFd)48 return;49 50 // If in tracer, use the parent's file.51 if (pid == stoptheworld_tracer_pid)52 pid = stoptheworld_tracer_ppid;53 if (fd != kInvalidFd) {54 // If the report file is already opened by the current process,55 // do nothing. Otherwise the report file was opened by the parent56 // process, close it now.57 if (fd_pid == pid)58 return;59 CloseFile(fd);60 }61 62 const char *exe_name = GetProcessName();63 if (common_flags()->log_exe_name && exe_name) {64 internal_snprintf(full_path, kMaxPathLength, "%s.%s.%zu", path_prefix,65 exe_name, pid);66 } else {67 internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid);68 }69 if (common_flags()->log_suffix) {70 internal_strlcat(full_path, common_flags()->log_suffix, kMaxPathLength);71 }72 error_t err;73 fd = OpenFile(full_path, WrOnly, &err);74 if (fd == kInvalidFd) {75 bool fallback = common_flags()->log_fallback_to_stderr;76 const char *ErrorMsgPrefix =77 fallback ? "WARNING: Can't open file, falling back to stderr: "78 : "ERROR: Can't open file: ";79 WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));80 WriteToFile(kStderrFd, full_path, internal_strlen(full_path));81 char errmsg[100];82 internal_snprintf(errmsg, sizeof(errmsg), " (reason: %d)\n", err);83 WriteToFile(kStderrFd, errmsg, internal_strlen(errmsg));84 if (!fallback)85 Die();86 fallbackToStderrActive = true;87 fd = kStderrFd;88 }89 fd_pid = pid;90}91 92static void RecursiveCreateParentDirs(char *path, fd_t &fd) {93 if (path[0] == '\0')94 return;95 for (int i = 1; path[i] != '\0'; ++i) {96 char save = path[i];97 if (!IsPathSeparator(path[i]))98 continue;99 path[i] = '\0';100 if (!DirExists(path) && !CreateDir(path)) {101 bool fallback = common_flags()->log_fallback_to_stderr;102 const char *ErrorMsgPrefix =103 fallback ? "WARNING: Can't create directory, falling back to stderr: "104 : "ERROR: Can't create directory: ";105 WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));106 WriteToFile(kStderrFd, path, internal_strlen(path));107 const char *ErrorMsgSuffix = "\n";108 WriteToFile(kStderrFd, ErrorMsgSuffix, internal_strlen(ErrorMsgSuffix));109 if (!fallback)110 Die();111 path[i] = save;112 fd = kStderrFd;113 return;114 }115 path[i] = save;116 }117}118 119/// Parse the report path \p pattern and copy the parsed path to \p dest.120///121/// * `%%` becomes `%`122/// * `%H` expands to the environment variable `HOME`123/// * `%t` expands to the environment variable `TMPDIR`124/// * `%p` expands to the process ID (PID)125static void ParseAndSetPath(const char *pattern, char *dest,126 const uptr dest_size) {127 CHECK(pattern);128 CHECK(dest);129 CHECK_GE(dest_size, 1);130 dest[0] = '\0';131 // Return empty string if empty string was passed132 if (internal_strlen(pattern) == 0)133 return;134 uptr next_substr_start_idx = 0;135 for (uptr i = 0; i < internal_strlen(pattern) - 1; i++) {136 if (pattern[i] != '%')137 continue;138 int bytes_to_copy = i - next_substr_start_idx;139 // Copy over previous substring.140 CHECK_LT(internal_strlcat(dest, pattern + next_substr_start_idx,141 internal_strlen(dest) + bytes_to_copy + 1),142 dest_size);143 const char *str_to_concat;144 switch (pattern[++i]) {145 case '%':146 str_to_concat = "%";147 break;148 case 'H':149 str_to_concat = GetEnv("HOME");150 break;151 case 't':152 str_to_concat = GetEnv("TMPDIR");153 break;154 case 'p': {155 // Use printf directly to write the PID since it's not a static string.156 int remaining_capacity = dest_size - internal_strlen(dest);157 int bytes_copied =158 internal_snprintf(dest + internal_strlen(dest), remaining_capacity,159 "%ld", internal_getpid());160 CHECK_GT(bytes_copied, 0);161 CHECK_LT(bytes_copied, remaining_capacity);162 str_to_concat = "";163 break;164 }165 default: {166 // Invalid pattern: fallback to original pattern.167 const char *message = "ERROR: Unexpected pattern: ";168 WriteToFile(kStderrFd, message, internal_strlen(message));169 WriteToFile(kStderrFd, pattern, internal_strlen(pattern));170 WriteToFile(kStderrFd, "\n", internal_strlen("\n"));171 CHECK_LT(internal_strlcpy(dest, pattern, dest_size), dest_size);172 return;173 }174 }175 CHECK(str_to_concat);176 CHECK_LT(internal_strlcat(dest, str_to_concat, dest_size), dest_size);177 next_substr_start_idx = i + 1;178 }179 CHECK_LT(internal_strlcat(dest, pattern + next_substr_start_idx, dest_size),180 dest_size);181}182 183void ReportFile::SetReportPath(const char *path) {184 if (path) {185 uptr len = internal_strlen(path);186 if (len > sizeof(path_prefix) - 100) {187 bool fallback = common_flags()->log_fallback_to_stderr;188 const char *message =189 fallback ? "WARNING: Path is too long, falling back to stderr: "190 : "ERROR: Path is too long: ";191 WriteToFile(kStderrFd, message, internal_strlen(message));192 WriteToFile(kStderrFd, path, 8);193 message = "...\n";194 WriteToFile(kStderrFd, message, internal_strlen(message));195 if (!fallback)196 Die();197 path = "stderr";198 }199 }200 201 SpinMutexLock l(mu);202 if (fd != kStdoutFd && fd != kStderrFd && fd != kInvalidFd)203 CloseFile(fd);204 fd = kInvalidFd;205 if (!path || internal_strcmp(path, "stderr") == 0) {206 fd = kStderrFd;207 } else if (internal_strcmp(path, "stdout") == 0) {208 fd = kStdoutFd;209 } else {210 ParseAndSetPath(path, path_prefix, kMaxPathLength);211 RecursiveCreateParentDirs(path_prefix, fd);212 }213}214 215const char *ReportFile::GetReportPath() {216 SpinMutexLock l(mu);217 ReopenIfNecessary();218 return full_path;219}220 221bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,222 uptr *read_len, uptr max_len, error_t *errno_p) {223 *buff = nullptr;224 *buff_size = 0;225 *read_len = 0;226 if (!max_len)227 return true;228 uptr PageSize = GetPageSizeCached();229 uptr kMinFileLen = Min(PageSize, max_len);230 231 // The files we usually open are not seekable, so try different buffer sizes.232 for (uptr size = kMinFileLen;; size = Min(size * 2, max_len)) {233 UnmapOrDie(*buff, *buff_size);234 *buff = (char*)MmapOrDie(size, __func__);235 *buff_size = size;236 fd_t fd = OpenFile(file_name, RdOnly, errno_p);237 if (fd == kInvalidFd) {238 UnmapOrDie(*buff, *buff_size);239 return false;240 }241 *read_len = 0;242 // Read up to one page at a time.243 bool reached_eof = false;244 while (*read_len < size) {245 uptr just_read;246 if (!ReadFromFile(fd, *buff + *read_len, size - *read_len, &just_read,247 errno_p)) {248 UnmapOrDie(*buff, *buff_size);249 CloseFile(fd);250 return false;251 }252 *read_len += just_read;253 if (just_read == 0 || *read_len == max_len) {254 reached_eof = true;255 break;256 }257 }258 CloseFile(fd);259 if (reached_eof) // We've read the whole file.260 break;261 }262 return true;263}264 265bool ReadFileToVector(const char *file_name,266 InternalMmapVectorNoCtor<char> *buff, uptr max_len,267 error_t *errno_p) {268 buff->clear();269 if (!max_len)270 return true;271 uptr PageSize = GetPageSizeCached();272 fd_t fd = OpenFile(file_name, RdOnly, errno_p);273 if (fd == kInvalidFd)274 return false;275 uptr read_len = 0;276 while (read_len < max_len) {277 if (read_len >= buff->size())278 buff->resize(Min(Max(PageSize, read_len * 2), max_len));279 CHECK_LT(read_len, buff->size());280 CHECK_LE(buff->size(), max_len);281 uptr just_read;282 if (!ReadFromFile(fd, buff->data() + read_len, buff->size() - read_len,283 &just_read, errno_p)) {284 CloseFile(fd);285 return false;286 }287 read_len += just_read;288 if (!just_read)289 break;290 }291 CloseFile(fd);292 buff->resize(read_len);293 return true;294}295 296static const char kPathSeparator = SANITIZER_WINDOWS ? ';' : ':';297 298char *FindPathToBinary(const char *name) {299 if (FileExists(name)) {300 return internal_strdup(name);301 }302 303 const char *path = GetEnv("PATH");304 if (!path)305 return nullptr;306 uptr name_len = internal_strlen(name);307 InternalMmapVector<char> buffer(kMaxPathLength);308 const char *beg = path;309 while (true) {310 const char *end = internal_strchrnul(beg, kPathSeparator);311 uptr prefix_len = end - beg;312 if (prefix_len + name_len + 2 <= kMaxPathLength) {313 internal_memcpy(buffer.data(), beg, prefix_len);314 buffer[prefix_len] = '/';315 internal_memcpy(&buffer[prefix_len + 1], name, name_len);316 buffer[prefix_len + 1 + name_len] = '\0';317 if (FileExists(buffer.data()))318 return internal_strdup(buffer.data());319 }320 if (*end == '\0') break;321 beg = end + 1;322 }323 return nullptr;324}325 326} // namespace __sanitizer327 328using namespace __sanitizer;329 330extern "C" {331void __sanitizer_set_report_path(const char *path) {332 report_file.SetReportPath(path);333}334 335void __sanitizer_set_report_fd(void *fd) {336 report_file.fd = (fd_t)reinterpret_cast<uptr>(fd);337 report_file.fd_pid = internal_getpid();338}339 340const char *__sanitizer_get_report_path() {341 return report_file.GetReportPath();342}343} // extern "C"344 345#endif // !SANITIZER_FUCHSIA346