PR target/82524
[official-gcc.git] / libsanitizer / sanitizer_common / sanitizer_common.cc
blobb445f613b85c2a4219964591c7d95fdf255137b1
1 //===-- sanitizer_common.cc -----------------------------------------------===//
2 //
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
5 //
6 //===----------------------------------------------------------------------===//
7 //
8 // This file is shared between AddressSanitizer and ThreadSanitizer
9 // run-time libraries.
10 //===----------------------------------------------------------------------===//
12 #include "sanitizer_common.h"
13 #include "sanitizer_allocator_interface.h"
14 #include "sanitizer_allocator_internal.h"
15 #include "sanitizer_flags.h"
16 #include "sanitizer_libc.h"
17 #include "sanitizer_placement_new.h"
18 #include "sanitizer_stacktrace_printer.h"
19 #include "sanitizer_symbolizer.h"
21 namespace __sanitizer {
23 const char *SanitizerToolName = "SanitizerTool";
25 atomic_uint32_t current_verbosity;
26 uptr PageSizeCached;
28 StaticSpinMutex report_file_mu;
29 ReportFile report_file = {&report_file_mu, kStderrFd, "", "", 0};
31 void RawWrite(const char *buffer) {
32 report_file.Write(buffer, internal_strlen(buffer));
35 void ReportFile::ReopenIfNecessary() {
36 mu->CheckLocked();
37 if (fd == kStdoutFd || fd == kStderrFd) return;
39 uptr pid = internal_getpid();
40 // If in tracer, use the parent's file.
41 if (pid == stoptheworld_tracer_pid)
42 pid = stoptheworld_tracer_ppid;
43 if (fd != kInvalidFd) {
44 // If the report file is already opened by the current process,
45 // do nothing. Otherwise the report file was opened by the parent
46 // process, close it now.
47 if (fd_pid == pid)
48 return;
49 else
50 CloseFile(fd);
53 const char *exe_name = GetProcessName();
54 if (common_flags()->log_exe_name && exe_name) {
55 internal_snprintf(full_path, kMaxPathLength, "%s.%s.%zu", path_prefix,
56 exe_name, pid);
57 } else {
58 internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid);
60 fd = OpenFile(full_path, WrOnly);
61 if (fd == kInvalidFd) {
62 const char *ErrorMsgPrefix = "ERROR: Can't open file: ";
63 WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));
64 WriteToFile(kStderrFd, full_path, internal_strlen(full_path));
65 Die();
67 fd_pid = pid;
70 void ReportFile::SetReportPath(const char *path) {
71 if (!path)
72 return;
73 uptr len = internal_strlen(path);
74 if (len > sizeof(path_prefix) - 100) {
75 Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n",
76 path[0], path[1], path[2], path[3],
77 path[4], path[5], path[6], path[7]);
78 Die();
81 SpinMutexLock l(mu);
82 if (fd != kStdoutFd && fd != kStderrFd && fd != kInvalidFd)
83 CloseFile(fd);
84 fd = kInvalidFd;
85 if (internal_strcmp(path, "stdout") == 0) {
86 fd = kStdoutFd;
87 } else if (internal_strcmp(path, "stderr") == 0) {
88 fd = kStderrFd;
89 } else {
90 internal_snprintf(path_prefix, kMaxPathLength, "%s", path);
94 // PID of the tracer task in StopTheWorld. It shares the address space with the
95 // main process, but has a different PID and thus requires special handling.
96 uptr stoptheworld_tracer_pid = 0;
97 // Cached pid of parent process - if the parent process dies, we want to keep
98 // writing to the same log file.
99 uptr stoptheworld_tracer_ppid = 0;
101 void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type,
102 const char *mmap_type, error_t err,
103 bool raw_report) {
104 static int recursion_count;
105 if (raw_report || recursion_count) {
106 // If raw report is requested or we went into recursion, just die.
107 // The Report() and CHECK calls below may call mmap recursively and fail.
108 RawWrite("ERROR: Failed to mmap\n");
109 Die();
111 recursion_count++;
112 Report("ERROR: %s failed to "
113 "%s 0x%zx (%zd) bytes of %s (error code: %d)\n",
114 SanitizerToolName, mmap_type, size, size, mem_type, err);
115 #if !SANITIZER_GO
116 DumpProcessMap();
117 #endif
118 UNREACHABLE("unable to mmap");
121 bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
122 uptr *read_len, uptr max_len, error_t *errno_p) {
123 uptr PageSize = GetPageSizeCached();
124 uptr kMinFileLen = PageSize;
125 *buff = nullptr;
126 *buff_size = 0;
127 *read_len = 0;
128 // The files we usually open are not seekable, so try different buffer sizes.
129 for (uptr size = kMinFileLen; size <= max_len; size *= 2) {
130 fd_t fd = OpenFile(file_name, RdOnly, errno_p);
131 if (fd == kInvalidFd) return false;
132 UnmapOrDie(*buff, *buff_size);
133 *buff = (char*)MmapOrDie(size, __func__);
134 *buff_size = size;
135 *read_len = 0;
136 // Read up to one page at a time.
137 bool reached_eof = false;
138 while (*read_len + PageSize <= size) {
139 uptr just_read;
140 if (!ReadFromFile(fd, *buff + *read_len, PageSize, &just_read, errno_p)) {
141 UnmapOrDie(*buff, *buff_size);
142 return false;
144 if (just_read == 0) {
145 reached_eof = true;
146 break;
148 *read_len += just_read;
150 CloseFile(fd);
151 if (reached_eof) // We've read the whole file.
152 break;
154 return true;
157 typedef bool UptrComparisonFunction(const uptr &a, const uptr &b);
158 typedef bool U32ComparisonFunction(const u32 &a, const u32 &b);
160 template<class T>
161 static inline bool CompareLess(const T &a, const T &b) {
162 return a < b;
165 void SortArray(uptr *array, uptr size) {
166 InternalSort<uptr*, UptrComparisonFunction>(&array, size, CompareLess);
169 void SortArray(u32 *array, uptr size) {
170 InternalSort<u32*, U32ComparisonFunction>(&array, size, CompareLess);
173 const char *StripPathPrefix(const char *filepath,
174 const char *strip_path_prefix) {
175 if (!filepath) return nullptr;
176 if (!strip_path_prefix) return filepath;
177 const char *res = filepath;
178 if (const char *pos = internal_strstr(filepath, strip_path_prefix))
179 res = pos + internal_strlen(strip_path_prefix);
180 if (res[0] == '.' && res[1] == '/')
181 res += 2;
182 return res;
185 const char *StripModuleName(const char *module) {
186 if (!module)
187 return nullptr;
188 if (SANITIZER_WINDOWS) {
189 // On Windows, both slash and backslash are possible.
190 // Pick the one that goes last.
191 if (const char *bslash_pos = internal_strrchr(module, '\\'))
192 return StripModuleName(bslash_pos + 1);
194 if (const char *slash_pos = internal_strrchr(module, '/')) {
195 return slash_pos + 1;
197 return module;
200 void ReportErrorSummary(const char *error_message) {
201 if (!common_flags()->print_summary)
202 return;
203 InternalScopedString buff(kMaxSummaryLength);
204 buff.append("SUMMARY: %s: %s", SanitizerToolName, error_message);
205 __sanitizer_report_error_summary(buff.data());
208 #if !SANITIZER_GO
209 void ReportErrorSummary(const char *error_type, const AddressInfo &info) {
210 if (!common_flags()->print_summary)
211 return;
212 InternalScopedString buff(kMaxSummaryLength);
213 buff.append("%s ", error_type);
214 RenderFrame(&buff, "%L %F", 0, info, common_flags()->symbolize_vs_style,
215 common_flags()->strip_path_prefix);
216 ReportErrorSummary(buff.data());
218 #endif
220 // Removes the ANSI escape sequences from the input string (in-place).
221 void RemoveANSIEscapeSequencesFromString(char *str) {
222 if (!str)
223 return;
225 // We are going to remove the escape sequences in place.
226 char *s = str;
227 char *z = str;
228 while (*s != '\0') {
229 CHECK_GE(s, z);
230 // Skip over ANSI escape sequences with pointer 's'.
231 if (*s == '\033' && *(s + 1) == '[') {
232 s = internal_strchrnul(s, 'm');
233 if (*s == '\0') {
234 break;
236 s++;
237 continue;
239 // 's' now points at a character we want to keep. Copy over the buffer
240 // content if the escape sequence has been perviously skipped andadvance
241 // both pointers.
242 if (s != z)
243 *z = *s;
245 // If we have not seen an escape sequence, just advance both pointers.
246 z++;
247 s++;
250 // Null terminate the string.
251 *z = '\0';
254 void LoadedModule::set(const char *module_name, uptr base_address) {
255 clear();
256 full_name_ = internal_strdup(module_name);
257 base_address_ = base_address;
260 void LoadedModule::clear() {
261 InternalFree(full_name_);
262 full_name_ = nullptr;
263 while (!ranges_.empty()) {
264 AddressRange *r = ranges_.front();
265 ranges_.pop_front();
266 InternalFree(r);
270 void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable) {
271 void *mem = InternalAlloc(sizeof(AddressRange));
272 AddressRange *r = new(mem) AddressRange(beg, end, executable);
273 ranges_.push_back(r);
276 bool LoadedModule::containsAddress(uptr address) const {
277 for (const AddressRange &r : ranges()) {
278 if (r.beg <= address && address < r.end)
279 return true;
281 return false;
284 static atomic_uintptr_t g_total_mmaped;
286 void IncreaseTotalMmap(uptr size) {
287 if (!common_flags()->mmap_limit_mb) return;
288 uptr total_mmaped =
289 atomic_fetch_add(&g_total_mmaped, size, memory_order_relaxed) + size;
290 // Since for now mmap_limit_mb is not a user-facing flag, just kill
291 // a program. Use RAW_CHECK to avoid extra mmaps in reporting.
292 RAW_CHECK((total_mmaped >> 20) < common_flags()->mmap_limit_mb);
295 void DecreaseTotalMmap(uptr size) {
296 if (!common_flags()->mmap_limit_mb) return;
297 atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed);
300 bool TemplateMatch(const char *templ, const char *str) {
301 if ((!str) || str[0] == 0)
302 return false;
303 bool start = false;
304 if (templ && templ[0] == '^') {
305 start = true;
306 templ++;
308 bool asterisk = false;
309 while (templ && templ[0]) {
310 if (templ[0] == '*') {
311 templ++;
312 start = false;
313 asterisk = true;
314 continue;
316 if (templ[0] == '$')
317 return str[0] == 0 || asterisk;
318 if (str[0] == 0)
319 return false;
320 char *tpos = (char*)internal_strchr(templ, '*');
321 char *tpos1 = (char*)internal_strchr(templ, '$');
322 if ((!tpos) || (tpos1 && tpos1 < tpos))
323 tpos = tpos1;
324 if (tpos)
325 tpos[0] = 0;
326 const char *str0 = str;
327 const char *spos = internal_strstr(str, templ);
328 str = spos + internal_strlen(templ);
329 templ = tpos;
330 if (tpos)
331 tpos[0] = tpos == tpos1 ? '$' : '*';
332 if (!spos)
333 return false;
334 if (start && spos != str0)
335 return false;
336 start = false;
337 asterisk = false;
339 return true;
342 static const char kPathSeparator = SANITIZER_WINDOWS ? ';' : ':';
344 char *FindPathToBinary(const char *name) {
345 if (FileExists(name)) {
346 return internal_strdup(name);
349 const char *path = GetEnv("PATH");
350 if (!path)
351 return nullptr;
352 uptr name_len = internal_strlen(name);
353 InternalScopedBuffer<char> buffer(kMaxPathLength);
354 const char *beg = path;
355 while (true) {
356 const char *end = internal_strchrnul(beg, kPathSeparator);
357 uptr prefix_len = end - beg;
358 if (prefix_len + name_len + 2 <= kMaxPathLength) {
359 internal_memcpy(buffer.data(), beg, prefix_len);
360 buffer[prefix_len] = '/';
361 internal_memcpy(&buffer[prefix_len + 1], name, name_len);
362 buffer[prefix_len + 1 + name_len] = '\0';
363 if (FileExists(buffer.data()))
364 return internal_strdup(buffer.data());
366 if (*end == '\0') break;
367 beg = end + 1;
369 return nullptr;
372 static char binary_name_cache_str[kMaxPathLength];
373 static char process_name_cache_str[kMaxPathLength];
375 const char *GetProcessName() {
376 return process_name_cache_str;
379 static uptr ReadProcessName(/*out*/ char *buf, uptr buf_len) {
380 ReadLongProcessName(buf, buf_len);
381 char *s = const_cast<char *>(StripModuleName(buf));
382 uptr len = internal_strlen(s);
383 if (s != buf) {
384 internal_memmove(buf, s, len);
385 buf[len] = '\0';
387 return len;
390 void UpdateProcessName() {
391 ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));
394 // Call once to make sure that binary_name_cache_str is initialized
395 void CacheBinaryName() {
396 if (binary_name_cache_str[0] != '\0')
397 return;
398 ReadBinaryName(binary_name_cache_str, sizeof(binary_name_cache_str));
399 ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));
402 uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len) {
403 CacheBinaryName();
404 uptr name_len = internal_strlen(binary_name_cache_str);
405 name_len = (name_len < buf_len - 1) ? name_len : buf_len - 1;
406 if (buf_len == 0)
407 return 0;
408 internal_memcpy(buf, binary_name_cache_str, name_len);
409 buf[name_len] = '\0';
410 return name_len;
413 void PrintCmdline() {
414 char **argv = GetArgv();
415 if (!argv) return;
416 Printf("\nCommand: ");
417 for (uptr i = 0; argv[i]; ++i)
418 Printf("%s ", argv[i]);
419 Printf("\n\n");
422 // Malloc hooks.
423 static const int kMaxMallocFreeHooks = 5;
424 struct MallocFreeHook {
425 void (*malloc_hook)(const void *, uptr);
426 void (*free_hook)(const void *);
429 static MallocFreeHook MFHooks[kMaxMallocFreeHooks];
431 void RunMallocHooks(const void *ptr, uptr size) {
432 for (int i = 0; i < kMaxMallocFreeHooks; i++) {
433 auto hook = MFHooks[i].malloc_hook;
434 if (!hook) return;
435 hook(ptr, size);
439 void RunFreeHooks(const void *ptr) {
440 for (int i = 0; i < kMaxMallocFreeHooks; i++) {
441 auto hook = MFHooks[i].free_hook;
442 if (!hook) return;
443 hook(ptr);
447 static int InstallMallocFreeHooks(void (*malloc_hook)(const void *, uptr),
448 void (*free_hook)(const void *)) {
449 if (!malloc_hook || !free_hook) return 0;
450 for (int i = 0; i < kMaxMallocFreeHooks; i++) {
451 if (MFHooks[i].malloc_hook == nullptr) {
452 MFHooks[i].malloc_hook = malloc_hook;
453 MFHooks[i].free_hook = free_hook;
454 return i + 1;
457 return 0;
460 } // namespace __sanitizer
462 using namespace __sanitizer; // NOLINT
464 extern "C" {
465 void __sanitizer_set_report_path(const char *path) {
466 report_file.SetReportPath(path);
469 void __sanitizer_set_report_fd(void *fd) {
470 report_file.fd = (fd_t)reinterpret_cast<uptr>(fd);
471 report_file.fd_pid = internal_getpid();
474 void __sanitizer_report_error_summary(const char *error_summary) {
475 Printf("%s\n", error_summary);
478 SANITIZER_INTERFACE_ATTRIBUTE
479 void __sanitizer_set_death_callback(void (*callback)(void)) {
480 SetUserDieCallback(callback);
483 SANITIZER_INTERFACE_ATTRIBUTE
484 int __sanitizer_install_malloc_and_free_hooks(void (*malloc_hook)(const void *,
485 uptr),
486 void (*free_hook)(const void *)) {
487 return InstallMallocFreeHooks(malloc_hook, free_hook);
490 #if !SANITIZER_GO && !SANITIZER_SUPPORTS_WEAK_HOOKS
491 SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
492 void __sanitizer_print_memory_profile(int top_percent) {
493 (void)top_percent;
495 #endif
496 } // extern "C"