1 //===-- sanitizer_common.cc -----------------------------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file is shared between AddressSanitizer and ThreadSanitizer
11 // run-time libraries.
12 //===----------------------------------------------------------------------===//
14 #include "sanitizer_common.h"
15 #include "sanitizer_allocator_interface.h"
16 #include "sanitizer_allocator_internal.h"
17 #include "sanitizer_flags.h"
18 #include "sanitizer_libc.h"
19 #include "sanitizer_placement_new.h"
20 #include "sanitizer_stacktrace_printer.h"
21 #include "sanitizer_symbolizer.h"
23 namespace __sanitizer
{
25 const char *SanitizerToolName
= "SanitizerTool";
27 atomic_uint32_t current_verbosity
;
30 StaticSpinMutex report_file_mu
;
31 ReportFile report_file
= {&report_file_mu
, kStderrFd
, "", "", 0};
33 void RawWrite(const char *buffer
) {
34 report_file
.Write(buffer
, internal_strlen(buffer
));
37 void ReportFile::ReopenIfNecessary() {
39 if (fd
== kStdoutFd
|| fd
== kStderrFd
) return;
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.
55 const char *exe_name
= GetProcessName();
56 if (common_flags()->log_exe_name
&& exe_name
) {
57 internal_snprintf(full_path
, kMaxPathLength
, "%s.%s.%zu", path_prefix
,
60 internal_snprintf(full_path
, kMaxPathLength
, "%s.%zu", path_prefix
, pid
);
62 fd
= OpenFile(full_path
, WrOnly
);
63 if (fd
== kInvalidFd
) {
64 const char *ErrorMsgPrefix
= "ERROR: Can't open file: ";
65 WriteToFile(kStderrFd
, ErrorMsgPrefix
, internal_strlen(ErrorMsgPrefix
));
66 WriteToFile(kStderrFd
, full_path
, internal_strlen(full_path
));
72 void ReportFile::SetReportPath(const char *path
) {
75 uptr len
= internal_strlen(path
);
76 if (len
> sizeof(path_prefix
) - 100) {
77 Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n",
78 path
[0], path
[1], path
[2], path
[3],
79 path
[4], path
[5], path
[6], path
[7]);
84 if (fd
!= kStdoutFd
&& fd
!= kStderrFd
&& fd
!= kInvalidFd
)
87 if (internal_strcmp(path
, "stdout") == 0) {
89 } else if (internal_strcmp(path
, "stderr") == 0) {
92 internal_snprintf(path_prefix
, kMaxPathLength
, "%s", path
);
96 // PID of the tracer task in StopTheWorld. It shares the address space with the
97 // main process, but has a different PID and thus requires special handling.
98 uptr stoptheworld_tracer_pid
= 0;
99 // Cached pid of parent process - if the parent process dies, we want to keep
100 // writing to the same log file.
101 uptr stoptheworld_tracer_ppid
= 0;
103 void NORETURN
ReportMmapFailureAndDie(uptr size
, const char *mem_type
,
104 const char *mmap_type
, error_t err
,
106 static int recursion_count
;
107 if (raw_report
|| recursion_count
) {
108 // If raw report is requested or we went into recursion, just die.
109 // The Report() and CHECK calls below may call mmap recursively and fail.
110 RawWrite("ERROR: Failed to mmap\n");
114 Report("ERROR: %s failed to "
115 "%s 0x%zx (%zd) bytes of %s (error code: %d)\n",
116 SanitizerToolName
, mmap_type
, size
, size
, mem_type
, err
);
120 UNREACHABLE("unable to mmap");
123 bool ReadFileToBuffer(const char *file_name
, char **buff
, uptr
*buff_size
,
124 uptr
*read_len
, uptr max_len
, error_t
*errno_p
) {
125 uptr PageSize
= GetPageSizeCached();
126 uptr kMinFileLen
= PageSize
;
130 // The files we usually open are not seekable, so try different buffer sizes.
131 for (uptr size
= kMinFileLen
; size
<= max_len
; size
*= 2) {
132 fd_t fd
= OpenFile(file_name
, RdOnly
, errno_p
);
133 if (fd
== kInvalidFd
) return false;
134 UnmapOrDie(*buff
, *buff_size
);
135 *buff
= (char*)MmapOrDie(size
, __func__
);
138 // Read up to one page at a time.
139 bool reached_eof
= false;
140 while (*read_len
+ PageSize
<= size
) {
142 if (!ReadFromFile(fd
, *buff
+ *read_len
, PageSize
, &just_read
, errno_p
)) {
143 UnmapOrDie(*buff
, *buff_size
);
146 if (just_read
== 0) {
150 *read_len
+= just_read
;
153 if (reached_eof
) // We've read the whole file.
159 typedef bool UptrComparisonFunction(const uptr
&a
, const uptr
&b
);
162 static inline bool CompareLess(const T
&a
, const T
&b
) {
166 void SortArray(uptr
*array
, uptr size
) {
167 InternalSort
<uptr
*, UptrComparisonFunction
>(&array
, size
, CompareLess
);
170 const char *StripPathPrefix(const char *filepath
,
171 const char *strip_path_prefix
) {
172 if (!filepath
) return nullptr;
173 if (!strip_path_prefix
) return filepath
;
174 const char *res
= filepath
;
175 if (const char *pos
= internal_strstr(filepath
, strip_path_prefix
))
176 res
= pos
+ internal_strlen(strip_path_prefix
);
177 if (res
[0] == '.' && res
[1] == '/')
182 const char *StripModuleName(const char *module
) {
185 if (SANITIZER_WINDOWS
) {
186 // On Windows, both slash and backslash are possible.
187 // Pick the one that goes last.
188 if (const char *bslash_pos
= internal_strrchr(module
, '\\'))
189 return StripModuleName(bslash_pos
+ 1);
191 if (const char *slash_pos
= internal_strrchr(module
, '/')) {
192 return slash_pos
+ 1;
197 void ReportErrorSummary(const char *error_message
) {
198 if (!common_flags()->print_summary
)
200 InternalScopedString
buff(kMaxSummaryLength
);
201 buff
.append("SUMMARY: %s: %s", SanitizerToolName
, error_message
);
202 __sanitizer_report_error_summary(buff
.data());
206 void ReportErrorSummary(const char *error_type
, const AddressInfo
&info
) {
207 if (!common_flags()->print_summary
)
209 InternalScopedString
buff(kMaxSummaryLength
);
210 buff
.append("%s ", error_type
);
211 RenderFrame(&buff
, "%L %F", 0, info
, common_flags()->symbolize_vs_style
,
212 common_flags()->strip_path_prefix
);
213 ReportErrorSummary(buff
.data());
217 // Removes the ANSI escape sequences from the input string (in-place).
218 void RemoveANSIEscapeSequencesFromString(char *str
) {
222 // We are going to remove the escape sequences in place.
227 // Skip over ANSI escape sequences with pointer 's'.
228 if (*s
== '\033' && *(s
+ 1) == '[') {
229 s
= internal_strchrnul(s
, 'm');
236 // 's' now points at a character we want to keep. Copy over the buffer
237 // content if the escape sequence has been perviously skipped andadvance
242 // If we have not seen an escape sequence, just advance both pointers.
247 // Null terminate the string.
251 void LoadedModule::set(const char *module_name
, uptr base_address
) {
253 full_name_
= internal_strdup(module_name
);
254 base_address_
= base_address
;
257 void LoadedModule::clear() {
258 InternalFree(full_name_
);
259 full_name_
= nullptr;
260 while (!ranges_
.empty()) {
261 AddressRange
*r
= ranges_
.front();
267 void LoadedModule::addAddressRange(uptr beg
, uptr end
, bool executable
) {
268 void *mem
= InternalAlloc(sizeof(AddressRange
));
269 AddressRange
*r
= new(mem
) AddressRange(beg
, end
, executable
);
270 ranges_
.push_back(r
);
273 bool LoadedModule::containsAddress(uptr address
) const {
274 for (const AddressRange
&r
: ranges()) {
275 if (r
.beg
<= address
&& address
< r
.end
)
281 static atomic_uintptr_t g_total_mmaped
;
283 void IncreaseTotalMmap(uptr size
) {
284 if (!common_flags()->mmap_limit_mb
) return;
286 atomic_fetch_add(&g_total_mmaped
, size
, memory_order_relaxed
) + size
;
287 // Since for now mmap_limit_mb is not a user-facing flag, just kill
288 // a program. Use RAW_CHECK to avoid extra mmaps in reporting.
289 RAW_CHECK((total_mmaped
>> 20) < common_flags()->mmap_limit_mb
);
292 void DecreaseTotalMmap(uptr size
) {
293 if (!common_flags()->mmap_limit_mb
) return;
294 atomic_fetch_sub(&g_total_mmaped
, size
, memory_order_relaxed
);
297 bool TemplateMatch(const char *templ
, const char *str
) {
298 if ((!str
) || str
[0] == 0)
301 if (templ
&& templ
[0] == '^') {
305 bool asterisk
= false;
306 while (templ
&& templ
[0]) {
307 if (templ
[0] == '*') {
314 return str
[0] == 0 || asterisk
;
317 char *tpos
= (char*)internal_strchr(templ
, '*');
318 char *tpos1
= (char*)internal_strchr(templ
, '$');
319 if ((!tpos
) || (tpos1
&& tpos1
< tpos
))
323 const char *str0
= str
;
324 const char *spos
= internal_strstr(str
, templ
);
325 str
= spos
+ internal_strlen(templ
);
328 tpos
[0] = tpos
== tpos1
? '$' : '*';
331 if (start
&& spos
!= str0
)
339 static const char kPathSeparator
= SANITIZER_WINDOWS
? ';' : ':';
341 char *FindPathToBinary(const char *name
) {
342 if (FileExists(name
)) {
343 return internal_strdup(name
);
346 const char *path
= GetEnv("PATH");
349 uptr name_len
= internal_strlen(name
);
350 InternalScopedBuffer
<char> buffer(kMaxPathLength
);
351 const char *beg
= path
;
353 const char *end
= internal_strchrnul(beg
, kPathSeparator
);
354 uptr prefix_len
= end
- beg
;
355 if (prefix_len
+ name_len
+ 2 <= kMaxPathLength
) {
356 internal_memcpy(buffer
.data(), beg
, prefix_len
);
357 buffer
[prefix_len
] = '/';
358 internal_memcpy(&buffer
[prefix_len
+ 1], name
, name_len
);
359 buffer
[prefix_len
+ 1 + name_len
] = '\0';
360 if (FileExists(buffer
.data()))
361 return internal_strdup(buffer
.data());
363 if (*end
== '\0') break;
369 static char binary_name_cache_str
[kMaxPathLength
];
370 static char process_name_cache_str
[kMaxPathLength
];
372 const char *GetProcessName() {
373 return process_name_cache_str
;
376 static uptr
ReadProcessName(/*out*/ char *buf
, uptr buf_len
) {
377 ReadLongProcessName(buf
, buf_len
);
378 char *s
= const_cast<char *>(StripModuleName(buf
));
379 uptr len
= internal_strlen(s
);
381 internal_memmove(buf
, s
, len
);
387 void UpdateProcessName() {
388 ReadProcessName(process_name_cache_str
, sizeof(process_name_cache_str
));
391 // Call once to make sure that binary_name_cache_str is initialized
392 void CacheBinaryName() {
393 if (binary_name_cache_str
[0] != '\0')
395 ReadBinaryName(binary_name_cache_str
, sizeof(binary_name_cache_str
));
396 ReadProcessName(process_name_cache_str
, sizeof(process_name_cache_str
));
399 uptr
ReadBinaryNameCached(/*out*/char *buf
, uptr buf_len
) {
401 uptr name_len
= internal_strlen(binary_name_cache_str
);
402 name_len
= (name_len
< buf_len
- 1) ? name_len
: buf_len
- 1;
405 internal_memcpy(buf
, binary_name_cache_str
, name_len
);
406 buf
[name_len
] = '\0';
410 void PrintCmdline() {
411 char **argv
= GetArgv();
413 Printf("\nCommand: ");
414 for (uptr i
= 0; argv
[i
]; ++i
)
415 Printf("%s ", argv
[i
]);
420 static const int kMaxMallocFreeHooks
= 5;
421 struct MallocFreeHook
{
422 void (*malloc_hook
)(const void *, uptr
);
423 void (*free_hook
)(const void *);
426 static MallocFreeHook MFHooks
[kMaxMallocFreeHooks
];
428 void RunMallocHooks(const void *ptr
, uptr size
) {
429 for (int i
= 0; i
< kMaxMallocFreeHooks
; i
++) {
430 auto hook
= MFHooks
[i
].malloc_hook
;
436 void RunFreeHooks(const void *ptr
) {
437 for (int i
= 0; i
< kMaxMallocFreeHooks
; i
++) {
438 auto hook
= MFHooks
[i
].free_hook
;
444 static int InstallMallocFreeHooks(void (*malloc_hook
)(const void *, uptr
),
445 void (*free_hook
)(const void *)) {
446 if (!malloc_hook
|| !free_hook
) return 0;
447 for (int i
= 0; i
< kMaxMallocFreeHooks
; i
++) {
448 if (MFHooks
[i
].malloc_hook
== nullptr) {
449 MFHooks
[i
].malloc_hook
= malloc_hook
;
450 MFHooks
[i
].free_hook
= free_hook
;
457 } // namespace __sanitizer
459 using namespace __sanitizer
; // NOLINT
462 void __sanitizer_set_report_path(const char *path
) {
463 report_file
.SetReportPath(path
);
466 void __sanitizer_set_report_fd(void *fd
) {
467 report_file
.fd
= (fd_t
)reinterpret_cast<uptr
>(fd
);
468 report_file
.fd_pid
= internal_getpid();
471 void __sanitizer_report_error_summary(const char *error_summary
) {
472 Printf("%s\n", error_summary
);
475 SANITIZER_INTERFACE_ATTRIBUTE
476 void __sanitizer_set_death_callback(void (*callback
)(void)) {
477 SetUserDieCallback(callback
);
480 SANITIZER_INTERFACE_ATTRIBUTE
481 int __sanitizer_install_malloc_and_free_hooks(void (*malloc_hook
)(const void *,
483 void (*free_hook
)(const void *)) {
484 return InstallMallocFreeHooks(malloc_hook
, free_hook
);