diff --git a/clang/docs/SanitizerSpecialCaseList.rst b/clang/docs/SanitizerSpecialCaseList.rst --- a/clang/docs/SanitizerSpecialCaseList.rst +++ b/clang/docs/SanitizerSpecialCaseList.rst @@ -75,6 +75,9 @@ # 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. + mainsrc:file.c # Turn off checks for a particular functions (use mangled names): fun:MyFooBar fun:_Z8MyFooBarv @@ -93,3 +96,18 @@ [cfi-vcall|cfi-icall] fun:*BadCfiCall # Entries without sections are placed into [*] and apply to all sanitizers + +``mainsrc`` 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 ``mainsrc`` pattern function may not be the prevailing copy picked by +the linker. Therefore, using ``mainsrc`` 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 an action at a distance risk.) + +``mainsrc`` 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 --- a/clang/include/clang/Basic/NoSanitizeList.h +++ b/clang/include/clang/Basic/NoSanitizeList.h @@ -41,6 +41,8 @@ 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 --- a/clang/lib/Basic/NoSanitizeList.cpp +++ b/clang/lib/Basic/NoSanitizeList.cpp @@ -47,6 +47,11 @@ return SSCL->inSection(Mask, "src", FileName, Category); } +bool NoSanitizeList::containsMainFile(SanitizerMask Mask, StringRef FileName, + StringRef Category) const { + return SSCL->inSection(Mask, "mainsrc", 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 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2780,16 +2780,18 @@ // NoSanitize by function name. if (NoSanitizeL.containsFunction(Kind, Fn->getName())) return true; - // NoSanitize by location. + // NoSanitize by location. Check "mainsrc" category. + auto &SM = Context.getSourceManager(); + const FileEntry &MainFile = *SM.getFileEntryForID(SM.getMainFileID()); + if (NoSanitizeL.containsMainFile(Kind, MainFile.getName())) + return true; + + // Check "src" category. 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 @@ 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-mainsrc.c b/clang/test/CodeGen/sanitize-ignorelist-mainsrc.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/sanitize-ignorelist-mainsrc.c @@ -0,0 +1,41 @@ +/// Test mainsrc 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 +mainsrc:*a.c + +//--- b.list +[address] +mainsrc:*a.c + +[alignment] +mainsrc:*.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 --- 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", "mainsrc", "fun" and "global". +// Wildcard expressions define, respectively, source files, main source 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 +// mainsrc:file_with_tricky_code.cc // // [dataflow] // # Functions with pure functional semantics: