//===-- sanitizer_suppressions.cc -----------------------------------------===// // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Suppression parsing/matching code shared between TSan and LSan. // //===----------------------------------------------------------------------===// #include "sanitizer_suppressions.h" #include "sanitizer_allocator_internal.h" #include "sanitizer_common.h" #include "sanitizer_libc.h" namespace __sanitizer { static const char *const kTypeStrings[SuppressionTypeCount] = { "none", "race", "mutex", "thread", "signal", "leak", "called_from_lib" }; bool TemplateMatch(char *templ, const char *str) { if (str == 0 || str[0] == 0) return false; bool start = false; if (templ && templ[0] == '^') { start = true; templ++; } bool asterisk = false; while (templ && templ[0]) { if (templ[0] == '*') { templ++; start = false; asterisk = true; continue; } if (templ[0] == '$') return str[0] == 0 || asterisk; if (str[0] == 0) return false; char *tpos = (char*)internal_strchr(templ, '*'); char *tpos1 = (char*)internal_strchr(templ, '$'); if (tpos == 0 || (tpos1 && tpos1 < tpos)) tpos = tpos1; if (tpos != 0) tpos[0] = 0; const char *str0 = str; const char *spos = internal_strstr(str, templ); str = spos + internal_strlen(templ); templ = tpos; if (tpos) tpos[0] = tpos == tpos1 ? '$' : '*'; if (spos == 0) return false; if (start && spos != str0) return false; start = false; asterisk = false; } return true; } bool SuppressionContext::Match(const char *str, SuppressionType type, Suppression **s) { can_parse_ = false; uptr i; for (i = 0; i < suppressions_.size(); i++) if (type == suppressions_[i].type && TemplateMatch(suppressions_[i].templ, str)) break; if (i == suppressions_.size()) return false; *s = &suppressions_[i]; return true; } static const char *StripPrefix(const char *str, const char *prefix) { while (str && *str == *prefix) { str++; prefix++; } if (!*prefix) return str; return 0; } void SuppressionContext::Parse(const char *str) { // Context must not mutate once Match has been called. CHECK(can_parse_); const char *line = str; 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--; int type; for (type = 0; type < SuppressionTypeCount; type++) { const char *next_char = StripPrefix(line, kTypeStrings[type]); if (next_char && *next_char == ':') { line = ++next_char; break; } } if (type == SuppressionTypeCount) { Printf("%s: failed to parse suppressions\n", SanitizerToolName); Die(); } Suppression s; s.type = static_cast(type); s.templ = (char*)InternalAlloc(end2 - line + 1); internal_memcpy(s.templ, line, end2 - line); s.templ[end2 - line] = 0; s.hit_count = 0; s.weight = 0; suppressions_.push_back(s); } if (end[0] == 0) break; line = end + 1; } } uptr SuppressionContext::SuppressionCount() const { return suppressions_.size(); } const Suppression *SuppressionContext::SuppressionAt(uptr i) const { CHECK_LT(i, suppressions_.size()); return &suppressions_[i]; } void SuppressionContext::GetMatched( InternalMmapVector *matched) { for (uptr i = 0; i < suppressions_.size(); i++) if (suppressions_[i].hit_count) matched->push_back(&suppressions_[i]); } const char *SuppressionTypeString(SuppressionType t) { CHECK(t < SuppressionTypeCount); return kTypeStrings[t]; } } // namespace __sanitizer