1353944Sdim//===-- sanitizer_suppressions.cpp ----------------------------------------===//
2353944Sdim//
3353944Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353944Sdim// See https://llvm.org/LICENSE.txt for license information.
5353944Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6353944Sdim//
7353944Sdim//===----------------------------------------------------------------------===//
8353944Sdim//
9353944Sdim// Suppression parsing/matching code.
10353944Sdim//
11353944Sdim//===----------------------------------------------------------------------===//
12353944Sdim
13353944Sdim#include "sanitizer_suppressions.h"
14353944Sdim
15353944Sdim#include "sanitizer_allocator_internal.h"
16353944Sdim#include "sanitizer_common.h"
17353944Sdim#include "sanitizer_flags.h"
18353944Sdim#include "sanitizer_file.h"
19353944Sdim#include "sanitizer_libc.h"
20353944Sdim#include "sanitizer_placement_new.h"
21353944Sdim
22353944Sdimnamespace __sanitizer {
23353944Sdim
24353944SdimSuppressionContext::SuppressionContext(const char *suppression_types[],
25353944Sdim                                       int suppression_types_num)
26353944Sdim    : suppression_types_(suppression_types),
27353944Sdim      suppression_types_num_(suppression_types_num),
28353944Sdim      can_parse_(true) {
29353944Sdim  CHECK_LE(suppression_types_num_, kMaxSuppressionTypes);
30353944Sdim  internal_memset(has_suppression_type_, 0, suppression_types_num_);
31353944Sdim}
32353944Sdim
33353944Sdim#if !SANITIZER_FUCHSIA
34353944Sdimstatic bool GetPathAssumingFileIsRelativeToExec(const char *file_path,
35353944Sdim                                                /*out*/char *new_file_path,
36353944Sdim                                                uptr new_file_path_size) {
37353944Sdim  InternalScopedString exec(kMaxPathLength);
38353944Sdim  if (ReadBinaryNameCached(exec.data(), exec.size())) {
39353944Sdim    const char *file_name_pos = StripModuleName(exec.data());
40353944Sdim    uptr path_to_exec_len = file_name_pos - exec.data();
41353944Sdim    internal_strncat(new_file_path, exec.data(),
42353944Sdim                     Min(path_to_exec_len, new_file_path_size - 1));
43353944Sdim    internal_strncat(new_file_path, file_path,
44353944Sdim                     new_file_path_size - internal_strlen(new_file_path) - 1);
45353944Sdim    return true;
46353944Sdim  }
47353944Sdim  return false;
48353944Sdim}
49353944Sdim
50353944Sdimstatic const char *FindFile(const char *file_path,
51353944Sdim                            /*out*/char *new_file_path,
52353944Sdim                            uptr new_file_path_size) {
53353944Sdim  // If we cannot find the file, check if its location is relative to
54353944Sdim  // the location of the executable.
55353944Sdim  if (!FileExists(file_path) && !IsAbsolutePath(file_path) &&
56353944Sdim      GetPathAssumingFileIsRelativeToExec(file_path, new_file_path,
57353944Sdim                                          new_file_path_size)) {
58353944Sdim    return new_file_path;
59353944Sdim  }
60353944Sdim  return file_path;
61353944Sdim}
62353944Sdim#else
63353944Sdimstatic const char *FindFile(const char *file_path, char *, uptr) {
64353944Sdim  return file_path;
65353944Sdim}
66353944Sdim#endif
67353944Sdim
68353944Sdimvoid SuppressionContext::ParseFromFile(const char *filename) {
69353944Sdim  if (filename[0] == '\0')
70353944Sdim    return;
71353944Sdim
72353944Sdim  InternalScopedString new_file_path(kMaxPathLength);
73353944Sdim  filename = FindFile(filename, new_file_path.data(), new_file_path.size());
74353944Sdim
75353944Sdim  // Read the file.
76353944Sdim  VPrintf(1, "%s: reading suppressions file at %s\n",
77353944Sdim          SanitizerToolName, filename);
78353944Sdim  char *file_contents;
79353944Sdim  uptr buffer_size;
80353944Sdim  uptr contents_size;
81353944Sdim  if (!ReadFileToBuffer(filename, &file_contents, &buffer_size,
82353944Sdim                        &contents_size)) {
83353944Sdim    Printf("%s: failed to read suppressions file '%s'\n", SanitizerToolName,
84353944Sdim           filename);
85353944Sdim    Die();
86353944Sdim  }
87353944Sdim
88353944Sdim  Parse(file_contents);
89353944Sdim}
90353944Sdim
91353944Sdimbool SuppressionContext::Match(const char *str, const char *type,
92353944Sdim                               Suppression **s) {
93353944Sdim  can_parse_ = false;
94353944Sdim  if (!HasSuppressionType(type))
95353944Sdim    return false;
96353944Sdim  for (uptr i = 0; i < suppressions_.size(); i++) {
97353944Sdim    Suppression &cur = suppressions_[i];
98353944Sdim    if (0 == internal_strcmp(cur.type, type) && TemplateMatch(cur.templ, str)) {
99353944Sdim      *s = &cur;
100353944Sdim      return true;
101353944Sdim    }
102353944Sdim  }
103353944Sdim  return false;
104353944Sdim}
105353944Sdim
106353944Sdimstatic const char *StripPrefix(const char *str, const char *prefix) {
107353944Sdim  while (*str && *str == *prefix) {
108353944Sdim    str++;
109353944Sdim    prefix++;
110353944Sdim  }
111353944Sdim  if (!*prefix)
112353944Sdim    return str;
113353944Sdim  return 0;
114353944Sdim}
115353944Sdim
116353944Sdimvoid SuppressionContext::Parse(const char *str) {
117353944Sdim  // Context must not mutate once Match has been called.
118353944Sdim  CHECK(can_parse_);
119353944Sdim  const char *line = str;
120353944Sdim  while (line) {
121353944Sdim    while (line[0] == ' ' || line[0] == '\t')
122353944Sdim      line++;
123353944Sdim    const char *end = internal_strchr(line, '\n');
124353944Sdim    if (end == 0)
125353944Sdim      end = line + internal_strlen(line);
126353944Sdim    if (line != end && line[0] != '#') {
127353944Sdim      const char *end2 = end;
128353944Sdim      while (line != end2 &&
129353944Sdim             (end2[-1] == ' ' || end2[-1] == '\t' || end2[-1] == '\r'))
130353944Sdim        end2--;
131353944Sdim      int type;
132353944Sdim      for (type = 0; type < suppression_types_num_; type++) {
133353944Sdim        const char *next_char = StripPrefix(line, suppression_types_[type]);
134353944Sdim        if (next_char && *next_char == ':') {
135353944Sdim          line = ++next_char;
136353944Sdim          break;
137353944Sdim        }
138353944Sdim      }
139353944Sdim      if (type == suppression_types_num_) {
140353944Sdim        Printf("%s: failed to parse suppressions\n", SanitizerToolName);
141353944Sdim        Die();
142353944Sdim      }
143353944Sdim      Suppression s;
144353944Sdim      s.type = suppression_types_[type];
145353944Sdim      s.templ = (char*)InternalAlloc(end2 - line + 1);
146353944Sdim      internal_memcpy(s.templ, line, end2 - line);
147353944Sdim      s.templ[end2 - line] = 0;
148353944Sdim      suppressions_.push_back(s);
149353944Sdim      has_suppression_type_[type] = true;
150353944Sdim    }
151353944Sdim    if (end[0] == 0)
152353944Sdim      break;
153353944Sdim    line = end + 1;
154353944Sdim  }
155353944Sdim}
156353944Sdim
157353944Sdimuptr SuppressionContext::SuppressionCount() const {
158353944Sdim  return suppressions_.size();
159353944Sdim}
160353944Sdim
161353944Sdimbool SuppressionContext::HasSuppressionType(const char *type) const {
162353944Sdim  for (int i = 0; i < suppression_types_num_; i++) {
163353944Sdim    if (0 == internal_strcmp(type, suppression_types_[i]))
164353944Sdim      return has_suppression_type_[i];
165353944Sdim  }
166353944Sdim  return false;
167353944Sdim}
168353944Sdim
169353944Sdimconst Suppression *SuppressionContext::SuppressionAt(uptr i) const {
170353944Sdim  CHECK_LT(i, suppressions_.size());
171353944Sdim  return &suppressions_[i];
172353944Sdim}
173353944Sdim
174353944Sdimvoid SuppressionContext::GetMatched(
175353944Sdim    InternalMmapVector<Suppression *> *matched) {
176353944Sdim  for (uptr i = 0; i < suppressions_.size(); i++)
177353944Sdim    if (atomic_load_relaxed(&suppressions_[i].hit_count))
178353944Sdim      matched->push_back(&suppressions_[i]);
179353944Sdim}
180353944Sdim
181353944Sdim}  // namespace __sanitizer
182