1//===-- tsan_suppressions.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 a part of ThreadSanitizer (TSan), a race detector.
11//
12//===----------------------------------------------------------------------===//
13
14#include "sanitizer_common/sanitizer_common.h"
15#include "sanitizer_common/sanitizer_libc.h"
16#include "tsan_suppressions.h"
17#include "tsan_rtl.h"
18#include "tsan_flags.h"
19#include "tsan_mman.h"
20#include "tsan_platform.h"
21
22// Can be overriden in frontend.
23#ifndef TSAN_GO
24extern "C" const char *WEAK __tsan_default_suppressions() {
25  return 0;
26}
27#endif
28
29namespace __tsan {
30
31static Suppression *g_suppressions;
32
33static char *ReadFile(const char *filename) {
34  if (filename == 0 || filename[0] == 0)
35    return 0;
36  InternalScopedBuffer<char> tmp(4*1024);
37  if (filename[0] == '/' || GetPwd() == 0)
38    internal_snprintf(tmp.data(), tmp.size(), "%s", filename);
39  else
40    internal_snprintf(tmp.data(), tmp.size(), "%s/%s", GetPwd(), filename);
41  fd_t fd = OpenFile(tmp.data(), false);
42  if (fd == kInvalidFd) {
43    Printf("ThreadSanitizer: failed to open suppressions file '%s'\n",
44               tmp.data());
45    Die();
46  }
47  const uptr fsize = internal_filesize(fd);
48  if (fsize == (uptr)-1) {
49    Printf("ThreadSanitizer: failed to stat suppressions file '%s'\n",
50               tmp.data());
51    Die();
52  }
53  char *buf = (char*)internal_alloc(MBlockSuppression, fsize + 1);
54  if (fsize != internal_read(fd, buf, fsize)) {
55    Printf("ThreadSanitizer: failed to read suppressions file '%s'\n",
56               tmp.data());
57    Die();
58  }
59  internal_close(fd);
60  buf[fsize] = 0;
61  return buf;
62}
63
64bool SuppressionMatch(char *templ, const char *str) {
65  if (str == 0 || str[0] == 0)
66    return false;
67  char *tpos;
68  const char *spos;
69  while (templ && templ[0]) {
70    if (templ[0] == '*') {
71      templ++;
72      continue;
73    }
74    if (str[0] == 0)
75      return false;
76    tpos = (char*)internal_strchr(templ, '*');
77    if (tpos != 0)
78      tpos[0] = 0;
79    spos = internal_strstr(str, templ);
80    str = spos + internal_strlen(templ);
81    templ = tpos;
82    if (tpos)
83      tpos[0] = '*';
84    if (spos == 0)
85      return false;
86  }
87  return true;
88}
89
90Suppression *SuppressionParse(Suppression *head, const char* supp) {
91  const char *line = supp;
92  while (line) {
93    while (line[0] == ' ' || line[0] == '\t')
94      line++;
95    const char *end = internal_strchr(line, '\n');
96    if (end == 0)
97      end = line + internal_strlen(line);
98    if (line != end && line[0] != '#') {
99      const char *end2 = end;
100      while (line != end2 && (end2[-1] == ' ' || end2[-1] == '\t'))
101        end2--;
102      SuppressionType stype;
103      if (0 == internal_strncmp(line, "race:", sizeof("race:") - 1)) {
104        stype = SuppressionRace;
105        line += sizeof("race:") - 1;
106      } else if (0 == internal_strncmp(line, "thread:",
107          sizeof("thread:") - 1)) {
108        stype = SuppressionThread;
109        line += sizeof("thread:") - 1;
110      } else if (0 == internal_strncmp(line, "mutex:",
111          sizeof("mutex:") - 1)) {
112        stype = SuppressionMutex;
113        line += sizeof("mutex:") - 1;
114      } else if (0 == internal_strncmp(line, "signal:",
115          sizeof("signal:") - 1)) {
116        stype = SuppressionSignal;
117        line += sizeof("signal:") - 1;
118      } else {
119        Printf("ThreadSanitizer: failed to parse suppressions file\n");
120        Die();
121      }
122      Suppression *s = (Suppression*)internal_alloc(MBlockSuppression,
123          sizeof(Suppression));
124      s->next = head;
125      head = s;
126      s->type = stype;
127      s->templ = (char*)internal_alloc(MBlockSuppression, end2 - line + 1);
128      internal_memcpy(s->templ, line, end2 - line);
129      s->templ[end2 - line] = 0;
130    }
131    if (end[0] == 0)
132      break;
133    line = end + 1;
134  }
135  return head;
136}
137
138void InitializeSuppressions() {
139  const char *supp = ReadFile(flags()->suppressions);
140  g_suppressions = SuppressionParse(0, supp);
141#ifndef TSAN_GO
142  supp = __tsan_default_suppressions();
143  g_suppressions = SuppressionParse(g_suppressions, supp);
144#endif
145}
146
147uptr IsSuppressed(ReportType typ, const ReportStack *stack) {
148  if (g_suppressions == 0 || stack == 0)
149    return 0;
150  SuppressionType stype;
151  if (typ == ReportTypeRace)
152    stype = SuppressionRace;
153  else if (typ == ReportTypeThreadLeak)
154    stype = SuppressionThread;
155  else if (typ == ReportTypeMutexDestroyLocked)
156    stype = SuppressionMutex;
157  else if (typ == ReportTypeSignalUnsafe)
158    stype = SuppressionSignal;
159  else
160    return 0;
161  for (const ReportStack *frame = stack; frame; frame = frame->next) {
162    for (Suppression *supp = g_suppressions; supp; supp = supp->next) {
163      if (stype == supp->type &&
164          (SuppressionMatch(supp->templ, frame->func) ||
165           SuppressionMatch(supp->templ, frame->file) ||
166           SuppressionMatch(supp->templ, frame->module))) {
167        DPrintf("ThreadSanitizer: matched suppression '%s'\n", supp->templ);
168        return frame->pc;
169      }
170    }
171  }
172  return 0;
173}
174}  // namespace __tsan
175