2013-11-04 22:33:31 +01:00
|
|
|
//===-- 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"
|
2014-09-23 19:59:53 +02:00
|
|
|
#include "sanitizer_flags.h"
|
2013-11-04 22:33:31 +01:00
|
|
|
#include "sanitizer_libc.h"
|
2014-09-23 19:59:53 +02:00
|
|
|
#include "sanitizer_placement_new.h"
|
2013-11-04 22:33:31 +01:00
|
|
|
|
|
|
|
namespace __sanitizer {
|
|
|
|
|
|
|
|
static const char *const kTypeStrings[SuppressionTypeCount] = {
|
2014-09-23 19:59:53 +02:00
|
|
|
"none", "race", "mutex", "thread", "signal",
|
|
|
|
"leak", "called_from_lib", "deadlock", "vptr_check"};
|
2013-11-04 22:33:31 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-09-23 19:59:53 +02:00
|
|
|
ALIGNED(64) static char placeholder[sizeof(SuppressionContext)];
|
|
|
|
static SuppressionContext *suppression_ctx = 0;
|
|
|
|
|
|
|
|
SuppressionContext *SuppressionContext::Get() {
|
|
|
|
CHECK(suppression_ctx);
|
|
|
|
return suppression_ctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SuppressionContext::InitIfNecessary() {
|
|
|
|
if (suppression_ctx)
|
|
|
|
return;
|
|
|
|
suppression_ctx = new(placeholder) SuppressionContext;
|
|
|
|
if (common_flags()->suppressions[0] == '\0')
|
|
|
|
return;
|
|
|
|
char *suppressions_from_file;
|
|
|
|
uptr buffer_size;
|
|
|
|
uptr contents_size =
|
|
|
|
ReadFileToBuffer(common_flags()->suppressions, &suppressions_from_file,
|
|
|
|
&buffer_size, 1 << 26 /* max_len */);
|
|
|
|
if (contents_size == 0) {
|
|
|
|
Printf("%s: failed to read suppressions file '%s'\n", SanitizerToolName,
|
|
|
|
common_flags()->suppressions);
|
|
|
|
Die();
|
|
|
|
}
|
|
|
|
suppression_ctx->Parse(suppressions_from_file);
|
|
|
|
}
|
|
|
|
|
2013-11-04 22:33:31 +01:00
|
|
|
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<SuppressionType>(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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-05 10:18:38 +01:00
|
|
|
uptr SuppressionContext::SuppressionCount() const {
|
2013-11-04 22:33:31 +01:00
|
|
|
return suppressions_.size();
|
|
|
|
}
|
|
|
|
|
2013-12-05 10:18:38 +01:00
|
|
|
const Suppression *SuppressionContext::SuppressionAt(uptr i) const {
|
|
|
|
CHECK_LT(i, suppressions_.size());
|
|
|
|
return &suppressions_[i];
|
|
|
|
}
|
|
|
|
|
2013-11-04 22:33:31 +01:00
|
|
|
void SuppressionContext::GetMatched(
|
|
|
|
InternalMmapVector<Suppression *> *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
|