xref: /freebsd/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_common.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1 //===-- sanitizer_common.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.
11 //===----------------------------------------------------------------------===//
12 
13 #include "sanitizer_common.h"
14 
15 #include "sanitizer_allocator_interface.h"
16 #include "sanitizer_allocator_internal.h"
17 #include "sanitizer_atomic.h"
18 #include "sanitizer_flags.h"
19 #include "sanitizer_interface_internal.h"
20 #include "sanitizer_libc.h"
21 #include "sanitizer_placement_new.h"
22 
23 namespace __sanitizer {
24 
25 const char *SanitizerToolName = "SanitizerTool";
26 
27 atomic_uint32_t current_verbosity;
28 uptr PageSizeCached;
29 u32 NumberOfCPUsCached;
30 
31 // PID of the tracer task in StopTheWorld. It shares the address space with the
32 // main process, but has a different PID and thus requires special handling.
33 uptr stoptheworld_tracer_pid = 0;
34 // Cached pid of parent process - if the parent process dies, we want to keep
35 // writing to the same log file.
36 uptr stoptheworld_tracer_ppid = 0;
37 
ReportMmapFailureAndDie(uptr size,const char * mem_type,const char * mmap_type,error_t err,bool raw_report)38 void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type,
39                                       const char *mmap_type, error_t err,
40                                       bool raw_report) {
41   static int recursion_count;
42   if (raw_report || recursion_count) {
43     // If raw report is requested or we went into recursion just die.  The
44     // Report() and CHECK calls below may call mmap recursively and fail.
45     RawWrite("ERROR: Failed to mmap\n");
46     Die();
47   }
48   recursion_count++;
49   if (ErrorIsOOM(err)) {
50     ERROR_OOM("failed to %s 0x%zx (%zd) bytes of %s (error code: %d)\n",
51               mmap_type, size, size, mem_type, err);
52   } else {
53     Report(
54         "ERROR: %s failed to "
55         "%s 0x%zx (%zd) bytes of %s (error code: %d)\n",
56         SanitizerToolName, mmap_type, size, size, mem_type, err);
57   }
58 #if !SANITIZER_GO
59   DumpProcessMap();
60 #endif
61   UNREACHABLE("unable to mmap");
62 }
63 
ReportMunmapFailureAndDie(void * addr,uptr size,error_t err,bool raw_report)64 void NORETURN ReportMunmapFailureAndDie(void *addr, uptr size, error_t err,
65                                         bool raw_report) {
66   static int recursion_count;
67   if (raw_report || recursion_count) {
68     // If raw report is requested or we went into recursion just die.  The
69     // Report() and CHECK calls below may call munmap recursively and fail.
70     RawWrite("ERROR: Failed to munmap\n");
71     Die();
72   }
73   recursion_count++;
74   Report(
75       "ERROR: %s failed to deallocate 0x%zx (%zd) bytes at address %p (error "
76       "code: %d)\n",
77       SanitizerToolName, size, size, addr, err);
78 #if !SANITIZER_GO
79   DumpProcessMap();
80 #endif
81   UNREACHABLE("unable to unmmap");
82 }
83 
84 typedef bool UptrComparisonFunction(const uptr &a, const uptr &b);
85 typedef bool U32ComparisonFunction(const u32 &a, const u32 &b);
86 
StripPathPrefix(const char * filepath,const char * strip_path_prefix)87 const char *StripPathPrefix(const char *filepath,
88                             const char *strip_path_prefix) {
89   if (!filepath) return nullptr;
90   if (!strip_path_prefix) return filepath;
91   const char *res = filepath;
92   if (const char *pos = internal_strstr(filepath, strip_path_prefix))
93     res = pos + internal_strlen(strip_path_prefix);
94   if (res[0] == '.' && res[1] == '/')
95     res += 2;
96   return res;
97 }
98 
StripModuleName(const char * module)99 const char *StripModuleName(const char *module) {
100   if (!module)
101     return nullptr;
102   if (SANITIZER_WINDOWS) {
103     // On Windows, both slash and backslash are possible.
104     // Pick the one that goes last.
105     if (const char *bslash_pos = internal_strrchr(module, '\\'))
106       return StripModuleName(bslash_pos + 1);
107   }
108   if (const char *slash_pos = internal_strrchr(module, '/')) {
109     return slash_pos + 1;
110   }
111   return module;
112 }
113 
ReportErrorSummary(const char * error_message,const char * alt_tool_name)114 void ReportErrorSummary(const char *error_message, const char *alt_tool_name) {
115   if (!common_flags()->print_summary)
116     return;
117   InternalScopedString buff;
118   buff.AppendF("SUMMARY: %s: %s",
119                alt_tool_name ? alt_tool_name : SanitizerToolName,
120                error_message);
121   __sanitizer_report_error_summary(buff.data());
122 }
123 
124 // Removes the ANSI escape sequences from the input string (in-place).
RemoveANSIEscapeSequencesFromString(char * str)125 void RemoveANSIEscapeSequencesFromString(char *str) {
126   if (!str)
127     return;
128 
129   // We are going to remove the escape sequences in place.
130   char *s = str;
131   char *z = str;
132   while (*s != '\0') {
133     CHECK_GE(s, z);
134     // Skip over ANSI escape sequences with pointer 's'.
135     if (*s == '\033' && *(s + 1) == '[') {
136       s = internal_strchrnul(s, 'm');
137       if (*s == '\0') {
138         break;
139       }
140       s++;
141       continue;
142     }
143     // 's' now points at a character we want to keep. Copy over the buffer
144     // content if the escape sequence has been perviously skipped andadvance
145     // both pointers.
146     if (s != z)
147       *z = *s;
148 
149     // If we have not seen an escape sequence, just advance both pointers.
150     z++;
151     s++;
152   }
153 
154   // Null terminate the string.
155   *z = '\0';
156 }
157 
set(const char * module_name,uptr base_address)158 void LoadedModule::set(const char *module_name, uptr base_address) {
159   clear();
160   full_name_ = internal_strdup(module_name);
161   base_address_ = base_address;
162 }
163 
set(const char * module_name,uptr base_address,ModuleArch arch,u8 uuid[kModuleUUIDSize],bool instrumented)164 void LoadedModule::set(const char *module_name, uptr base_address,
165                        ModuleArch arch, u8 uuid[kModuleUUIDSize],
166                        bool instrumented) {
167   set(module_name, base_address);
168   arch_ = arch;
169   internal_memcpy(uuid_, uuid, sizeof(uuid_));
170   uuid_size_ = kModuleUUIDSize;
171   instrumented_ = instrumented;
172 }
173 
setUuid(const char * uuid,uptr size)174 void LoadedModule::setUuid(const char *uuid, uptr size) {
175   if (size > kModuleUUIDSize)
176     size = kModuleUUIDSize;
177   internal_memcpy(uuid_, uuid, size);
178   uuid_size_ = size;
179 }
180 
clear()181 void LoadedModule::clear() {
182   InternalFree(full_name_);
183   base_address_ = 0;
184   max_address_ = 0;
185   full_name_ = nullptr;
186   arch_ = kModuleArchUnknown;
187   internal_memset(uuid_, 0, kModuleUUIDSize);
188   instrumented_ = false;
189   while (!ranges_.empty()) {
190     AddressRange *r = ranges_.front();
191     ranges_.pop_front();
192     InternalFree(r);
193   }
194 }
195 
addAddressRange(uptr beg,uptr end,bool executable,bool writable,const char * name)196 void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable,
197                                    bool writable, const char *name) {
198   void *mem = InternalAlloc(sizeof(AddressRange));
199   AddressRange *r =
200       new(mem) AddressRange(beg, end, executable, writable, name);
201   ranges_.push_back(r);
202   max_address_ = Max(max_address_, end);
203 }
204 
containsAddress(uptr address) const205 bool LoadedModule::containsAddress(uptr address) const {
206   for (const AddressRange &r : ranges()) {
207     if (r.beg <= address && address < r.end)
208       return true;
209   }
210   return false;
211 }
212 
213 static atomic_uintptr_t g_total_mmaped;
214 
IncreaseTotalMmap(uptr size)215 void IncreaseTotalMmap(uptr size) {
216   if (!common_flags()->mmap_limit_mb) return;
217   uptr total_mmaped =
218       atomic_fetch_add(&g_total_mmaped, size, memory_order_relaxed) + size;
219   // Since for now mmap_limit_mb is not a user-facing flag, just kill
220   // a program. Use RAW_CHECK to avoid extra mmaps in reporting.
221   RAW_CHECK((total_mmaped >> 20) < common_flags()->mmap_limit_mb);
222 }
223 
DecreaseTotalMmap(uptr size)224 void DecreaseTotalMmap(uptr size) {
225   if (!common_flags()->mmap_limit_mb) return;
226   atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed);
227 }
228 
TemplateMatch(const char * templ,const char * str)229 bool TemplateMatch(const char *templ, const char *str) {
230   if ((!str) || str[0] == 0)
231     return false;
232   bool start = false;
233   if (templ && templ[0] == '^') {
234     start = true;
235     templ++;
236   }
237   bool asterisk = false;
238   while (templ && templ[0]) {
239     if (templ[0] == '*') {
240       templ++;
241       start = false;
242       asterisk = true;
243       continue;
244     }
245     if (templ[0] == '$')
246       return str[0] == 0 || asterisk;
247     if (str[0] == 0)
248       return false;
249     char *tpos = (char*)internal_strchr(templ, '*');
250     char *tpos1 = (char*)internal_strchr(templ, '$');
251     if ((!tpos) || (tpos1 && tpos1 < tpos))
252       tpos = tpos1;
253     if (tpos)
254       tpos[0] = 0;
255     const char *str0 = str;
256     const char *spos = internal_strstr(str, templ);
257     str = spos + internal_strlen(templ);
258     templ = tpos;
259     if (tpos)
260       tpos[0] = tpos == tpos1 ? '$' : '*';
261     if (!spos)
262       return false;
263     if (start && spos != str0)
264       return false;
265     start = false;
266     asterisk = false;
267   }
268   return true;
269 }
270 
271 static char binary_name_cache_str[kMaxPathLength];
272 static char process_name_cache_str[kMaxPathLength];
273 
GetProcessName()274 const char *GetProcessName() {
275   return process_name_cache_str;
276 }
277 
ReadProcessName(char * buf,uptr buf_len)278 static uptr ReadProcessName(/*out*/ char *buf, uptr buf_len) {
279   ReadLongProcessName(buf, buf_len);
280   char *s = const_cast<char *>(StripModuleName(buf));
281   uptr len = internal_strlen(s);
282   if (s != buf) {
283     internal_memmove(buf, s, len);
284     buf[len] = '\0';
285   }
286   return len;
287 }
288 
UpdateProcessName()289 void UpdateProcessName() {
290   ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));
291 }
292 
293 // Call once to make sure that binary_name_cache_str is initialized
CacheBinaryName()294 void CacheBinaryName() {
295   if (binary_name_cache_str[0] != '\0')
296     return;
297   ReadBinaryName(binary_name_cache_str, sizeof(binary_name_cache_str));
298   ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));
299 }
300 
ReadBinaryNameCached(char * buf,uptr buf_len)301 uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len) {
302   CacheBinaryName();
303   uptr name_len = internal_strlen(binary_name_cache_str);
304   name_len = (name_len < buf_len - 1) ? name_len : buf_len - 1;
305   if (buf_len == 0)
306     return 0;
307   internal_memcpy(buf, binary_name_cache_str, name_len);
308   buf[name_len] = '\0';
309   return name_len;
310 }
311 
ReadBinaryDir(char * buf,uptr buf_len)312 uptr ReadBinaryDir(/*out*/ char *buf, uptr buf_len) {
313   ReadBinaryNameCached(buf, buf_len);
314   const char *exec_name_pos = StripModuleName(buf);
315   uptr name_len = exec_name_pos - buf;
316   buf[name_len] = '\0';
317   return name_len;
318 }
319 
320 #if !SANITIZER_GO
PrintCmdline()321 void PrintCmdline() {
322   char **argv = GetArgv();
323   if (!argv) return;
324   Printf("\nCommand: ");
325   for (uptr i = 0; argv[i]; ++i)
326     Printf("%s ", argv[i]);
327   Printf("\n\n");
328 }
329 #endif
330 
331 // Malloc hooks.
332 static const int kMaxMallocFreeHooks = 5;
333 struct MallocFreeHook {
334   void (*malloc_hook)(const void *, uptr);
335   void (*free_hook)(const void *);
336 };
337 
338 static MallocFreeHook MFHooks[kMaxMallocFreeHooks];
339 
RunMallocHooks(void * ptr,uptr size)340 void RunMallocHooks(void *ptr, uptr size) {
341   __sanitizer_malloc_hook(ptr, size);
342   for (int i = 0; i < kMaxMallocFreeHooks; i++) {
343     auto hook = MFHooks[i].malloc_hook;
344     if (!hook)
345       break;
346     hook(ptr, size);
347   }
348 }
349 
350 // Returns '1' if the call to free() should be ignored (based on
351 // __sanitizer_ignore_free_hook), or '0' otherwise.
RunFreeHooks(void * ptr)352 int RunFreeHooks(void *ptr) {
353   if (__sanitizer_ignore_free_hook(ptr)) {
354     return 1;
355   }
356 
357   __sanitizer_free_hook(ptr);
358   for (int i = 0; i < kMaxMallocFreeHooks; i++) {
359     auto hook = MFHooks[i].free_hook;
360     if (!hook)
361       break;
362     hook(ptr);
363   }
364 
365   return 0;
366 }
367 
InstallMallocFreeHooks(void (* malloc_hook)(const void *,uptr),void (* free_hook)(const void *))368 static int InstallMallocFreeHooks(void (*malloc_hook)(const void *, uptr),
369                                   void (*free_hook)(const void *)) {
370   if (!malloc_hook || !free_hook) return 0;
371   for (int i = 0; i < kMaxMallocFreeHooks; i++) {
372     if (MFHooks[i].malloc_hook == nullptr) {
373       MFHooks[i].malloc_hook = malloc_hook;
374       MFHooks[i].free_hook = free_hook;
375       return i + 1;
376     }
377   }
378   return 0;
379 }
380 
internal_sleep(unsigned seconds)381 void internal_sleep(unsigned seconds) {
382   internal_usleep((u64)seconds * 1000 * 1000);
383 }
SleepForSeconds(unsigned seconds)384 void SleepForSeconds(unsigned seconds) {
385   internal_usleep((u64)seconds * 1000 * 1000);
386 }
SleepForMillis(unsigned millis)387 void SleepForMillis(unsigned millis) { internal_usleep((u64)millis * 1000); }
388 
WaitForDebugger(unsigned seconds,const char * label)389 void WaitForDebugger(unsigned seconds, const char *label) {
390   if (seconds) {
391     Report("Sleeping for %u second(s) %s\n", seconds, label);
392     SleepForSeconds(seconds);
393   }
394 }
395 
396 } // namespace __sanitizer
397 
398 using namespace __sanitizer;
399 
400 extern "C" {
SANITIZER_INTERFACE_WEAK_DEF(void,__sanitizer_report_error_summary,const char * error_summary)401 SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_report_error_summary,
402                              const char *error_summary) {
403   Printf("%s\n", error_summary);
404 }
405 
406 SANITIZER_INTERFACE_ATTRIBUTE
__sanitizer_acquire_crash_state()407 int __sanitizer_acquire_crash_state() {
408   static atomic_uint8_t in_crash_state = {};
409   return !atomic_exchange(&in_crash_state, 1, memory_order_relaxed);
410 }
411 
412 SANITIZER_INTERFACE_ATTRIBUTE
__sanitizer_install_malloc_and_free_hooks(void (* malloc_hook)(const void *,uptr),void (* free_hook)(const void *))413 int __sanitizer_install_malloc_and_free_hooks(void (*malloc_hook)(const void *,
414                                                                   uptr),
415                                               void (*free_hook)(const void *)) {
416   return InstallMallocFreeHooks(malloc_hook, free_hook);
417 }
418 
419 // Provide default (no-op) implementation of malloc hooks.
SANITIZER_INTERFACE_WEAK_DEF(void,__sanitizer_malloc_hook,void * ptr,uptr size)420 SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_malloc_hook, void *ptr,
421                              uptr size) {
422   (void)ptr;
423   (void)size;
424 }
425 
SANITIZER_INTERFACE_WEAK_DEF(void,__sanitizer_free_hook,void * ptr)426 SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_free_hook, void *ptr) {
427   (void)ptr;
428 }
429 
SANITIZER_INTERFACE_WEAK_DEF(int,__sanitizer_ignore_free_hook,void * ptr)430 SANITIZER_INTERFACE_WEAK_DEF(int, __sanitizer_ignore_free_hook, void *ptr) {
431   (void)ptr;
432   return 0;
433 }
434 
435 } // extern "C"
436