diff --git a/clang/docs/SanitizerSpecialCaseList.rst b/clang/docs/SanitizerSpecialCaseList.rst index cbb00fde9bb748..15e19b9c129ca4 100644 --- a/clang/docs/SanitizerSpecialCaseList.rst +++ b/clang/docs/SanitizerSpecialCaseList.rst @@ -75,6 +75,9 @@ tool-specific docs. # Turn off checks for the source file (use absolute path or path relative # to the current working directory): src:/path/to/source/file.c + # Turn off checks for this main file, including files included by it. + # Useful when the main file instead of an included file should be ignored. + mainfile:file.c # Turn off checks for a particular functions (use mangled names): fun:MyFooBar fun:_Z8MyFooBarv @@ -93,3 +96,18 @@ tool-specific docs. [cfi-vcall|cfi-icall] fun:*BadCfiCall # Entries without sections are placed into [*] and apply to all sanitizers + +``mainfile`` is similar to applying ``-fno-sanitize=`` to a set of files but +does not need plumbing into the build system. This works well for internal +linkage functions but has a caveat for C++ vague linkage functions. + +C++ vague linkage functions (e.g. inline functions, template instantiations) are +deduplicated at link time. A function (in an included file) ignored by a +specific ``mainfile`` pattern may not be the prevailing copy picked by the +linker. Therefore, using ``mainfile`` requires caution. It may still be useful, +e.g. when patterns are picked in a way to ensure the prevailing one is ignored. +(There is action-at-a-distance risk.) + +``mainfile`` can be useful enabling a ubsan check for a large code base when +finding the direct stack frame triggering the failure for every failure is +difficult. diff --git a/clang/include/clang/Basic/NoSanitizeList.h b/clang/include/clang/Basic/NoSanitizeList.h index 3f80e0fdedda12..43415859fcd54c 100644 --- a/clang/include/clang/Basic/NoSanitizeList.h +++ b/clang/include/clang/Basic/NoSanitizeList.h @@ -41,6 +41,8 @@ class NoSanitizeList { bool containsFunction(SanitizerMask Mask, StringRef FunctionName) const; bool containsFile(SanitizerMask Mask, StringRef FileName, StringRef Category = StringRef()) const; + bool containsMainFile(SanitizerMask Mask, StringRef FileName, + StringRef Category = StringRef()) const; bool containsLocation(SanitizerMask Mask, SourceLocation Loc, StringRef Category = StringRef()) const; }; diff --git a/clang/lib/Basic/NoSanitizeList.cpp b/clang/lib/Basic/NoSanitizeList.cpp index 3efd613b0d33ce..e7e63c1f419e61 100644 --- a/clang/lib/Basic/NoSanitizeList.cpp +++ b/clang/lib/Basic/NoSanitizeList.cpp @@ -47,6 +47,11 @@ bool NoSanitizeList::containsFile(SanitizerMask Mask, StringRef FileName, return SSCL->inSection(Mask, "src", FileName, Category); } +bool NoSanitizeList::containsMainFile(SanitizerMask Mask, StringRef FileName, + StringRef Category) const { + return SSCL->inSection(Mask, "mainfile", FileName, Category); +} + bool NoSanitizeList::containsLocation(SanitizerMask Mask, SourceLocation Loc, StringRef Category) const { return Loc.isValid() && diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 1e90b091453850..4daf4120e0cae7 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2780,16 +2780,18 @@ bool CodeGenModule::isInNoSanitizeList(SanitizerMask Kind, llvm::Function *Fn, // NoSanitize by function name. if (NoSanitizeL.containsFunction(Kind, Fn->getName())) return true; - // NoSanitize by location. + // NoSanitize by location. Check "mainfile" prefix. + auto &SM = Context.getSourceManager(); + const FileEntry &MainFile = *SM.getFileEntryForID(SM.getMainFileID()); + if (NoSanitizeL.containsMainFile(Kind, MainFile.getName())) + return true; + + // Check "src" prefix. if (Loc.isValid()) return NoSanitizeL.containsLocation(Kind, Loc); // If location is unknown, this may be a compiler-generated function. Assume // it's located in the main file. - auto &SM = Context.getSourceManager(); - if (const auto *MainFile = SM.getFileEntryForID(SM.getMainFileID())) { - return NoSanitizeL.containsFile(Kind, MainFile->getName()); - } - return false; + return NoSanitizeL.containsFile(Kind, MainFile.getName()); } bool CodeGenModule::isInNoSanitizeList(SanitizerMask Kind, @@ -2799,8 +2801,13 @@ bool CodeGenModule::isInNoSanitizeList(SanitizerMask Kind, const auto &NoSanitizeL = getContext().getNoSanitizeList(); if (NoSanitizeL.containsGlobal(Kind, GV->getName(), Category)) return true; + auto &SM = Context.getSourceManager(); + if (NoSanitizeL.containsMainFile( + Kind, SM.getFileEntryForID(SM.getMainFileID())->getName(), Category)) + return true; if (NoSanitizeL.containsLocation(Kind, Loc, Category)) return true; + // Check global type. if (!Ty.isNull()) { // Drill down the array types: if global variable of a fixed type is diff --git a/clang/test/CodeGen/sanitize-ignorelist-mainfile.c b/clang/test/CodeGen/sanitize-ignorelist-mainfile.c new file mode 100644 index 00000000000000..84d58b4c018e3a --- /dev/null +++ b/clang/test/CodeGen/sanitize-ignorelist-mainfile.c @@ -0,0 +1,41 @@ +/// Test mainfile in a sanitizer special case list. +// RUN: rm -rf %t && split-file %s %t && cd %t +// RUN: %clang_cc1 -emit-llvm -triple x86_64 -fsanitize=address,alignment a.c -o - | FileCheck %s --check-prefixes=CHECK,DEFAULT +// RUN: %clang_cc1 -emit-llvm -triple x86_64 -fsanitize=address,alignment -fsanitize-ignorelist=a.list a.c -o - | FileCheck %s --check-prefixes=CHECK,IGNORE +// RUN: %clang_cc1 -emit-llvm -triple x86_64 -fsanitize=address,alignment -fsanitize-ignorelist=b.list a.c -o - | FileCheck %s --check-prefixes=CHECK,IGNORE + +//--- a.list +mainfile:*a.c + +//--- b.list +[address] +mainfile:*a.c + +[alignment] +mainfile:*.c + +//--- a.h +int global_h; + +static inline int load(int *x) { + return *x; +} + +//--- a.c +#include "a.h" + +int global_c; + +int foo(void *x) { + return load(x); +} + +// DEFAULT: @___asan_gen_{{.*}} = {{.*}} c"global_h\00" +// DEFAULT: @___asan_gen_{{.*}} = {{.*}} c"global_c\00" +// IGNORE-NOT: @___asan_gen_ + +// CHECK-LABEL: define {{.*}}@load( +// DEFAULT: call void @__ubsan_handle_type_mismatch_v1_abort( +// DEFAULT: call void @__asan_report_load4( +// IGNORE-NOT: call void @__ubsan_handle_type_mismatch_v1_abort( +// IGNORE-NOT: call void @__asan_report_load4( diff --git a/llvm/include/llvm/Support/SpecialCaseList.h b/llvm/include/llvm/Support/SpecialCaseList.h index d022a8f53706ac..0d56c4b9912dbd 100644 --- a/llvm/include/llvm/Support/SpecialCaseList.h +++ b/llvm/include/llvm/Support/SpecialCaseList.h @@ -19,9 +19,9 @@ // prefix:wildcard_expression[=category] // If category is not specified, it is assumed to be empty string. // Definitions of "prefix" and "category" are sanitizer-specific. For example, -// sanitizer exclusion support prefixes "src", "fun" and "global". -// Wildcard expressions define, respectively, source files, functions or -// globals which shouldn't be instrumented. +// sanitizer exclusion support prefixes "src", "mainfile", "fun" and "global". +// Wildcard expressions define, respectively, source files, main files, +// functions or globals which shouldn't be instrumented. // Examples of categories: // "functional": used in DFSan to list functions with pure functional // semantics. @@ -37,6 +37,7 @@ // type:*Namespace::ClassName*=init // src:file_with_tricky_code.cc // src:ignore-global-initializers-issues.cc=init +// mainfile:main_file.cc // // [dataflow] // # Functions with pure functional semantics: