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 | |
24 | namespace __sanitizer { |
25 | |
26 | void CatastrophicErrorWrite(const char *buffer, uptr length) { |
27 | WriteToFile(kStderrFd, buff: buffer, buff_size: length); |
28 | } |
29 | |
30 | StaticSpinMutex report_file_mu; |
31 | ReportFile report_file = {.mu: &report_file_mu, kStderrFd, .path_prefix: "" , .full_path: "" , .fd_pid: 0}; |
32 | |
33 | void RawWrite(const char *buffer) { |
34 | report_file.Write(buffer, length: internal_strlen(s: buffer)); |
35 | } |
36 | |
37 | void ReportFile::ReopenIfNecessary() { |
38 | mu->CheckLocked(); |
39 | if (fd == kStdoutFd || fd == kStderrFd) return; |
40 | |
41 | uptr pid = internal_getpid(); |
42 | // If in tracer, use the parent's file. |
43 | if (pid == stoptheworld_tracer_pid) |
44 | pid = stoptheworld_tracer_ppid; |
45 | if (fd != kInvalidFd) { |
46 | // If the report file is already opened by the current process, |
47 | // do nothing. Otherwise the report file was opened by the parent |
48 | // process, close it now. |
49 | if (fd_pid == pid) |
50 | return; |
51 | else |
52 | CloseFile(fd); |
53 | } |
54 | |
55 | const char *exe_name = GetProcessName(); |
56 | if (common_flags()->log_exe_name && exe_name) { |
57 | internal_snprintf(buffer: full_path, length: kMaxPathLength, format: "%s.%s.%zu" , path_prefix, |
58 | exe_name, pid); |
59 | } else { |
60 | internal_snprintf(buffer: full_path, length: kMaxPathLength, format: "%s.%zu" , path_prefix, pid); |
61 | } |
62 | if (common_flags()->log_suffix) { |
63 | internal_strlcat(dst: full_path, src: common_flags()->log_suffix, maxlen: kMaxPathLength); |
64 | } |
65 | error_t err; |
66 | fd = OpenFile(filename: full_path, mode: WrOnly, errno_p: &err); |
67 | if (fd == kInvalidFd) { |
68 | const char *ErrorMsgPrefix = "ERROR: Can't open file: " ; |
69 | WriteToFile(kStderrFd, buff: ErrorMsgPrefix, buff_size: internal_strlen(s: ErrorMsgPrefix)); |
70 | WriteToFile(kStderrFd, buff: full_path, buff_size: internal_strlen(s: full_path)); |
71 | char errmsg[100]; |
72 | internal_snprintf(buffer: errmsg, length: sizeof(errmsg), format: " (reason: %d)\n" , err); |
73 | WriteToFile(kStderrFd, buff: errmsg, buff_size: internal_strlen(s: errmsg)); |
74 | Die(); |
75 | } |
76 | fd_pid = pid; |
77 | } |
78 | |
79 | static void RecursiveCreateParentDirs(char *path) { |
80 | if (path[0] == '\0') |
81 | return; |
82 | for (int i = 1; path[i] != '\0'; ++i) { |
83 | char save = path[i]; |
84 | if (!IsPathSeparator(c: path[i])) |
85 | continue; |
86 | path[i] = '\0'; |
87 | if (!DirExists(path) && !CreateDir(pathname: path)) { |
88 | const char *ErrorMsgPrefix = "ERROR: Can't create directory: " ; |
89 | WriteToFile(kStderrFd, buff: ErrorMsgPrefix, buff_size: internal_strlen(s: ErrorMsgPrefix)); |
90 | WriteToFile(kStderrFd, buff: path, buff_size: internal_strlen(s: path)); |
91 | const char *ErrorMsgSuffix = "\n" ; |
92 | WriteToFile(kStderrFd, buff: ErrorMsgSuffix, buff_size: internal_strlen(s: ErrorMsgSuffix)); |
93 | Die(); |
94 | } |
95 | path[i] = save; |
96 | } |
97 | } |
98 | |
99 | /// Parse the report path \p pattern and copy the parsed path to \p dest. |
100 | /// |
101 | /// * `%%` becomes `%` |
102 | /// * `%H` expands to the environment variable `HOME` |
103 | /// * `%t` expands to the environment variable `TMPDIR` |
104 | /// * `%p` expands to the process ID (PID) |
105 | static void ParseAndSetPath(const char *pattern, char *dest, |
106 | const uptr dest_size) { |
107 | CHECK(pattern); |
108 | CHECK(dest); |
109 | CHECK_GE(dest_size, 1); |
110 | dest[0] = '\0'; |
111 | uptr next_substr_start_idx = 0; |
112 | for (uptr i = 0; i < internal_strlen(s: pattern) - 1; i++) { |
113 | if (pattern[i] != '%') |
114 | continue; |
115 | int bytes_to_copy = i - next_substr_start_idx; |
116 | // Copy over previous substring. |
117 | CHECK_LT(internal_strlcat(dest, pattern + next_substr_start_idx, |
118 | internal_strlen(dest) + bytes_to_copy + 1), |
119 | dest_size); |
120 | const char *str_to_concat; |
121 | switch (pattern[++i]) { |
122 | case '%': |
123 | str_to_concat = "%" ; |
124 | break; |
125 | case 'H': |
126 | str_to_concat = GetEnv(name: "HOME" ); |
127 | break; |
128 | case 't': |
129 | str_to_concat = GetEnv(name: "TMPDIR" ); |
130 | break; |
131 | case 'p': { |
132 | // Use printf directly to write the PID since it's not a static string. |
133 | int remaining_capacity = dest_size - internal_strlen(s: dest); |
134 | int bytes_copied = |
135 | internal_snprintf(buffer: dest + internal_strlen(s: dest), length: remaining_capacity, |
136 | format: "%ld" , internal_getpid()); |
137 | CHECK_GT(bytes_copied, 0); |
138 | CHECK_LT(bytes_copied, remaining_capacity); |
139 | str_to_concat = "" ; |
140 | break; |
141 | } |
142 | default: { |
143 | // Invalid pattern: fallback to original pattern. |
144 | const char *message = "ERROR: Unexpected pattern: " ; |
145 | WriteToFile(kStderrFd, buff: message, buff_size: internal_strlen(s: message)); |
146 | WriteToFile(kStderrFd, buff: pattern, buff_size: internal_strlen(s: pattern)); |
147 | WriteToFile(kStderrFd, buff: "\n" , buff_size: internal_strlen(s: "\n" )); |
148 | CHECK_LT(internal_strlcpy(dest, pattern, dest_size), dest_size); |
149 | return; |
150 | } |
151 | } |
152 | CHECK(str_to_concat); |
153 | CHECK_LT(internal_strlcat(dest, str_to_concat, dest_size), dest_size); |
154 | next_substr_start_idx = i + 1; |
155 | } |
156 | CHECK_LT(internal_strlcat(dest, pattern + next_substr_start_idx, dest_size), |
157 | dest_size); |
158 | } |
159 | |
160 | void ReportFile::SetReportPath(const char *path) { |
161 | if (path) { |
162 | uptr len = internal_strlen(s: path); |
163 | if (len > sizeof(path_prefix) - 100) { |
164 | const char *message = "ERROR: Path is too long: " ; |
165 | WriteToFile(kStderrFd, buff: message, buff_size: internal_strlen(s: message)); |
166 | WriteToFile(kStderrFd, buff: path, buff_size: 8); |
167 | message = "...\n" ; |
168 | WriteToFile(kStderrFd, buff: message, buff_size: internal_strlen(s: message)); |
169 | Die(); |
170 | } |
171 | } |
172 | |
173 | SpinMutexLock l(mu); |
174 | if (fd != kStdoutFd && fd != kStderrFd && fd != kInvalidFd) |
175 | CloseFile(fd); |
176 | fd = kInvalidFd; |
177 | if (!path || internal_strcmp(s1: path, s2: "stderr" ) == 0) { |
178 | fd = kStderrFd; |
179 | } else if (internal_strcmp(s1: path, s2: "stdout" ) == 0) { |
180 | fd = kStdoutFd; |
181 | } else { |
182 | ParseAndSetPath(pattern: path, dest: path_prefix, dest_size: kMaxPathLength); |
183 | RecursiveCreateParentDirs(path: path_prefix); |
184 | } |
185 | } |
186 | |
187 | const char *ReportFile::GetReportPath() { |
188 | SpinMutexLock l(mu); |
189 | ReopenIfNecessary(); |
190 | return full_path; |
191 | } |
192 | |
193 | bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, |
194 | uptr *read_len, uptr max_len, error_t *errno_p) { |
195 | *buff = nullptr; |
196 | *buff_size = 0; |
197 | *read_len = 0; |
198 | if (!max_len) |
199 | return true; |
200 | uptr PageSize = GetPageSizeCached(); |
201 | uptr kMinFileLen = Min(a: PageSize, b: max_len); |
202 | |
203 | // The files we usually open are not seekable, so try different buffer sizes. |
204 | for (uptr size = kMinFileLen;; size = Min(a: size * 2, b: max_len)) { |
205 | UnmapOrDie(addr: *buff, size: *buff_size); |
206 | *buff = (char*)MmapOrDie(size, mem_type: __func__); |
207 | *buff_size = size; |
208 | fd_t fd = OpenFile(filename: file_name, mode: RdOnly, errno_p); |
209 | if (fd == kInvalidFd) { |
210 | UnmapOrDie(addr: *buff, size: *buff_size); |
211 | return false; |
212 | } |
213 | *read_len = 0; |
214 | // Read up to one page at a time. |
215 | bool reached_eof = false; |
216 | while (*read_len < size) { |
217 | uptr just_read; |
218 | if (!ReadFromFile(fd, buff: *buff + *read_len, buff_size: size - *read_len, bytes_read: &just_read, |
219 | error_p: errno_p)) { |
220 | UnmapOrDie(addr: *buff, size: *buff_size); |
221 | CloseFile(fd); |
222 | return false; |
223 | } |
224 | *read_len += just_read; |
225 | if (just_read == 0 || *read_len == max_len) { |
226 | reached_eof = true; |
227 | break; |
228 | } |
229 | } |
230 | CloseFile(fd); |
231 | if (reached_eof) // We've read the whole file. |
232 | break; |
233 | } |
234 | return true; |
235 | } |
236 | |
237 | bool ReadFileToVector(const char *file_name, |
238 | InternalMmapVectorNoCtor<char> *buff, uptr max_len, |
239 | error_t *errno_p) { |
240 | buff->clear(); |
241 | if (!max_len) |
242 | return true; |
243 | uptr PageSize = GetPageSizeCached(); |
244 | fd_t fd = OpenFile(filename: file_name, mode: RdOnly, errno_p); |
245 | if (fd == kInvalidFd) |
246 | return false; |
247 | uptr read_len = 0; |
248 | while (read_len < max_len) { |
249 | if (read_len >= buff->size()) |
250 | buff->resize(new_size: Min(a: Max(a: PageSize, b: read_len * 2), b: max_len)); |
251 | CHECK_LT(read_len, buff->size()); |
252 | CHECK_LE(buff->size(), max_len); |
253 | uptr just_read; |
254 | if (!ReadFromFile(fd, buff: buff->data() + read_len, buff_size: buff->size() - read_len, |
255 | bytes_read: &just_read, error_p: errno_p)) { |
256 | CloseFile(fd); |
257 | return false; |
258 | } |
259 | read_len += just_read; |
260 | if (!just_read) |
261 | break; |
262 | } |
263 | CloseFile(fd); |
264 | buff->resize(new_size: read_len); |
265 | return true; |
266 | } |
267 | |
268 | static const char kPathSeparator = SANITIZER_WINDOWS ? ';' : ':'; |
269 | |
270 | char *FindPathToBinary(const char *name) { |
271 | if (FileExists(filename: name)) { |
272 | return internal_strdup(s: name); |
273 | } |
274 | |
275 | const char *path = GetEnv(name: "PATH" ); |
276 | if (!path) |
277 | return nullptr; |
278 | uptr name_len = internal_strlen(s: name); |
279 | InternalMmapVector<char> buffer(kMaxPathLength); |
280 | const char *beg = path; |
281 | while (true) { |
282 | const char *end = internal_strchrnul(s: beg, c: kPathSeparator); |
283 | uptr prefix_len = end - beg; |
284 | if (prefix_len + name_len + 2 <= kMaxPathLength) { |
285 | internal_memcpy(dest: buffer.data(), src: beg, n: prefix_len); |
286 | buffer[prefix_len] = '/'; |
287 | internal_memcpy(dest: &buffer[prefix_len + 1], src: name, n: name_len); |
288 | buffer[prefix_len + 1 + name_len] = '\0'; |
289 | if (FileExists(filename: buffer.data())) |
290 | return internal_strdup(s: buffer.data()); |
291 | } |
292 | if (*end == '\0') break; |
293 | beg = end + 1; |
294 | } |
295 | return nullptr; |
296 | } |
297 | |
298 | } // namespace __sanitizer |
299 | |
300 | using namespace __sanitizer; |
301 | |
302 | extern "C" { |
303 | void __sanitizer_set_report_path(const char *path) { |
304 | report_file.SetReportPath(path); |
305 | } |
306 | |
307 | void __sanitizer_set_report_fd(void *fd) { |
308 | report_file.fd = (fd_t)reinterpret_cast<uptr>(fd); |
309 | report_file.fd_pid = internal_getpid(); |
310 | } |
311 | |
312 | const char *__sanitizer_get_report_path() { |
313 | return report_file.GetReportPath(); |
314 | } |
315 | } // extern "C" |
316 | |
317 | #endif // !SANITIZER_FUCHSIA |
318 | |