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)" , 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 | Die(); |
92 | } |
93 | path[i] = save; |
94 | } |
95 | } |
96 | |
97 | void ReportFile::SetReportPath(const char *path) { |
98 | if (path) { |
99 | uptr len = internal_strlen(s: path); |
100 | if (len > sizeof(path_prefix) - 100) { |
101 | Report(format: "ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n" , path[0], path[1], |
102 | path[2], path[3], path[4], path[5], path[6], path[7]); |
103 | Die(); |
104 | } |
105 | } |
106 | |
107 | SpinMutexLock l(mu); |
108 | if (fd != kStdoutFd && fd != kStderrFd && fd != kInvalidFd) |
109 | CloseFile(fd); |
110 | fd = kInvalidFd; |
111 | if (!path || internal_strcmp(s1: path, s2: "stderr" ) == 0) { |
112 | fd = kStderrFd; |
113 | } else if (internal_strcmp(s1: path, s2: "stdout" ) == 0) { |
114 | fd = kStdoutFd; |
115 | } else { |
116 | internal_snprintf(buffer: path_prefix, length: kMaxPathLength, format: "%s" , path); |
117 | RecursiveCreateParentDirs(path: path_prefix); |
118 | } |
119 | } |
120 | |
121 | const char *ReportFile::GetReportPath() { |
122 | SpinMutexLock l(mu); |
123 | ReopenIfNecessary(); |
124 | return full_path; |
125 | } |
126 | |
127 | bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, |
128 | uptr *read_len, uptr max_len, error_t *errno_p) { |
129 | *buff = nullptr; |
130 | *buff_size = 0; |
131 | *read_len = 0; |
132 | if (!max_len) |
133 | return true; |
134 | uptr PageSize = GetPageSizeCached(); |
135 | uptr kMinFileLen = Min(a: PageSize, b: max_len); |
136 | |
137 | // The files we usually open are not seekable, so try different buffer sizes. |
138 | for (uptr size = kMinFileLen;; size = Min(a: size * 2, b: max_len)) { |
139 | UnmapOrDie(addr: *buff, size: *buff_size); |
140 | *buff = (char*)MmapOrDie(size, mem_type: __func__); |
141 | *buff_size = size; |
142 | fd_t fd = OpenFile(filename: file_name, mode: RdOnly, errno_p); |
143 | if (fd == kInvalidFd) { |
144 | UnmapOrDie(addr: *buff, size: *buff_size); |
145 | return false; |
146 | } |
147 | *read_len = 0; |
148 | // Read up to one page at a time. |
149 | bool reached_eof = false; |
150 | while (*read_len < size) { |
151 | uptr just_read; |
152 | if (!ReadFromFile(fd, buff: *buff + *read_len, buff_size: size - *read_len, bytes_read: &just_read, |
153 | error_p: errno_p)) { |
154 | UnmapOrDie(addr: *buff, size: *buff_size); |
155 | CloseFile(fd); |
156 | return false; |
157 | } |
158 | *read_len += just_read; |
159 | if (just_read == 0 || *read_len == max_len) { |
160 | reached_eof = true; |
161 | break; |
162 | } |
163 | } |
164 | CloseFile(fd); |
165 | if (reached_eof) // We've read the whole file. |
166 | break; |
167 | } |
168 | return true; |
169 | } |
170 | |
171 | bool ReadFileToVector(const char *file_name, |
172 | InternalMmapVectorNoCtor<char> *buff, uptr max_len, |
173 | error_t *errno_p) { |
174 | buff->clear(); |
175 | if (!max_len) |
176 | return true; |
177 | uptr PageSize = GetPageSizeCached(); |
178 | fd_t fd = OpenFile(filename: file_name, mode: RdOnly, errno_p); |
179 | if (fd == kInvalidFd) |
180 | return false; |
181 | uptr read_len = 0; |
182 | while (read_len < max_len) { |
183 | if (read_len >= buff->size()) |
184 | buff->resize(new_size: Min(a: Max(a: PageSize, b: read_len * 2), b: max_len)); |
185 | CHECK_LT(read_len, buff->size()); |
186 | CHECK_LE(buff->size(), max_len); |
187 | uptr just_read; |
188 | if (!ReadFromFile(fd, buff: buff->data() + read_len, buff_size: buff->size() - read_len, |
189 | bytes_read: &just_read, error_p: errno_p)) { |
190 | CloseFile(fd); |
191 | return false; |
192 | } |
193 | read_len += just_read; |
194 | if (!just_read) |
195 | break; |
196 | } |
197 | CloseFile(fd); |
198 | buff->resize(new_size: read_len); |
199 | return true; |
200 | } |
201 | |
202 | static const char kPathSeparator = SANITIZER_WINDOWS ? ';' : ':'; |
203 | |
204 | char *FindPathToBinary(const char *name) { |
205 | if (FileExists(filename: name)) { |
206 | return internal_strdup(s: name); |
207 | } |
208 | |
209 | const char *path = GetEnv(name: "PATH" ); |
210 | if (!path) |
211 | return nullptr; |
212 | uptr name_len = internal_strlen(s: name); |
213 | InternalMmapVector<char> buffer(kMaxPathLength); |
214 | const char *beg = path; |
215 | while (true) { |
216 | const char *end = internal_strchrnul(s: beg, c: kPathSeparator); |
217 | uptr prefix_len = end - beg; |
218 | if (prefix_len + name_len + 2 <= kMaxPathLength) { |
219 | internal_memcpy(dest: buffer.data(), src: beg, n: prefix_len); |
220 | buffer[prefix_len] = '/'; |
221 | internal_memcpy(dest: &buffer[prefix_len + 1], src: name, n: name_len); |
222 | buffer[prefix_len + 1 + name_len] = '\0'; |
223 | if (FileExists(filename: buffer.data())) |
224 | return internal_strdup(s: buffer.data()); |
225 | } |
226 | if (*end == '\0') break; |
227 | beg = end + 1; |
228 | } |
229 | return nullptr; |
230 | } |
231 | |
232 | } // namespace __sanitizer |
233 | |
234 | using namespace __sanitizer; |
235 | |
236 | extern "C" { |
237 | void __sanitizer_set_report_path(const char *path) { |
238 | report_file.SetReportPath(path); |
239 | } |
240 | |
241 | void __sanitizer_set_report_fd(void *fd) { |
242 | report_file.fd = (fd_t)reinterpret_cast<uptr>(fd); |
243 | report_file.fd_pid = internal_getpid(); |
244 | } |
245 | |
246 | const char *__sanitizer_get_report_path() { |
247 | return report_file.GetReportPath(); |
248 | } |
249 | } // extern "C" |
250 | |
251 | #endif // !SANITIZER_FUCHSIA |
252 | |