//===-- tsan_suppressions.cc ----------------------------------------------===// // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" #include "tsan_suppressions.h" #include "tsan_rtl.h" #include "tsan_flags.h" #include "tsan_mman.h" #include "tsan_platform.h" // Can be overriden in frontend. #ifndef TSAN_GO extern "C" const char *WEAK __tsan_default_suppressions() { return 0; } #endif namespace __tsan { static Suppression *g_suppressions; static char *ReadFile(const char *filename) { if (filename == 0 || filename[0] == 0) return 0; InternalScopedBuffer tmp(4*1024); if (filename[0] == '/' || GetPwd() == 0) internal_snprintf(tmp.data(), tmp.size(), "%s", filename); else internal_snprintf(tmp.data(), tmp.size(), "%s/%s", GetPwd(), filename); fd_t fd = OpenFile(tmp.data(), false); if (fd == kInvalidFd) { Printf("ThreadSanitizer: failed to open suppressions file '%s'\n", tmp.data()); Die(); } const uptr fsize = internal_filesize(fd); if (fsize == (uptr)-1) { Printf("ThreadSanitizer: failed to stat suppressions file '%s'\n", tmp.data()); Die(); } char *buf = (char*)internal_alloc(MBlockSuppression, fsize + 1); if (fsize != internal_read(fd, buf, fsize)) { Printf("ThreadSanitizer: failed to read suppressions file '%s'\n", tmp.data()); Die(); } internal_close(fd); buf[fsize] = 0; return buf; } bool SuppressionMatch(char *templ, const char *str) { if (str == 0 || str[0] == 0) return false; char *tpos; const char *spos; while (templ && templ[0]) { if (templ[0] == '*') { templ++; continue; } if (str[0] == 0) return false; tpos = (char*)internal_strchr(templ, '*'); if (tpos != 0) tpos[0] = 0; spos = internal_strstr(str, templ); str = spos + internal_strlen(templ); templ = tpos; if (tpos) tpos[0] = '*'; if (spos == 0) return false; } return true; } Suppression *SuppressionParse(Suppression *head, const char* supp) { const char *line = supp; while (line) { while (line[0] == ' ' || line[0] == '\t') line++; const char *end = internal_strchr(line, '\n'); if (end == 0) end = line + internal_strlen(line); if (line != end && line[0] != '#') { const char *end2 = end; while (line != end2 && (end2[-1] == ' ' || end2[-1] == '\t')) end2--; SuppressionType stype; if (0 == internal_strncmp(line, "race:", sizeof("race:") - 1)) { stype = SuppressionRace; line += sizeof("race:") - 1; } else if (0 == internal_strncmp(line, "thread:", sizeof("thread:") - 1)) { stype = SuppressionThread; line += sizeof("thread:") - 1; } else if (0 == internal_strncmp(line, "mutex:", sizeof("mutex:") - 1)) { stype = SuppressionMutex; line += sizeof("mutex:") - 1; } else if (0 == internal_strncmp(line, "signal:", sizeof("signal:") - 1)) { stype = SuppressionSignal; line += sizeof("signal:") - 1; } else { Printf("ThreadSanitizer: failed to parse suppressions file\n"); Die(); } Suppression *s = (Suppression*)internal_alloc(MBlockSuppression, sizeof(Suppression)); s->next = head; head = s; s->type = stype; s->templ = (char*)internal_alloc(MBlockSuppression, end2 - line + 1); internal_memcpy(s->templ, line, end2 - line); s->templ[end2 - line] = 0; } if (end[0] == 0) break; line = end + 1; } return head; } void InitializeSuppressions() { const char *supp = ReadFile(flags()->suppressions); g_suppressions = SuppressionParse(0, supp); #ifndef TSAN_GO supp = __tsan_default_suppressions(); g_suppressions = SuppressionParse(g_suppressions, supp); #endif } uptr IsSuppressed(ReportType typ, const ReportStack *stack) { if (g_suppressions == 0 || stack == 0) return 0; SuppressionType stype; if (typ == ReportTypeRace) stype = SuppressionRace; else if (typ == ReportTypeThreadLeak) stype = SuppressionThread; else if (typ == ReportTypeMutexDestroyLocked) stype = SuppressionMutex; else if (typ == ReportTypeSignalUnsafe) stype = SuppressionSignal; else return 0; for (const ReportStack *frame = stack; frame; frame = frame->next) { for (Suppression *supp = g_suppressions; supp; supp = supp->next) { if (stype == supp->type && (SuppressionMatch(supp->templ, frame->func) || SuppressionMatch(supp->templ, frame->file) || SuppressionMatch(supp->templ, frame->module))) { DPrintf("ThreadSanitizer: matched suppression '%s'\n", supp->templ); return frame->pc; } } } return 0; } } // namespace __tsan