1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
|
//===-- SpecialCaseList.cpp - special case list for sanitizers ------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This is a utility class for instrumentation passes (like AddressSanitizer
// or ThreadSanitizer) to avoid instrumenting some functions or global
// variables, or to instrument some functions or global variables in a specific
// way, based on a user-supplied list.
//
//===----------------------------------------------------------------------===//
#include "llvm/Support/SpecialCaseList.h"
#include "llvm/Support/LineIterator.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/VirtualFileSystem.h"
#include <stdio.h>
#include <string>
#include <system_error>
#include <utility>
namespace llvm {
Error SpecialCaseList::Matcher::insert(StringRef Pattern, unsigned LineNumber,
bool UseGlobs) {
if (Pattern.empty())
return createStringError(errc::invalid_argument,
Twine("Supplied ") +
(UseGlobs ? "glob" : "regex") + " was blank");
if (!UseGlobs) {
// Replace * with .*
auto Regexp = Pattern.str();
for (size_t pos = 0; (pos = Regexp.find('*', pos)) != std::string::npos;
pos += strlen(".*")) {
Regexp.replace(pos, strlen("*"), ".*");
}
Regexp = (Twine("^(") + StringRef(Regexp) + ")$").str();
// Check that the regexp is valid.
Regex CheckRE(Regexp);
std::string REError;
if (!CheckRE.isValid(REError))
return createStringError(errc::invalid_argument, REError);
RegExes.emplace_back(std::make_pair(
std::make_unique<Regex>(std::move(CheckRE)), LineNumber));
return Error::success();
}
auto [It, DidEmplace] = Globs.try_emplace(Pattern);
if (DidEmplace) {
// We must be sure to use the string in the map rather than the provided
// reference which could be destroyed before match() is called
Pattern = It->getKey();
auto &Pair = It->getValue();
if (auto Err = GlobPattern::create(Pattern, /*MaxSubPatterns=*/1024)
.moveInto(Pair.first))
return Err;
Pair.second = LineNumber;
}
return Error::success();
}
unsigned SpecialCaseList::Matcher::match(StringRef Query) const {
for (const auto &[Pattern, Pair] : Globs)
if (Pair.first.match(Query))
return Pair.second;
for (const auto &[Regex, LineNumber] : RegExes)
if (Regex->match(Query))
return LineNumber;
return 0;
}
// TODO: Refactor this to return Expected<...>
std::unique_ptr<SpecialCaseList>
SpecialCaseList::create(const std::vector<std::string> &Paths,
llvm::vfs::FileSystem &FS, std::string &Error) {
std::unique_ptr<SpecialCaseList> SCL(new SpecialCaseList());
if (SCL->createInternal(Paths, FS, Error))
return SCL;
return nullptr;
}
std::unique_ptr<SpecialCaseList> SpecialCaseList::create(const MemoryBuffer *MB,
std::string &Error) {
std::unique_ptr<SpecialCaseList> SCL(new SpecialCaseList());
if (SCL->createInternal(MB, Error))
return SCL;
return nullptr;
}
std::unique_ptr<SpecialCaseList>
SpecialCaseList::createOrDie(const std::vector<std::string> &Paths,
llvm::vfs::FileSystem &FS) {
std::string Error;
if (auto SCL = create(Paths, FS, Error))
return SCL;
report_fatal_error(Twine(Error));
}
bool SpecialCaseList::createInternal(const std::vector<std::string> &Paths,
vfs::FileSystem &VFS, std::string &Error) {
for (const auto &Path : Paths) {
ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
VFS.getBufferForFile(Path);
if (std::error_code EC = FileOrErr.getError()) {
Error = (Twine("can't open file '") + Path + "': " + EC.message()).str();
return false;
}
std::string ParseError;
if (!parse(FileOrErr.get().get(), ParseError)) {
Error = (Twine("error parsing file '") + Path + "': " + ParseError).str();
return false;
}
}
return true;
}
bool SpecialCaseList::createInternal(const MemoryBuffer *MB,
std::string &Error) {
if (!parse(MB, Error))
return false;
return true;
}
Expected<SpecialCaseList::Section *>
SpecialCaseList::addSection(StringRef SectionStr, unsigned LineNo,
bool UseGlobs) {
auto [It, DidEmplace] = Sections.try_emplace(SectionStr);
auto &Section = It->getValue();
if (DidEmplace)
if (auto Err = Section.SectionMatcher->insert(SectionStr, LineNo, UseGlobs))
return createStringError(errc::invalid_argument,
"malformed section at line " + Twine(LineNo) +
": '" + SectionStr +
"': " + toString(std::move(Err)));
return &Section;
}
bool SpecialCaseList::parse(const MemoryBuffer *MB, std::string &Error) {
Section *CurrentSection;
if (auto Err = addSection("*", 1).moveInto(CurrentSection)) {
Error = toString(std::move(Err));
return false;
}
// In https://reviews.llvm.org/D154014 we added glob support and planned to
// remove regex support in patterns. We temporarily support the original
// behavior using regexes if "#!special-case-list-v1" is the first line of the
// file. For more details, see
// https://discourse.llvm.org/t/use-glob-instead-of-regex-for-specialcaselists/71666
bool UseGlobs = !MB->getBuffer().starts_with("#!special-case-list-v1\n");
for (line_iterator LineIt(*MB, /*SkipBlanks=*/true, /*CommentMarker=*/'#');
!LineIt.is_at_eof(); LineIt++) {
unsigned LineNo = LineIt.line_number();
StringRef Line = LineIt->trim();
if (Line.empty())
continue;
// Save section names
if (Line.starts_with("[")) {
if (!Line.ends_with("]")) {
Error =
("malformed section header on line " + Twine(LineNo) + ": " + Line)
.str();
return false;
}
if (auto Err = addSection(Line.drop_front().drop_back(), LineNo, UseGlobs)
.moveInto(CurrentSection)) {
Error = toString(std::move(Err));
return false;
}
continue;
}
// Get our prefix and unparsed glob.
auto [Prefix, Postfix] = Line.split(":");
if (Postfix.empty()) {
// Missing ':' in the line.
Error = ("malformed line " + Twine(LineNo) + ": '" + Line + "'").str();
return false;
}
auto [Pattern, Category] = Postfix.split("=");
auto &Entry = CurrentSection->Entries[Prefix][Category];
if (auto Err = Entry.insert(Pattern, LineNo, UseGlobs)) {
Error =
(Twine("malformed ") + (UseGlobs ? "glob" : "regex") + " in line " +
Twine(LineNo) + ": '" + Pattern + "': " + toString(std::move(Err)))
.str();
return false;
}
}
return true;
}
SpecialCaseList::~SpecialCaseList() = default;
bool SpecialCaseList::inSection(StringRef Section, StringRef Prefix,
StringRef Query, StringRef Category) const {
return inSectionBlame(Section, Prefix, Query, Category);
}
unsigned SpecialCaseList::inSectionBlame(StringRef Section, StringRef Prefix,
StringRef Query,
StringRef Category) const {
for (const auto &It : Sections) {
const auto &S = It.getValue();
if (S.SectionMatcher->match(Section)) {
unsigned Blame = inSectionBlame(S.Entries, Prefix, Query, Category);
if (Blame)
return Blame;
}
}
return 0;
}
unsigned SpecialCaseList::inSectionBlame(const SectionEntries &Entries,
StringRef Prefix, StringRef Query,
StringRef Category) const {
SectionEntries::const_iterator I = Entries.find(Prefix);
if (I == Entries.end()) return 0;
StringMap<Matcher>::const_iterator II = I->second.find(Category);
if (II == I->second.end()) return 0;
return II->getValue().match(Query);
}
} // namespace llvm
|