1 //===-- sanitizer_common.cc -----------------------------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file is shared between AddressSanitizer and ThreadSanitizer
11 // run-time libraries.
12 //===----------------------------------------------------------------------===//
13
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"
22
23 namespace __sanitizer {
24
25 const char *SanitizerToolName = "SanitizerTool";
26
27 atomic_uint32_t current_verbosity;
28 uptr PageSizeCached;
29
30 StaticSpinMutex report_file_mu;
31 ReportFile report_file = {&report_file_mu, kStderrFd, "", "", 0};
32
RawWrite(const char * buffer)33 void RawWrite(const char *buffer) {
34 report_file.Write(buffer, internal_strlen(buffer));
35 }
36
ReopenIfNecessary()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(full_path, kMaxPathLength, "%s.%s.%zu", path_prefix,
58 exe_name, pid);
59 } else {
60 internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid);
61 }
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));
67 Die();
68 }
69 fd_pid = pid;
70 }
71
SetReportPath(const char * path)72 void ReportFile::SetReportPath(const char *path) {
73 if (!path)
74 return;
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]);
80 Die();
81 }
82
83 SpinMutexLock l(mu);
84 if (fd != kStdoutFd && fd != kStderrFd && fd != kInvalidFd)
85 CloseFile(fd);
86 fd = kInvalidFd;
87 if (internal_strcmp(path, "stdout") == 0) {
88 fd = kStdoutFd;
89 } else if (internal_strcmp(path, "stderr") == 0) {
90 fd = kStderrFd;
91 } else {
92 internal_snprintf(path_prefix, kMaxPathLength, "%s", path);
93 }
94 }
95
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;
102
ReportMmapFailureAndDie(uptr size,const char * mem_type,const char * mmap_type,error_t err,bool raw_report)103 void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type,
104 const char *mmap_type, error_t err,
105 bool raw_report) {
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");
111 Die();
112 }
113 recursion_count++;
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);
117 #ifndef SANITIZER_GO
118 DumpProcessMap();
119 #endif
120 UNREACHABLE("unable to mmap");
121 }
122
ReadFileToBuffer(const char * file_name,char ** buff,uptr * buff_size,uptr * read_len,uptr max_len,error_t * errno_p)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;
127 *buff = nullptr;
128 *buff_size = 0;
129 *read_len = 0;
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__);
136 *buff_size = size;
137 *read_len = 0;
138 // Read up to one page at a time.
139 bool reached_eof = false;
140 while (*read_len + PageSize <= size) {
141 uptr just_read;
142 if (!ReadFromFile(fd, *buff + *read_len, PageSize, &just_read, errno_p)) {
143 UnmapOrDie(*buff, *buff_size);
144 return false;
145 }
146 if (just_read == 0) {
147 reached_eof = true;
148 break;
149 }
150 *read_len += just_read;
151 }
152 CloseFile(fd);
153 if (reached_eof) // We've read the whole file.
154 break;
155 }
156 return true;
157 }
158
159 typedef bool UptrComparisonFunction(const uptr &a, const uptr &b);
160
161 template<class T>
CompareLess(const T & a,const T & b)162 static inline bool CompareLess(const T &a, const T &b) {
163 return a < b;
164 }
165
SortArray(uptr * array,uptr size)166 void SortArray(uptr *array, uptr size) {
167 InternalSort<uptr*, UptrComparisonFunction>(&array, size, CompareLess);
168 }
169
StripPathPrefix(const char * filepath,const char * strip_path_prefix)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] == '/')
178 res += 2;
179 return res;
180 }
181
StripModuleName(const char * module)182 const char *StripModuleName(const char *module) {
183 if (!module)
184 return nullptr;
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);
190 }
191 if (const char *slash_pos = internal_strrchr(module, '/')) {
192 return slash_pos + 1;
193 }
194 return module;
195 }
196
ReportErrorSummary(const char * error_message)197 void ReportErrorSummary(const char *error_message) {
198 if (!common_flags()->print_summary)
199 return;
200 InternalScopedString buff(kMaxSummaryLength);
201 buff.append("SUMMARY: %s: %s", SanitizerToolName, error_message);
202 __sanitizer_report_error_summary(buff.data());
203 }
204
205 #ifndef SANITIZER_GO
ReportErrorSummary(const char * error_type,const AddressInfo & info)206 void ReportErrorSummary(const char *error_type, const AddressInfo &info) {
207 if (!common_flags()->print_summary)
208 return;
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());
214 }
215 #endif
216
217 // Removes the ANSI escape sequences from the input string (in-place).
RemoveANSIEscapeSequencesFromString(char * str)218 void RemoveANSIEscapeSequencesFromString(char *str) {
219 if (!str)
220 return;
221
222 // We are going to remove the escape sequences in place.
223 char *s = str;
224 char *z = str;
225 while (*s != '\0') {
226 CHECK_GE(s, z);
227 // Skip over ANSI escape sequences with pointer 's'.
228 if (*s == '\033' && *(s + 1) == '[') {
229 s = internal_strchrnul(s, 'm');
230 if (*s == '\0') {
231 break;
232 }
233 s++;
234 continue;
235 }
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
238 // both pointers.
239 if (s != z)
240 *z = *s;
241
242 // If we have not seen an escape sequence, just advance both pointers.
243 z++;
244 s++;
245 }
246
247 // Null terminate the string.
248 *z = '\0';
249 }
250
set(const char * module_name,uptr base_address)251 void LoadedModule::set(const char *module_name, uptr base_address) {
252 clear();
253 full_name_ = internal_strdup(module_name);
254 base_address_ = base_address;
255 }
256
clear()257 void LoadedModule::clear() {
258 InternalFree(full_name_);
259 full_name_ = nullptr;
260 while (!ranges_.empty()) {
261 AddressRange *r = ranges_.front();
262 ranges_.pop_front();
263 InternalFree(r);
264 }
265 }
266
addAddressRange(uptr beg,uptr end,bool executable)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);
271 }
272
containsAddress(uptr address) const273 bool LoadedModule::containsAddress(uptr address) const {
274 for (const AddressRange &r : ranges()) {
275 if (r.beg <= address && address < r.end)
276 return true;
277 }
278 return false;
279 }
280
281 static atomic_uintptr_t g_total_mmaped;
282
IncreaseTotalMmap(uptr size)283 void IncreaseTotalMmap(uptr size) {
284 if (!common_flags()->mmap_limit_mb) return;
285 uptr total_mmaped =
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);
290 }
291
DecreaseTotalMmap(uptr size)292 void DecreaseTotalMmap(uptr size) {
293 if (!common_flags()->mmap_limit_mb) return;
294 atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed);
295 }
296
TemplateMatch(const char * templ,const char * str)297 bool TemplateMatch(const char *templ, const char *str) {
298 if ((!str) || str[0] == 0)
299 return false;
300 bool start = false;
301 if (templ && templ[0] == '^') {
302 start = true;
303 templ++;
304 }
305 bool asterisk = false;
306 while (templ && templ[0]) {
307 if (templ[0] == '*') {
308 templ++;
309 start = false;
310 asterisk = true;
311 continue;
312 }
313 if (templ[0] == '$')
314 return str[0] == 0 || asterisk;
315 if (str[0] == 0)
316 return false;
317 char *tpos = (char*)internal_strchr(templ, '*');
318 char *tpos1 = (char*)internal_strchr(templ, '$');
319 if ((!tpos) || (tpos1 && tpos1 < tpos))
320 tpos = tpos1;
321 if (tpos)
322 tpos[0] = 0;
323 const char *str0 = str;
324 const char *spos = internal_strstr(str, templ);
325 str = spos + internal_strlen(templ);
326 templ = tpos;
327 if (tpos)
328 tpos[0] = tpos == tpos1 ? '$' : '*';
329 if (!spos)
330 return false;
331 if (start && spos != str0)
332 return false;
333 start = false;
334 asterisk = false;
335 }
336 return true;
337 }
338
339 static const char kPathSeparator = SANITIZER_WINDOWS ? ';' : ':';
340
FindPathToBinary(const char * name)341 char *FindPathToBinary(const char *name) {
342 if (FileExists(name)) {
343 return internal_strdup(name);
344 }
345
346 const char *path = GetEnv("PATH");
347 if (!path)
348 return nullptr;
349 uptr name_len = internal_strlen(name);
350 InternalScopedBuffer<char> buffer(kMaxPathLength);
351 const char *beg = path;
352 while (true) {
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());
362 }
363 if (*end == '\0') break;
364 beg = end + 1;
365 }
366 return nullptr;
367 }
368
369 static char binary_name_cache_str[kMaxPathLength];
370 static char process_name_cache_str[kMaxPathLength];
371
GetProcessName()372 const char *GetProcessName() {
373 return process_name_cache_str;
374 }
375
ReadProcessName(char * buf,uptr buf_len)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);
380 if (s != buf) {
381 internal_memmove(buf, s, len);
382 buf[len] = '\0';
383 }
384 return len;
385 }
386
UpdateProcessName()387 void UpdateProcessName() {
388 ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));
389 }
390
391 // Call once to make sure that binary_name_cache_str is initialized
CacheBinaryName()392 void CacheBinaryName() {
393 if (binary_name_cache_str[0] != '\0')
394 return;
395 ReadBinaryName(binary_name_cache_str, sizeof(binary_name_cache_str));
396 ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));
397 }
398
ReadBinaryNameCached(char * buf,uptr buf_len)399 uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len) {
400 CacheBinaryName();
401 uptr name_len = internal_strlen(binary_name_cache_str);
402 name_len = (name_len < buf_len - 1) ? name_len : buf_len - 1;
403 if (buf_len == 0)
404 return 0;
405 internal_memcpy(buf, binary_name_cache_str, name_len);
406 buf[name_len] = '\0';
407 return name_len;
408 }
409
PrintCmdline()410 void PrintCmdline() {
411 char **argv = GetArgv();
412 if (!argv) return;
413 Printf("\nCommand: ");
414 for (uptr i = 0; argv[i]; ++i)
415 Printf("%s ", argv[i]);
416 Printf("\n\n");
417 }
418
419 // Malloc hooks.
420 static const int kMaxMallocFreeHooks = 5;
421 struct MallocFreeHook {
422 void (*malloc_hook)(const void *, uptr);
423 void (*free_hook)(const void *);
424 };
425
426 static MallocFreeHook MFHooks[kMaxMallocFreeHooks];
427
RunMallocHooks(const void * ptr,uptr size)428 void RunMallocHooks(const void *ptr, uptr size) {
429 for (int i = 0; i < kMaxMallocFreeHooks; i++) {
430 auto hook = MFHooks[i].malloc_hook;
431 if (!hook) return;
432 hook(ptr, size);
433 }
434 }
435
RunFreeHooks(const void * ptr)436 void RunFreeHooks(const void *ptr) {
437 for (int i = 0; i < kMaxMallocFreeHooks; i++) {
438 auto hook = MFHooks[i].free_hook;
439 if (!hook) return;
440 hook(ptr);
441 }
442 }
443
InstallMallocFreeHooks(void (* malloc_hook)(const void *,uptr),void (* free_hook)(const void *))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;
451 return i + 1;
452 }
453 }
454 return 0;
455 }
456
457 } // namespace __sanitizer
458
459 using namespace __sanitizer; // NOLINT
460
461 extern "C" {
__sanitizer_set_report_path(const char * path)462 void __sanitizer_set_report_path(const char *path) {
463 report_file.SetReportPath(path);
464 }
465
__sanitizer_set_report_fd(void * fd)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();
469 }
470
__sanitizer_report_error_summary(const char * error_summary)471 void __sanitizer_report_error_summary(const char *error_summary) {
472 Printf("%s\n", error_summary);
473 }
474
475 SANITIZER_INTERFACE_ATTRIBUTE
__sanitizer_set_death_callback(void (* callback)(void))476 void __sanitizer_set_death_callback(void (*callback)(void)) {
477 SetUserDieCallback(callback);
478 }
479
480 SANITIZER_INTERFACE_ATTRIBUTE
__sanitizer_install_malloc_and_free_hooks(void (* malloc_hook)(const void *,uptr),void (* free_hook)(const void *))481 int __sanitizer_install_malloc_and_free_hooks(void (*malloc_hook)(const void *,
482 uptr),
483 void (*free_hook)(const void *)) {
484 return InstallMallocFreeHooks(malloc_hook, free_hook);
485 }
486 } // extern "C"
487