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-exception
6//
7//===---------------------------------------------------------------------===//
8//
9// This file is shared between AddressSanitizer and ThreadSanitizer
10// run-time libraries. It defines filesystem-related interfaces. This
11// is separate from sanitizer_common.cpp so that it's simpler to disable
12// 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_FUCHSIA
19
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, buff: buffer, buff_size: length);
28}
29
30StaticSpinMutex report_file_mu;
31ReportFile report_file = {.mu: &report_file_mu, kStderrFd, .path_prefix: "", .full_path: "", .fd_pid: 0};
32
33void RawWrite(const char *buffer) {
34 report_file.Write(buffer, length: internal_strlen(s: 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 a
42 // 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 parent
56 // 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(buffer: full_path, length: kMaxPathLength, format: "%s.%s.%zu", path_prefix,
65 exe_name, pid);
66 } else {
67 internal_snprintf(buffer: full_path, length: kMaxPathLength, format: "%s.%zu", path_prefix, pid);
68 }
69 if (common_flags()->log_suffix) {
70 internal_strlcat(dst: full_path, src: common_flags()->log_suffix, maxlen: kMaxPathLength);
71 }
72 error_t err;
73 fd = OpenFile(filename: full_path, mode: WrOnly, errno_p: &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, buff: ErrorMsgPrefix, buff_size: internal_strlen(s: ErrorMsgPrefix));
80 WriteToFile(kStderrFd, buff: full_path, buff_size: internal_strlen(s: full_path));
81 char errmsg[100];
82 internal_snprintf(buffer: errmsg, length: sizeof(errmsg), format: " (reason: %d)\n", err);
83 WriteToFile(kStderrFd, buff: errmsg, buff_size: internal_strlen(s: 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(c: path[i]))
98 continue;
99 path[i] = '\0';
100 if (!DirExists(path) && !CreateDir(pathname: 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, buff: ErrorMsgPrefix, buff_size: internal_strlen(s: ErrorMsgPrefix));
106 WriteToFile(kStderrFd, buff: path, buff_size: internal_strlen(s: path));
107 const char *ErrorMsgSuffix = "\n";
108 WriteToFile(kStderrFd, buff: ErrorMsgSuffix, buff_size: internal_strlen(s: 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 passed
132 if (internal_strlen(s: pattern) == 0)
133 return;
134 uptr next_substr_start_idx = 0;
135 for (uptr i = 0; i < internal_strlen(s: 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(name: "HOME");
150 break;
151 case 't':
152 str_to_concat = GetEnv(name: "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(s: dest);
157 int bytes_copied =
158 internal_snprintf(buffer: dest + internal_strlen(s: dest), length: remaining_capacity,
159 format: "%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, buff: message, buff_size: internal_strlen(s: message));
169 WriteToFile(kStderrFd, buff: pattern, buff_size: internal_strlen(s: pattern));
170 WriteToFile(kStderrFd, buff: "\n", buff_size: internal_strlen(s: "\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(s: 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, buff: message, buff_size: internal_strlen(s: message));
192 WriteToFile(kStderrFd, buff: path, buff_size: 8);
193 message = "...\n";
194 WriteToFile(kStderrFd, buff: message, buff_size: internal_strlen(s: 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(s1: path, s2: "stderr") == 0) {
206 fd = kStderrFd;
207 } else if (internal_strcmp(s1: path, s2: "stdout") == 0) {
208 fd = kStdoutFd;
209 } else {
210 ParseAndSetPath(pattern: path, dest: path_prefix, dest_size: kMaxPathLength);
211 RecursiveCreateParentDirs(path: 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(a: PageSize, b: max_len);
230
231 // The files we usually open are not seekable, so try different buffer sizes.
232 for (uptr size = kMinFileLen;; size = Min(a: size * 2, b: max_len)) {
233 UnmapOrDie(addr: *buff, size: *buff_size);
234 *buff = (char*)MmapOrDie(size, mem_type: __func__);
235 *buff_size = size;
236 fd_t fd = OpenFile(filename: file_name, mode: RdOnly, errno_p);
237 if (fd == kInvalidFd) {
238 UnmapOrDie(addr: *buff, size: *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: *buff + *read_len, buff_size: size - *read_len, bytes_read: &just_read,
247 error_p: errno_p)) {
248 UnmapOrDie(addr: *buff, size: *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(filename: file_name, mode: 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(new_size: Min(a: Max(a: PageSize, b: read_len * 2), b: 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: buff->data() + read_len, buff_size: buff->size() - read_len,
283 bytes_read: &just_read, error_p: 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(new_size: read_len);
293 return true;
294}
295
296static const char kPathSeparator = SANITIZER_WINDOWS ? ';' : ':';
297
298char *FindPathToBinary(const char *name) {
299 if (FileExists(filename: name)) {
300 return internal_strdup(s: name);
301 }
302
303 const char *path = GetEnv(name: "PATH");
304 if (!path)
305 return nullptr;
306 uptr name_len = internal_strlen(s: name);
307 InternalMmapVector<char> buffer(kMaxPathLength);
308 const char *beg = path;
309 while (true) {
310 const char *end = internal_strchrnul(s: beg, c: kPathSeparator);
311 uptr prefix_len = end - beg;
312 if (prefix_len + name_len + 2 <= kMaxPathLength) {
313 internal_memcpy(dest: buffer.data(), src: beg, n: prefix_len);
314 buffer[prefix_len] = '/';
315 internal_memcpy(dest: &buffer[prefix_len + 1], src: name, n: name_len);
316 buffer[prefix_len + 1 + name_len] = '\0';
317 if (FileExists(filename: buffer.data()))
318 return internal_strdup(s: buffer.data());
319 }
320 if (*end == '\0') break;
321 beg = end + 1;
322 }
323 return nullptr;
324}
325
326} // namespace __sanitizer
327
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_FUCHSIA
346