From b9b8027ceec93ad9ed02d5b68130ccf5f06ae30b Mon Sep 17 00:00:00 2001 From: Alexey Samsonov Date: Wed, 4 Feb 2015 17:39:48 +0000 Subject: [PATCH] SpecialCaseList: Add support for parsing multiple input files. Summary: This change allows users to create SpecialCaseList objects from multiple local files. This is needed to implement a proper support for -fsanitize-blacklist flag (allow users to specify multiple blacklists, in addition to default blacklist, see PR22431). DFSan can also benefit from this change, as DFSan instrumentation pass now accepts ABI-lists both from -fsanitize-blacklist= and -mllvm -dfsan-abilist flags. Go bindings are fixed accordingly. Test Plan: regression test suite Reviewers: pcc Subscribers: llvm-commits, axw, kcc Differential Revision: http://reviews.llvm.org/D7367 llvm-svn: 228155 --- llvm/bindings/go/llvm/InstrumentationBindings.cpp | 9 +++- llvm/bindings/go/llvm/InstrumentationBindings.h | 4 +- .../bindings/go/llvm/transforms_instrumentation.go | 11 ++-- llvm/include/llvm/Support/SpecialCaseList.h | 26 ++++++---- llvm/include/llvm/Transforms/Instrumentation.h | 12 ++--- llvm/lib/Support/SpecialCaseList.cpp | 58 +++++++++++++--------- .../Instrumentation/DataFlowSanitizer.cpp | 37 ++++++++------ llvm/unittests/Support/SpecialCaseListTest.cpp | 38 +++++++++++--- 8 files changed, 126 insertions(+), 69 deletions(-) diff --git a/llvm/bindings/go/llvm/InstrumentationBindings.cpp b/llvm/bindings/go/llvm/InstrumentationBindings.cpp index b604abb..b7f2949 100644 --- a/llvm/bindings/go/llvm/InstrumentationBindings.cpp +++ b/llvm/bindings/go/llvm/InstrumentationBindings.cpp @@ -37,6 +37,11 @@ void LLVMAddMemorySanitizerPass(LLVMPassManagerRef PM) { } void LLVMAddDataFlowSanitizerPass(LLVMPassManagerRef PM, - const char *ABIListFile) { - unwrap(PM)->add(createDataFlowSanitizerPass(ABIListFile)); + int ABIListFilesNum, + const char **ABIListFiles) { + std::vector ABIListFilesVec; + for (int i = 0; i != ABIListFilesNum; ++i) { + ABIListFilesVec.push_back(ABIListFiles[i]); + } + unwrap(PM)->add(createDataFlowSanitizerPass(ABIListFilesVec)); } diff --git a/llvm/bindings/go/llvm/InstrumentationBindings.h b/llvm/bindings/go/llvm/InstrumentationBindings.h index e8dbd59..97af2d5 100644 --- a/llvm/bindings/go/llvm/InstrumentationBindings.h +++ b/llvm/bindings/go/llvm/InstrumentationBindings.h @@ -28,8 +28,8 @@ void LLVMAddAddressSanitizerFunctionPass(LLVMPassManagerRef PM); void LLVMAddAddressSanitizerModulePass(LLVMPassManagerRef PM); void LLVMAddThreadSanitizerPass(LLVMPassManagerRef PM); void LLVMAddMemorySanitizerPass(LLVMPassManagerRef PM); -void LLVMAddDataFlowSanitizerPass(LLVMPassManagerRef PM, - const char *ABIListFile); +void LLVMAddDataFlowSanitizerPass(LLVMPassManagerRef PM, int ABIListFilesNum, + const char **ABIListFiles); #ifdef __cplusplus } diff --git a/llvm/bindings/go/llvm/transforms_instrumentation.go b/llvm/bindings/go/llvm/transforms_instrumentation.go index 9b191b26..73e2732 100644 --- a/llvm/bindings/go/llvm/transforms_instrumentation.go +++ b/llvm/bindings/go/llvm/transforms_instrumentation.go @@ -36,8 +36,11 @@ func (pm PassManager) AddMemorySanitizerPass() { C.LLVMAddMemorySanitizerPass(pm.C) } -func (pm PassManager) AddDataFlowSanitizerPass(abilist string) { - cabilist := C.CString(abilist) - defer C.free(unsafe.Pointer(cabilist)) - C.LLVMAddDataFlowSanitizerPass(pm.C, cabilist) +func (pm PassManager) AddDataFlowSanitizerPass(abilist []string) { + abiliststrs := make([]*C.char, len(abilist)) + for i, arg := range abilist { + abiliststrs[i] = C.CString(arg) + defer C.free(unsafe.Pointer(abiliststrs[i])) + } + C.LLVMAddDataFlowSanitizerPass(pm.C, C.int(len(abilist)), &abiliststrs[0]) } diff --git a/llvm/include/llvm/Support/SpecialCaseList.h b/llvm/include/llvm/Support/SpecialCaseList.h index 313212e..af62edb 100644 --- a/llvm/include/llvm/Support/SpecialCaseList.h +++ b/llvm/include/llvm/Support/SpecialCaseList.h @@ -49,6 +49,8 @@ #define LLVM_SUPPORT_SPECIALCASELIST_H #include "llvm/ADT/StringMap.h" +#include +#include namespace llvm { class MemoryBuffer; @@ -57,18 +59,18 @@ class StringRef; class SpecialCaseList { public: - /// Parses the special case list from a file. If Path is empty, returns - /// an empty special case list. On failure, returns 0 and writes an error - /// message to string. - static std::unique_ptr create(StringRef Path, - std::string &Error); + /// Parses the special case list entries from files. On failure, returns + /// 0 and writes an error message to string. + static std::unique_ptr + create(const std::vector &Paths, std::string &Error); /// Parses the special case list from a memory buffer. On failure, returns /// 0 and writes an error message to string. static std::unique_ptr create(const MemoryBuffer *MB, - std::string &Error); - /// Parses the special case list from a file. On failure, reports a fatal - /// error. - static std::unique_ptr createOrDie(StringRef Path); + std::string &Error); + /// Parses the special case list entries from files. On failure, reports a + /// fatal error. + static std::unique_ptr + createOrDie(const std::vector &Paths); ~SpecialCaseList(); @@ -85,11 +87,15 @@ private: SpecialCaseList &operator=(SpecialCaseList const &) LLVM_DELETED_FUNCTION; struct Entry; - StringMap > Entries; + StringMap> Entries; + StringMap> Regexps; + bool IsCompiled; SpecialCaseList(); /// Parses just-constructed SpecialCaseList entries from a memory buffer. bool parse(const MemoryBuffer *MB, std::string &Error); + /// compile() should be called once, after parsing all the memory buffers. + void compile(); }; } // namespace llvm diff --git a/llvm/include/llvm/Transforms/Instrumentation.h b/llvm/include/llvm/Transforms/Instrumentation.h index 24e3ef7..1f034bc 100644 --- a/llvm/include/llvm/Transforms/Instrumentation.h +++ b/llvm/include/llvm/Transforms/Instrumentation.h @@ -86,17 +86,17 @@ FunctionPass *createMemorySanitizerPass(int TrackOrigins = 0); FunctionPass *createThreadSanitizerPass(); // Insert DataFlowSanitizer (dynamic data flow analysis) instrumentation -ModulePass *createDataFlowSanitizerPass(StringRef ABIListFile = StringRef(), - void *(*getArgTLS)() = nullptr, - void *(*getRetValTLS)() = nullptr); +ModulePass *createDataFlowSanitizerPass( + const std::vector &ABIListFiles = std::vector(), + void *(*getArgTLS)() = nullptr, void *(*getRetValTLS)() = nullptr); // Insert SanitizerCoverage instrumentation. ModulePass *createSanitizerCoverageModulePass(int CoverageLevel); #if defined(__GNUC__) && defined(__linux__) && !defined(ANDROID) -inline ModulePass *createDataFlowSanitizerPassForJIT(StringRef ABIListFile = - StringRef()) { - return createDataFlowSanitizerPass(ABIListFile, getDFSanArgTLSPtrForJIT, +inline ModulePass *createDataFlowSanitizerPassForJIT( + const std::vector &ABIListFiles = std::vector()) { + return createDataFlowSanitizerPass(ABIListFiles, getDFSanArgTLSPtrForJIT, getDFSanRetValTLSPtrForJIT); } #endif diff --git a/llvm/lib/Support/SpecialCaseList.cpp b/llvm/lib/Support/SpecialCaseList.cpp index 785cc60..c312cc1 100644 --- a/llvm/lib/Support/SpecialCaseList.cpp +++ b/llvm/lib/Support/SpecialCaseList.cpp @@ -46,19 +46,27 @@ struct SpecialCaseList::Entry { } }; -SpecialCaseList::SpecialCaseList() : Entries() {} +SpecialCaseList::SpecialCaseList() : Entries(), Regexps(), IsCompiled(false) {} -std::unique_ptr SpecialCaseList::create(StringRef Path, - std::string &Error) { - if (Path.empty()) - return std::unique_ptr(new SpecialCaseList()); - ErrorOr> FileOrErr = - MemoryBuffer::getFile(Path); - if (std::error_code EC = FileOrErr.getError()) { - Error = (Twine("Can't open file '") + Path + "': " + EC.message()).str(); - return nullptr; +std::unique_ptr +SpecialCaseList::create(const std::vector &Paths, + std::string &Error) { + std::unique_ptr SCL(new SpecialCaseList()); + for (auto Path : Paths) { + ErrorOr> FileOrErr = + MemoryBuffer::getFile(Path); + if (std::error_code EC = FileOrErr.getError()) { + Error = (Twine("can't open file '") + Path + "': " + EC.message()).str(); + return nullptr; + } + std::string ParseError; + if (!SCL->parse(FileOrErr.get().get(), ParseError)) { + Error = (Twine("error parsing file '") + Path + "': " + ParseError).str(); + return nullptr; + } } - return create(FileOrErr.get().get(), Error); + SCL->compile(); + return SCL; } std::unique_ptr SpecialCaseList::create(const MemoryBuffer *MB, @@ -66,12 +74,14 @@ std::unique_ptr SpecialCaseList::create(const MemoryBuffer *MB, std::unique_ptr SCL(new SpecialCaseList()); if (!SCL->parse(MB, Error)) return nullptr; + SCL->compile(); return SCL; } -std::unique_ptr SpecialCaseList::createOrDie(StringRef Path) { +std::unique_ptr +SpecialCaseList::createOrDie(const std::vector &Paths) { std::string Error; - if (auto SCL = create(Path, Error)) + if (auto SCL = create(Paths, Error)) return SCL; report_fatal_error(Error); } @@ -80,12 +90,8 @@ bool SpecialCaseList::parse(const MemoryBuffer *MB, std::string &Error) { // Iterate through each line in the blacklist file. SmallVector Lines; SplitString(MB->getBuffer(), Lines, "\n\r"); - StringMap > Regexps; - assert(Entries.empty() && - "parse() should be called on an empty SpecialCaseList"); int LineNo = 1; - for (SmallVectorImpl::iterator I = Lines.begin(), E = Lines.end(); - I != E; ++I, ++LineNo) { + for (auto I = Lines.begin(), E = Lines.end(); I != E; ++I, ++LineNo) { // Ignore empty lines and lines starting with "#" if (I->empty() || I->startswith("#")) continue; @@ -94,7 +100,7 @@ bool SpecialCaseList::parse(const MemoryBuffer *MB, std::string &Error) { StringRef Prefix = SplitLine.first; if (SplitLine.second.empty()) { // Missing ':' in the line. - Error = (Twine("Malformed line ") + Twine(LineNo) + ": '" + + Error = (Twine("malformed line ") + Twine(LineNo) + ": '" + SplitLine.first + "'").str(); return false; } @@ -119,7 +125,7 @@ bool SpecialCaseList::parse(const MemoryBuffer *MB, std::string &Error) { Regex CheckRE(Regexp); std::string REError; if (!CheckRE.isValid(REError)) { - Error = (Twine("Malformed regex in line ") + Twine(LineNo) + ": '" + + Error = (Twine("malformed regex in line ") + Twine(LineNo) + ": '" + SplitLine.second + "': " + REError).str(); return false; } @@ -129,10 +135,14 @@ bool SpecialCaseList::parse(const MemoryBuffer *MB, std::string &Error) { Regexps[Prefix][Category] += "|"; Regexps[Prefix][Category] += "^" + Regexp + "$"; } + return true; +} +void SpecialCaseList::compile() { + assert(!IsCompiled && "compile() should only be called once"); // Iterate through each of the prefixes, and create Regexs for them. - for (StringMap >::const_iterator I = Regexps.begin(), - E = Regexps.end(); + for (StringMap>::const_iterator I = Regexps.begin(), + E = Regexps.end(); I != E; ++I) { for (StringMap::const_iterator II = I->second.begin(), IE = I->second.end(); @@ -140,13 +150,15 @@ bool SpecialCaseList::parse(const MemoryBuffer *MB, std::string &Error) { Entries[I->getKey()][II->getKey()].RegEx.reset(new Regex(II->getValue())); } } - return true; + Regexps.clear(); + IsCompiled = true; } SpecialCaseList::~SpecialCaseList() {} bool SpecialCaseList::inSection(StringRef Section, StringRef Query, StringRef Category) const { + assert(IsCompiled && "SpecialCaseList::compile() was not called!"); StringMap >::const_iterator I = Entries.find(Section); if (I == Entries.end()) return false; StringMap::const_iterator II = I->second.find(Category); diff --git a/llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp index f722281..ae1c228 100644 --- a/llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp @@ -83,14 +83,14 @@ static cl::opt ClPreserveAlignment( cl::desc("respect alignment requirements provided by input IR"), cl::Hidden, cl::init(false)); -// The ABI list file controls how shadow parameters are passed. The pass treats +// The ABI list files control how shadow parameters are passed. The pass treats // every function labelled "uninstrumented" in the ABI list file as conforming // to the "native" (i.e. unsanitized) ABI. Unless the ABI list contains // additional annotations for those functions, a call to one of those functions // will produce a warning message, as the labelling behaviour of the function is // unknown. The other supported annotations are "functional" and "discard", // which are described below under DataFlowSanitizer::WrapperKind. -static cl::opt ClABIListFile( +static cl::list ClABIListFiles( "dfsan-abilist", cl::desc("File listing native ABI functions and how the pass treats them"), cl::Hidden); @@ -141,7 +141,9 @@ class DFSanABIList { std::unique_ptr SCL; public: - DFSanABIList(std::unique_ptr SCL) : SCL(std::move(SCL)) {} + DFSanABIList() {} + + void set(std::unique_ptr List) { SCL = std::move(List); } /// Returns whether either this function or its source file are listed in the /// given category. @@ -264,9 +266,9 @@ class DataFlowSanitizer : public ModulePass { Constant *getOrBuildTrampolineFunction(FunctionType *FT, StringRef FName); public: - DataFlowSanitizer(StringRef ABIListFile = StringRef(), - void *(*getArgTLS)() = nullptr, - void *(*getRetValTLS)() = nullptr); + DataFlowSanitizer( + const std::vector &ABIListFiles = std::vector(), + void *(*getArgTLS)() = nullptr, void *(*getRetValTLS)() = nullptr); static char ID; bool doInitialization(Module &M) override; bool runOnModule(Module &M) override; @@ -351,18 +353,21 @@ char DataFlowSanitizer::ID; INITIALIZE_PASS(DataFlowSanitizer, "dfsan", "DataFlowSanitizer: dynamic data flow analysis.", false, false) -ModulePass *llvm::createDataFlowSanitizerPass(StringRef ABIListFile, - void *(*getArgTLS)(), - void *(*getRetValTLS)()) { - return new DataFlowSanitizer(ABIListFile, getArgTLS, getRetValTLS); +ModulePass * +llvm::createDataFlowSanitizerPass(const std::vector &ABIListFiles, + void *(*getArgTLS)(), + void *(*getRetValTLS)()) { + return new DataFlowSanitizer(ABIListFiles, getArgTLS, getRetValTLS); } -DataFlowSanitizer::DataFlowSanitizer(StringRef ABIListFile, - void *(*getArgTLS)(), - void *(*getRetValTLS)()) - : ModulePass(ID), GetArgTLSPtr(getArgTLS), GetRetvalTLSPtr(getRetValTLS), - ABIList(SpecialCaseList::createOrDie(ABIListFile.empty() ? ClABIListFile - : ABIListFile)) { +DataFlowSanitizer::DataFlowSanitizer( + const std::vector &ABIListFiles, void *(*getArgTLS)(), + void *(*getRetValTLS)()) + : ModulePass(ID), GetArgTLSPtr(getArgTLS), GetRetvalTLSPtr(getRetValTLS) { + std::vector AllABIListFiles(std::move(ABIListFiles)); + AllABIListFiles.insert(AllABIListFiles.end(), ClABIListFiles.begin(), + ClABIListFiles.end()); + ABIList.set(SpecialCaseList::createOrDie(AllABIListFiles)); } FunctionType *DataFlowSanitizer::getArgsFunctionType(FunctionType *T) { diff --git a/llvm/unittests/Support/SpecialCaseListTest.cpp b/llvm/unittests/Support/SpecialCaseListTest.cpp index 740dbfe..0657f80 100644 --- a/llvm/unittests/Support/SpecialCaseListTest.cpp +++ b/llvm/unittests/Support/SpecialCaseListTest.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SpecialCaseList.h" #include "gtest/gtest.h" @@ -30,6 +31,16 @@ protected: assert(Error == ""); return SCL; } + + std::string makeSpecialCaseListFile(StringRef Contents) { + int FD; + SmallString<64> Path; + sys::fs::createTemporaryFile("SpecialCaseListTest", "temp", FD, Path); + raw_fd_ostream OF(FD, true, true); + OF << Contents; + OF.close(); + return Path.str(); + } }; TEST_F(SpecialCaseListTest, Basic) { @@ -86,17 +97,18 @@ TEST_F(SpecialCaseListTest, Substring) { TEST_F(SpecialCaseListTest, InvalidSpecialCaseList) { std::string Error; EXPECT_EQ(nullptr, makeSpecialCaseList("badline", Error)); - EXPECT_EQ("Malformed line 1: 'badline'", Error); + EXPECT_EQ("malformed line 1: 'badline'", Error); EXPECT_EQ(nullptr, makeSpecialCaseList("src:bad[a-", Error)); - EXPECT_EQ("Malformed regex in line 1: 'bad[a-': invalid character range", + EXPECT_EQ("malformed regex in line 1: 'bad[a-': invalid character range", Error); EXPECT_EQ(nullptr, makeSpecialCaseList("src:a.c\n" "fun:fun(a\n", Error)); - EXPECT_EQ("Malformed regex in line 2: 'fun(a': parentheses not balanced", + EXPECT_EQ("malformed regex in line 2: 'fun(a': parentheses not balanced", Error); - EXPECT_EQ(nullptr, SpecialCaseList::create("unexisting", Error)); - EXPECT_EQ(0U, Error.find("Can't open file 'unexisting':")); + std::vector Files(1, "unexisting"); + EXPECT_EQ(nullptr, SpecialCaseList::create(Files, Error)); + EXPECT_EQ(0U, Error.find("can't open file 'unexisting':")); } TEST_F(SpecialCaseListTest, EmptySpecialCaseList) { @@ -104,6 +116,20 @@ TEST_F(SpecialCaseListTest, EmptySpecialCaseList) { EXPECT_FALSE(SCL->inSection("foo", "bar")); } +TEST_F(SpecialCaseListTest, MultipleBlacklists) { + std::vector Files; + Files.push_back(makeSpecialCaseListFile("src:bar\n" + "src:*foo*\n" + "src:ban=init\n")); + Files.push_back(makeSpecialCaseListFile("src:baz\n" + "src:*fog*\n")); + auto SCL = SpecialCaseList::createOrDie(Files); + EXPECT_TRUE(SCL->inSection("src", "bar")); + EXPECT_TRUE(SCL->inSection("src", "baz")); + EXPECT_FALSE(SCL->inSection("src", "ban")); + EXPECT_TRUE(SCL->inSection("src", "ban", "init")); + EXPECT_TRUE(SCL->inSection("src", "tomfoolery")); + EXPECT_TRUE(SCL->inSection("src", "tomfoglery")); } - +} -- 2.7.4