diff --git a/clang/include/clang/Basic/SanitizerSpecialCaseList.h b/clang/include/clang/Basic/SanitizerSpecialCaseList.h index d024b7dfc2e85..25d518e7128cf 100644 --- a/clang/include/clang/Basic/SanitizerSpecialCaseList.h +++ b/clang/include/clang/Basic/SanitizerSpecialCaseList.h @@ -43,13 +43,18 @@ class SanitizerSpecialCaseList : public llvm::SpecialCaseList { bool inSection(SanitizerMask Mask, StringRef Prefix, StringRef Query, StringRef Category = StringRef()) const; + // Query ignorelisted entries if any bit in Mask matches the entry's section. + // Return 0 if not found. If found, return the line number (starts with 1). + unsigned inSectionBlame(SanitizerMask Mask, StringRef Prefix, StringRef Query, + StringRef Category = StringRef()) const; + protected: // Initialize SanitizerSections. void createSanitizerSections(); struct SanitizerSection { SanitizerSection(SanitizerMask SM, SectionEntries &E) - : Mask(SM), Entries(E){}; + : Mask(SM), Entries(E) {}; SanitizerMask Mask; SectionEntries &Entries; diff --git a/clang/lib/Basic/NoSanitizeList.cpp b/clang/lib/Basic/NoSanitizeList.cpp index e7e63c1f419e6..811480f914ec5 100644 --- a/clang/lib/Basic/NoSanitizeList.cpp +++ b/clang/lib/Basic/NoSanitizeList.cpp @@ -44,6 +44,13 @@ bool NoSanitizeList::containsFunction(SanitizerMask Mask, bool NoSanitizeList::containsFile(SanitizerMask Mask, StringRef FileName, StringRef Category) const { + unsigned nosanline = SSCL->inSectionBlame(Mask, "src", FileName, Category); + unsigned sanline = SSCL->inSectionBlame(Mask, "src", FileName, "sanitize"); + // If we have two cases such as `src:a.cpp=sanitize` and `src:a.cpp`, the + // current entry override the previous entry. + if (nosanline > 0 && sanline > 0) { + return nosanline > sanline; + } return SSCL->inSection(Mask, "src", FileName, Category); } diff --git a/clang/lib/Basic/SanitizerSpecialCaseList.cpp b/clang/lib/Basic/SanitizerSpecialCaseList.cpp index 2dbf04c6ede97..7da36f3801453 100644 --- a/clang/lib/Basic/SanitizerSpecialCaseList.cpp +++ b/clang/lib/Basic/SanitizerSpecialCaseList.cpp @@ -63,3 +63,19 @@ bool SanitizerSpecialCaseList::inSection(SanitizerMask Mask, StringRef Prefix, return false; } + +unsigned SanitizerSpecialCaseList::inSectionBlame(SanitizerMask Mask, + StringRef Prefix, + StringRef Query, + StringRef Category) const { + for (auto &S : SanitizerSections) { + if (S.Mask & Mask) { + unsigned lineNum = + SpecialCaseList::inSectionBlame(S.Entries, Prefix, Query, Category); + if (lineNum > 0) { + return lineNum; + } + } + } + return 0; +} diff --git a/clang/test/CodeGen/ubsan-src-ignorelist-category.test b/clang/test/CodeGen/ubsan-src-ignorelist-category.test new file mode 100644 index 0000000000000..e0efd65df8652 --- /dev/null +++ b/clang/test/CodeGen/ubsan-src-ignorelist-category.test @@ -0,0 +1,37 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=signed-integer-overflow -fsanitize-ignorelist=%t/src.ignorelist -emit-llvm %t/test1.c -o - | FileCheck %s -check-prefix=CHECK-ALLOWLIST +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=signed-integer-overflow -fsanitize-ignorelist=%t/src.ignorelist -emit-llvm %t/test2.c -o - | FileCheck %s -check-prefix=CHECK-IGNORELIST +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=signed-integer-overflow -fsanitize-ignorelist=%t/src.ignorelist.contradict1 -emit-llvm %t/test1.c -o - | FileCheck %s -check-prefix=CHECK-ALLOWLISTOVERIDE1 +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=signed-integer-overflow -fsanitize-ignorelist=%t/src.ignorelist.contradict2 -emit-llvm %t/test1.c -o - | FileCheck %s -check-prefix=CHECK-ALLOWLISTOVERIDE2 + + +// Verify ubsan only emits checks for files in the allowlist + +//--- src.ignorelist +src:* +src:*/test1.c=sanitize + +//--- src.ignorelist.contradict1 +src:* +src:*/test1.c=sanitize +src:*/test1.c + +//--- src.ignorelist.contradict2 +src:* +src:*/test1.c +src:*/test1.c=sanitize + +//--- test1.c +int add1(int a, int b) { +// CHECK-ALLOWLIST: llvm.sadd.with.overflow.i32 +// CHECK-ALLOWLISTOVERIDE1-NOT: llvm.sadd.with.overflow.i32 +// CHECK-ALLOWLISTOVERIDE2: llvm.sadd.with.overflow.i32 + return a+b; +} + +//--- test2.c +int add2(int a, int b) { +// CHECK-IGNORELIST-NOT: llvm.sadd.with.overflow.i32 + return a+b; +} diff --git a/llvm/lib/Support/SpecialCaseList.cpp b/llvm/lib/Support/SpecialCaseList.cpp index dddf84cbb1ced..926aadb65d493 100644 --- a/llvm/lib/Support/SpecialCaseList.cpp +++ b/llvm/lib/Support/SpecialCaseList.cpp @@ -132,18 +132,24 @@ bool SpecialCaseList::createInternal(const MemoryBuffer *MB, Expected SpecialCaseList::addSection(StringRef SectionStr, unsigned LineNo, bool UseGlobs) { - Sections.emplace_back(); - auto &Section = Sections.back(); - Section.SectionStr = SectionStr; - - if (auto Err = Section.SectionMatcher->insert(SectionStr, LineNo, UseGlobs)) { + auto it = + std::find_if(Sections.begin(), Sections.end(), [&](const Section &s) { + return s.SectionStr == SectionStr; + }); + if (it == Sections.end()) { + Sections.emplace_back(); + auto &sec = Sections.back(); + sec.SectionStr = SectionStr; + } + it = std::prev(Sections.end()); + if (auto Err = it->SectionMatcher->insert(SectionStr, LineNo, UseGlobs)) { return createStringError(errc::invalid_argument, "malformed section at line " + Twine(LineNo) + ": '" + SectionStr + "': " + toString(std::move(Err))); } - return &Section; + return &(*it); } bool SpecialCaseList::parse(const MemoryBuffer *MB, std::string &Error) { diff --git a/llvm/unittests/Support/SpecialCaseListTest.cpp b/llvm/unittests/Support/SpecialCaseListTest.cpp index 4289a5e702155..f9185ea7483ca 100644 --- a/llvm/unittests/Support/SpecialCaseListTest.cpp +++ b/llvm/unittests/Support/SpecialCaseListTest.cpp @@ -306,4 +306,23 @@ TEST_F(SpecialCaseListTest, Version2) { EXPECT_TRUE(SCL->inSection("sect2", "fun", "bar")); EXPECT_FALSE(SCL->inSection("sect3", "fun", "bar")); } + +TEST_F(SpecialCaseListTest, Version3) { + std::unique_ptr SCL = makeSpecialCaseList("[sect1]\n" + "fun:foo*\n" + "[sect1]\n" + "fun:bar*\n" + "[sect2]\n" + "fun:def\n"); + EXPECT_TRUE(SCL->inSection("sect1", "fun", "fooz")); + EXPECT_TRUE(SCL->inSection("sect1", "fun", "barz")); + EXPECT_FALSE(SCL->inSection("sect2", "fun", "fooz")); + + EXPECT_TRUE(SCL->inSection("sect2", "fun", "def")); + EXPECT_FALSE(SCL->inSection("sect1", "fun", "def")); + + EXPECT_EQ(2u, SCL->inSectionBlame("sect1", "fun", "fooz")); + EXPECT_EQ(4u, SCL->inSectionBlame("sect1", "fun", "barz")); + EXPECT_EQ(6u, SCL->inSectionBlame("sect2", "fun", "def")); +} }