Home | History | Annotate | Download | only in sanitizer_common
      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_internal.h"
     16 #include "sanitizer_flags.h"
     17 #include "sanitizer_libc.h"
     18 #include "sanitizer_placement_new.h"
     19 #include "sanitizer_stacktrace_printer.h"
     20 #include "sanitizer_symbolizer.h"
     21 
     22 namespace __sanitizer {
     23 
     24 const char *SanitizerToolName = "SanitizerTool";
     25 
     26 atomic_uint32_t current_verbosity;
     27 
     28 uptr GetPageSizeCached() {
     29   static uptr PageSize;
     30   if (!PageSize)
     31     PageSize = GetPageSize();
     32   return PageSize;
     33 }
     34 
     35 StaticSpinMutex report_file_mu;
     36 ReportFile report_file = {&report_file_mu, kStderrFd, "", "", 0};
     37 
     38 void RawWrite(const char *buffer) {
     39   report_file.Write(buffer, internal_strlen(buffer));
     40 }
     41 
     42 void ReportFile::ReopenIfNecessary() {
     43   mu->CheckLocked();
     44   if (fd == kStdoutFd || fd == kStderrFd) return;
     45 
     46   uptr pid = internal_getpid();
     47   // If in tracer, use the parent's file.
     48   if (pid == stoptheworld_tracer_pid)
     49     pid = stoptheworld_tracer_ppid;
     50   if (fd != kInvalidFd) {
     51     // If the report file is already opened by the current process,
     52     // do nothing. Otherwise the report file was opened by the parent
     53     // process, close it now.
     54     if (fd_pid == pid)
     55       return;
     56     else
     57       CloseFile(fd);
     58   }
     59 
     60   internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid);
     61   fd = OpenFile(full_path, WrOnly);
     62   if (fd == kInvalidFd) {
     63     const char *ErrorMsgPrefix = "ERROR: Can't open file: ";
     64     WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));
     65     WriteToFile(kStderrFd, full_path, internal_strlen(full_path));
     66     Die();
     67   }
     68   fd_pid = pid;
     69 }
     70 
     71 void ReportFile::SetReportPath(const char *path) {
     72   if (!path)
     73     return;
     74   uptr len = internal_strlen(path);
     75   if (len > sizeof(path_prefix) - 100) {
     76     Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n",
     77            path[0], path[1], path[2], path[3],
     78            path[4], path[5], path[6], path[7]);
     79     Die();
     80   }
     81 
     82   SpinMutexLock l(mu);
     83   if (fd != kStdoutFd && fd != kStderrFd && fd != kInvalidFd)
     84     CloseFile(fd);
     85   fd = kInvalidFd;
     86   if (internal_strcmp(path, "stdout") == 0) {
     87     fd = kStdoutFd;
     88   } else if (internal_strcmp(path, "stderr") == 0) {
     89     fd = kStderrFd;
     90   } else {
     91     internal_snprintf(path_prefix, kMaxPathLength, "%s", path);
     92   }
     93 }
     94 
     95 // PID of the tracer task in StopTheWorld. It shares the address space with the
     96 // main process, but has a different PID and thus requires special handling.
     97 uptr stoptheworld_tracer_pid = 0;
     98 // Cached pid of parent process - if the parent process dies, we want to keep
     99 // writing to the same log file.
    100 uptr stoptheworld_tracer_ppid = 0;
    101 
    102 static DieCallbackType InternalDieCallback, UserDieCallback;
    103 void SetDieCallback(DieCallbackType callback) {
    104   InternalDieCallback = callback;
    105 }
    106 void SetUserDieCallback(DieCallbackType callback) {
    107   UserDieCallback = callback;
    108 }
    109 
    110 DieCallbackType GetDieCallback() {
    111   return InternalDieCallback;
    112 }
    113 
    114 void NORETURN Die() {
    115   if (UserDieCallback)
    116     UserDieCallback();
    117   if (InternalDieCallback)
    118     InternalDieCallback();
    119   internal__exit(1);
    120 }
    121 
    122 static CheckFailedCallbackType CheckFailedCallback;
    123 void SetCheckFailedCallback(CheckFailedCallbackType callback) {
    124   CheckFailedCallback = callback;
    125 }
    126 
    127 void NORETURN CheckFailed(const char *file, int line, const char *cond,
    128                           u64 v1, u64 v2) {
    129   if (CheckFailedCallback) {
    130     CheckFailedCallback(file, line, cond, v1, v2);
    131   }
    132   Report("Sanitizer CHECK failed: %s:%d %s (%lld, %lld)\n", file, line, cond,
    133                                                             v1, v2);
    134   Die();
    135 }
    136 
    137 uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
    138                       uptr max_len, error_t *errno_p) {
    139   uptr PageSize = GetPageSizeCached();
    140   uptr kMinFileLen = PageSize;
    141   uptr read_len = 0;
    142   *buff = 0;
    143   *buff_size = 0;
    144   // The files we usually open are not seekable, so try different buffer sizes.
    145   for (uptr size = kMinFileLen; size <= max_len; size *= 2) {
    146     fd_t fd = OpenFile(file_name, RdOnly, errno_p);
    147     if (fd == kInvalidFd) return 0;
    148     UnmapOrDie(*buff, *buff_size);
    149     *buff = (char*)MmapOrDie(size, __func__);
    150     *buff_size = size;
    151     // Read up to one page at a time.
    152     read_len = 0;
    153     bool reached_eof = false;
    154     while (read_len + PageSize <= size) {
    155       uptr just_read;
    156       if (!ReadFromFile(fd, *buff + read_len, PageSize, &just_read, errno_p)) {
    157         UnmapOrDie(*buff, *buff_size);
    158         return 0;
    159       }
    160       if (just_read == 0) {
    161         reached_eof = true;
    162         break;
    163       }
    164       read_len += just_read;
    165     }
    166     CloseFile(fd);
    167     if (reached_eof)  // We've read the whole file.
    168       break;
    169   }
    170   return read_len;
    171 }
    172 
    173 typedef bool UptrComparisonFunction(const uptr &a, const uptr &b);
    174 
    175 template<class T>
    176 static inline bool CompareLess(const T &a, const T &b) {
    177   return a < b;
    178 }
    179 
    180 void SortArray(uptr *array, uptr size) {
    181   InternalSort<uptr*, UptrComparisonFunction>(&array, size, CompareLess);
    182 }
    183 
    184 // We want to map a chunk of address space aligned to 'alignment'.
    185 // We do it by maping a bit more and then unmaping redundant pieces.
    186 // We probably can do it with fewer syscalls in some OS-dependent way.
    187 void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type) {
    188 // uptr PageSize = GetPageSizeCached();
    189   CHECK(IsPowerOfTwo(size));
    190   CHECK(IsPowerOfTwo(alignment));
    191   uptr map_size = size + alignment;
    192   uptr map_res = (uptr)MmapOrDie(map_size, mem_type);
    193   uptr map_end = map_res + map_size;
    194   uptr res = map_res;
    195   if (res & (alignment - 1))  // Not aligned.
    196     res = (map_res + alignment) & ~(alignment - 1);
    197   uptr end = res + size;
    198   if (res != map_res)
    199     UnmapOrDie((void*)map_res, res - map_res);
    200   if (end != map_end)
    201     UnmapOrDie((void*)end, map_end - end);
    202   return (void*)res;
    203 }
    204 
    205 const char *StripPathPrefix(const char *filepath,
    206                             const char *strip_path_prefix) {
    207   if (filepath == 0) return 0;
    208   if (strip_path_prefix == 0) return filepath;
    209   const char *res = filepath;
    210   if (const char *pos = internal_strstr(filepath, strip_path_prefix))
    211     res = pos + internal_strlen(strip_path_prefix);
    212   if (res[0] == '.' && res[1] == '/')
    213     res += 2;
    214   return res;
    215 }
    216 
    217 const char *StripModuleName(const char *module) {
    218   if (module == 0)
    219     return 0;
    220   if (SANITIZER_WINDOWS) {
    221     // On Windows, both slash and backslash are possible.
    222     // Pick the one that goes last.
    223     if (const char *bslash_pos = internal_strrchr(module, '\\'))
    224       return StripModuleName(bslash_pos + 1);
    225   }
    226   if (const char *slash_pos = internal_strrchr(module, '/')) {
    227     return slash_pos + 1;
    228   }
    229   return module;
    230 }
    231 
    232 void ReportErrorSummary(const char *error_message) {
    233   if (!common_flags()->print_summary)
    234     return;
    235   InternalScopedString buff(kMaxSummaryLength);
    236   buff.append("SUMMARY: %s: %s", SanitizerToolName, error_message);
    237   __sanitizer_report_error_summary(buff.data());
    238 }
    239 
    240 #ifndef SANITIZER_GO
    241 void ReportErrorSummary(const char *error_type, const AddressInfo &info) {
    242   if (!common_flags()->print_summary)
    243     return;
    244   InternalScopedString buff(kMaxSummaryLength);
    245   buff.append("%s ", error_type);
    246   RenderFrame(&buff, "%L %F", 0, info, common_flags()->strip_path_prefix);
    247   ReportErrorSummary(buff.data());
    248 }
    249 #endif
    250 
    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 
    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 
    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 
    273 bool LoadedModule::containsAddress(uptr address) const {
    274   for (Iterator iter = ranges(); iter.hasNext();) {
    275     const AddressRange *r = iter.next();
    276     if (r->beg <= address && address < r->end)
    277       return true;
    278   }
    279   return false;
    280 }
    281 
    282 static atomic_uintptr_t g_total_mmaped;
    283 
    284 void IncreaseTotalMmap(uptr size) {
    285   if (!common_flags()->mmap_limit_mb) return;
    286   uptr total_mmaped =
    287       atomic_fetch_add(&g_total_mmaped, size, memory_order_relaxed) + size;
    288   // Since for now mmap_limit_mb is not a user-facing flag, just kill
    289   // a program. Use RAW_CHECK to avoid extra mmaps in reporting.
    290   RAW_CHECK((total_mmaped >> 20) < common_flags()->mmap_limit_mb);
    291 }
    292 
    293 void DecreaseTotalMmap(uptr size) {
    294   if (!common_flags()->mmap_limit_mb) return;
    295   atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed);
    296 }
    297 
    298 bool TemplateMatch(const char *templ, const char *str) {
    299   if (str == 0 || str[0] == 0)
    300     return false;
    301   bool start = false;
    302   if (templ && templ[0] == '^') {
    303     start = true;
    304     templ++;
    305   }
    306   bool asterisk = false;
    307   while (templ && templ[0]) {
    308     if (templ[0] == '*') {
    309       templ++;
    310       start = false;
    311       asterisk = true;
    312       continue;
    313     }
    314     if (templ[0] == '$')
    315       return str[0] == 0 || asterisk;
    316     if (str[0] == 0)
    317       return false;
    318     char *tpos = (char*)internal_strchr(templ, '*');
    319     char *tpos1 = (char*)internal_strchr(templ, '$');
    320     if (tpos == 0 || (tpos1 && tpos1 < tpos))
    321       tpos = tpos1;
    322     if (tpos != 0)
    323       tpos[0] = 0;
    324     const char *str0 = str;
    325     const char *spos = internal_strstr(str, templ);
    326     str = spos + internal_strlen(templ);
    327     templ = tpos;
    328     if (tpos)
    329       tpos[0] = tpos == tpos1 ? '$' : '*';
    330     if (spos == 0)
    331       return false;
    332     if (start && spos != str0)
    333       return false;
    334     start = false;
    335     asterisk = false;
    336   }
    337   return true;
    338 }
    339 
    340 }  // namespace __sanitizer
    341 
    342 using namespace __sanitizer;  // NOLINT
    343 
    344 extern "C" {
    345 void __sanitizer_set_report_path(const char *path) {
    346   report_file.SetReportPath(path);
    347 }
    348 
    349 void __sanitizer_report_error_summary(const char *error_summary) {
    350   Printf("%s\n", error_summary);
    351 }
    352 
    353 SANITIZER_INTERFACE_ATTRIBUTE
    354 void __sanitizer_set_death_callback(void (*callback)(void)) {
    355   SetUserDieCallback(callback);
    356 }
    357 }  // extern "C"
    358