1//===-- SpecialCaseList.cpp - special case list for sanitizers ------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This is a utility class for instrumentation passes (like AddressSanitizer
10// or ThreadSanitizer) to avoid instrumenting some functions or global
11// variables, or to instrument some functions or global variables in a specific
12// way, based on a user-supplied list.
13//
14//===----------------------------------------------------------------------===//
15
16#include "llvm/Support/SpecialCaseList.h"
17#include "llvm/ADT/SmallVector.h"
18#include "llvm/ADT/StringExtras.h"
19#include "llvm/Support/MemoryBuffer.h"
20#include "llvm/Support/Regex.h"
21#include "llvm/Support/VirtualFileSystem.h"
22#include <string>
23#include <system_error>
24#include <utility>
25
26#include <stdio.h>
27namespace llvm {
28
29bool SpecialCaseList::Matcher::insert(std::string Regexp,
30                                      unsigned LineNumber,
31                                      std::string &REError) {
32  if (Regexp.empty()) {
33    REError = "Supplied regexp was blank";
34    return false;
35  }
36
37  if (Regex::isLiteralERE(Regexp)) {
38    Strings[Regexp] = LineNumber;
39    return true;
40  }
41  Trigrams.insert(Regexp);
42
43  // Replace * with .*
44  for (size_t pos = 0; (pos = Regexp.find('*', pos)) != std::string::npos;
45       pos += strlen(".*")) {
46    Regexp.replace(pos, strlen("*"), ".*");
47  }
48
49  Regexp = (Twine("^(") + StringRef(Regexp) + ")$").str();
50
51  // Check that the regexp is valid.
52  Regex CheckRE(Regexp);
53  if (!CheckRE.isValid(REError))
54    return false;
55
56  RegExes.emplace_back(
57      std::make_pair(std::make_unique<Regex>(std::move(CheckRE)), LineNumber));
58  return true;
59}
60
61unsigned SpecialCaseList::Matcher::match(StringRef Query) const {
62  auto It = Strings.find(Query);
63  if (It != Strings.end())
64    return It->second;
65  if (Trigrams.isDefinitelyOut(Query))
66    return false;
67  for (auto& RegExKV : RegExes)
68    if (RegExKV.first->match(Query))
69      return RegExKV.second;
70  return 0;
71}
72
73std::unique_ptr<SpecialCaseList>
74SpecialCaseList::create(const std::vector<std::string> &Paths,
75                        llvm::vfs::FileSystem &FS, std::string &Error) {
76  std::unique_ptr<SpecialCaseList> SCL(new SpecialCaseList());
77  if (SCL->createInternal(Paths, FS, Error))
78    return SCL;
79  return nullptr;
80}
81
82std::unique_ptr<SpecialCaseList> SpecialCaseList::create(const MemoryBuffer *MB,
83                                                         std::string &Error) {
84  std::unique_ptr<SpecialCaseList> SCL(new SpecialCaseList());
85  if (SCL->createInternal(MB, Error))
86    return SCL;
87  return nullptr;
88}
89
90std::unique_ptr<SpecialCaseList>
91SpecialCaseList::createOrDie(const std::vector<std::string> &Paths,
92                             llvm::vfs::FileSystem &FS) {
93  std::string Error;
94  if (auto SCL = create(Paths, FS, Error))
95    return SCL;
96  report_fatal_error(Error);
97}
98
99bool SpecialCaseList::createInternal(const std::vector<std::string> &Paths,
100                                     vfs::FileSystem &VFS, std::string &Error) {
101  StringMap<size_t> Sections;
102  for (const auto &Path : Paths) {
103    ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
104        VFS.getBufferForFile(Path);
105    if (std::error_code EC = FileOrErr.getError()) {
106      Error = (Twine("can't open file '") + Path + "': " + EC.message()).str();
107      return false;
108    }
109    std::string ParseError;
110    if (!parse(FileOrErr.get().get(), Sections, ParseError)) {
111      Error = (Twine("error parsing file '") + Path + "': " + ParseError).str();
112      return false;
113    }
114  }
115  return true;
116}
117
118bool SpecialCaseList::createInternal(const MemoryBuffer *MB,
119                                     std::string &Error) {
120  StringMap<size_t> Sections;
121  if (!parse(MB, Sections, Error))
122    return false;
123  return true;
124}
125
126bool SpecialCaseList::parse(const MemoryBuffer *MB,
127                            StringMap<size_t> &SectionsMap,
128                            std::string &Error) {
129  // Iterate through each line in the exclusion list file.
130  SmallVector<StringRef, 16> Lines;
131  MB->getBuffer().split(Lines, '\n');
132
133  unsigned LineNo = 1;
134  StringRef Section = "*";
135
136  for (auto I = Lines.begin(), E = Lines.end(); I != E; ++I, ++LineNo) {
137    *I = I->trim();
138    // Ignore empty lines and lines starting with "#"
139    if (I->empty() || I->startswith("#"))
140      continue;
141
142    // Save section names
143    if (I->startswith("[")) {
144      if (!I->endswith("]")) {
145        Error = (Twine("malformed section header on line ") + Twine(LineNo) +
146                 ": " + *I).str();
147        return false;
148      }
149
150      Section = I->slice(1, I->size() - 1);
151
152      std::string REError;
153      Regex CheckRE(Section);
154      if (!CheckRE.isValid(REError)) {
155        Error =
156            (Twine("malformed regex for section ") + Section + ": '" + REError)
157                .str();
158        return false;
159      }
160
161      continue;
162    }
163
164    // Get our prefix and unparsed regexp.
165    std::pair<StringRef, StringRef> SplitLine = I->split(":");
166    StringRef Prefix = SplitLine.first;
167    if (SplitLine.second.empty()) {
168      // Missing ':' in the line.
169      Error = (Twine("malformed line ") + Twine(LineNo) + ": '" +
170               SplitLine.first + "'").str();
171      return false;
172    }
173
174    std::pair<StringRef, StringRef> SplitRegexp = SplitLine.second.split("=");
175    std::string Regexp = std::string(SplitRegexp.first);
176    StringRef Category = SplitRegexp.second;
177
178    // Create this section if it has not been seen before.
179    if (SectionsMap.find(Section) == SectionsMap.end()) {
180      std::unique_ptr<Matcher> M = std::make_unique<Matcher>();
181      std::string REError;
182      if (!M->insert(std::string(Section), LineNo, REError)) {
183        Error = (Twine("malformed section ") + Section + ": '" + REError).str();
184        return false;
185      }
186
187      SectionsMap[Section] = Sections.size();
188      Sections.emplace_back(std::move(M));
189    }
190
191    auto &Entry = Sections[SectionsMap[Section]].Entries[Prefix][Category];
192    std::string REError;
193    if (!Entry.insert(std::move(Regexp), LineNo, REError)) {
194      Error = (Twine("malformed regex in line ") + Twine(LineNo) + ": '" +
195               SplitLine.second + "': " + REError).str();
196      return false;
197    }
198  }
199  return true;
200}
201
202SpecialCaseList::~SpecialCaseList() {}
203
204bool SpecialCaseList::inSection(StringRef Section, StringRef Prefix,
205                                StringRef Query, StringRef Category) const {
206  return inSectionBlame(Section, Prefix, Query, Category);
207}
208
209unsigned SpecialCaseList::inSectionBlame(StringRef Section, StringRef Prefix,
210                                         StringRef Query,
211                                         StringRef Category) const {
212  for (auto &SectionIter : Sections)
213    if (SectionIter.SectionMatcher->match(Section)) {
214      unsigned Blame =
215          inSectionBlame(SectionIter.Entries, Prefix, Query, Category);
216      if (Blame)
217        return Blame;
218    }
219  return 0;
220}
221
222unsigned SpecialCaseList::inSectionBlame(const SectionEntries &Entries,
223                                         StringRef Prefix, StringRef Query,
224                                         StringRef Category) const {
225  SectionEntries::const_iterator I = Entries.find(Prefix);
226  if (I == Entries.end()) return 0;
227  StringMap<Matcher>::const_iterator II = I->second.find(Category);
228  if (II == I->second.end()) return 0;
229
230  return II->getValue().match(Query);
231}
232
233}  // namespace llvm
234