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