diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -417,6 +417,11 @@ /// coverage pass should actually not be instrumented. std::vector SanitizeCoverageIgnorelistFiles; + /// Path to ignorelist file specifying which objects + /// (files, functions) listed for instrumentation by sanitizer + /// binary metadata pass should not be instrumented. + std::vector SanitizeMetadataIgnorelistFiles; + /// Name of the stack usage file (i.e., .su file) if user passes /// -fstack-usage. If empty, it can be implied that -fstack-usage is not /// passed on the command line. diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -215,6 +215,8 @@ "malformed sanitizer coverage allowlist: '%0'">; def err_drv_malformed_sanitizer_coverage_ignorelist : Error< "malformed sanitizer coverage ignorelist: '%0'">; +def err_drv_malformed_sanitizer_metadata_ignorelist : Error< + "malformed sanitizer metadata ignorelist: '%0'">; def err_drv_unsupported_static_ubsan_darwin : Error< "static UndefinedBehaviorSanitizer runtime is not supported on darwin">; def err_drv_duplicate_config : Error< diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1756,6 +1756,10 @@ def fno_experimental_sanitize_metadata_EQ : CommaJoined<["-"], "fno-experimental-sanitize-metadata=">, Group, Flags<[CoreOption]>, HelpText<"Disable emitting metadata for binary analysis sanitizers">; +def fexperimental_sanitize_metadata_ignorelist_EQ : Joined<["-"], "fexperimental-sanitize-metadata-ignorelist=">, + Group, Flags<[CoreOption]>, + HelpText<"Disable sanitizer metadata for modules and functions that match the provided special case list">, + MarshallingInfoStringVector>; def fsanitize_memory_track_origins_EQ : Joined<["-"], "fsanitize-memory-track-origins=">, Group, HelpText<"Enable origins tracking in MemorySanitizer">, diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h --- a/clang/include/clang/Driver/SanitizerArgs.h +++ b/clang/include/clang/Driver/SanitizerArgs.h @@ -30,6 +30,7 @@ std::vector SystemIgnorelistFiles; std::vector CoverageAllowlistFiles; std::vector CoverageIgnorelistFiles; + std::vector BinaryMetadataIgnorelistFiles; int CoverageFeatures = 0; int BinaryMetadataFeatures = 0; int MsanTrackOrigins = 0; diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -665,7 +665,8 @@ if (CodeGenOpts.hasSanitizeBinaryMetadata()) { MPM.addPass(SanitizerBinaryMetadataPass( - getSanitizerBinaryMetadataOptions(CodeGenOpts))); + getSanitizerBinaryMetadataOptions(CodeGenOpts), + CodeGenOpts.SanitizeMetadataIgnorelistFiles)); } auto MSanPass = [&](SanitizerMask Mask, bool CompileKernel) { diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -864,6 +864,16 @@ } } + // Parse -fsanitize-metadata-ignorelist option if enabled. + if (BinaryMetadataFeatures) { + parseSpecialCaseListArg( + D, Args, BinaryMetadataIgnorelistFiles, + options::OPT_fexperimental_sanitize_metadata_ignorelist_EQ, + OptSpecifier(), // Cannot clear ignore list, only append. + clang::diag::err_drv_malformed_sanitizer_metadata_ignorelist, + DiagnoseErrors); + } + SharedRuntime = Args.hasFlag(options::OPT_shared_libsan, options::OPT_static_libsan, TC.getTriple().isAndroid() || TC.getTriple().isOSFuchsia() || @@ -1141,6 +1151,9 @@ CmdArgs.push_back( Args.MakeArgString("-fexperimental-sanitize-metadata=" + F.second)); } + addSpecialCaseListOpt(Args, CmdArgs, + "-fexperimental-sanitize-metadata-ignorelist=", + BinaryMetadataIgnorelistFiles); if (TC.getTriple().isOSWindows() && needsUbsanRt()) { // Instruct the code generator to embed linker directives in the object file diff --git a/clang/test/CodeGen/sanitize-metadata-ignorelist.c b/clang/test/CodeGen/sanitize-metadata-ignorelist.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/sanitize-metadata-ignorelist.c @@ -0,0 +1,55 @@ +// RUN: %clang -O -fexperimental-sanitize-metadata=all -target x86_64-gnu-linux -x c -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALLOW +// RUN: echo "fun:foo" > %t.fun +// RUN: %clang -O -fexperimental-sanitize-metadata=all -fexperimental-sanitize-metadata-ignorelist=%t.fun -target x86_64-gnu-linux -x c -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=FUN +// RUN: echo "src:%s" > %t.src +// RUN: %clang -O -fexperimental-sanitize-metadata=all -fexperimental-sanitize-metadata-ignorelist=%t.src -target x86_64-gnu-linux -x c -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=SRC + +int y; + +// ALLOW-LABEL: define {{[^@]+}}@foo +// ALLOW-SAME: () local_unnamed_addr #[[ATTR0:[0-9]+]] !pcsections !5 { +// ALLOW-NEXT: entry: +// ALLOW-NEXT: [[TMP0:%.*]] = atomicrmw add ptr @y, i32 1 monotonic, align 4, !pcsections !7 +// ALLOW-NEXT: ret void +// +// FUN-LABEL: define {{[^@]+}}@foo +// FUN-SAME: () local_unnamed_addr #[[ATTR0:[0-9]+]] { +// FUN-NEXT: entry: +// FUN-NEXT: [[TMP0:%.*]] = atomicrmw add ptr @y, i32 1 monotonic, align 4 +// FUN-NEXT: ret void +// +// SRC-LABEL: define {{[^@]+}}@foo +// SRC-SAME: () local_unnamed_addr #[[ATTR0:[0-9]+]] { +// SRC-NEXT: entry: +// SRC-NEXT: [[TMP0:%.*]] = atomicrmw add ptr @y, i32 1 monotonic, align 4 +// SRC-NEXT: ret void +// +void foo() { + __atomic_fetch_add(&y, 1, __ATOMIC_RELAXED); +} + +// ALLOW-LABEL: define {{[^@]+}}@bar +// ALLOW-SAME: () local_unnamed_addr #[[ATTR0]] !pcsections !5 { +// ALLOW-NEXT: entry: +// ALLOW-NEXT: [[TMP0:%.*]] = atomicrmw add ptr @y, i32 2 monotonic, align 4, !pcsections !7 +// ALLOW-NEXT: ret void +// +// FUN-LABEL: define {{[^@]+}}@bar +// FUN-SAME: () local_unnamed_addr #[[ATTR0]] !pcsections !5 { +// FUN-NEXT: entry: +// FUN-NEXT: [[TMP0:%.*]] = atomicrmw add ptr @y, i32 2 monotonic, align 4, !pcsections !7 +// FUN-NEXT: ret void +// +// SRC-LABEL: define {{[^@]+}}@bar +// SRC-SAME: () local_unnamed_addr #[[ATTR0]] { +// SRC-NEXT: entry: +// SRC-NEXT: [[TMP0:%.*]] = atomicrmw add ptr @y, i32 2 monotonic, align 4 +// SRC-NEXT: ret void +// +void bar() { + __atomic_fetch_add(&y, 2, __ATOMIC_RELAXED); +} + +// ALLOW: __sanitizer_metadata_covered.module_ctor +// FUN: __sanitizer_metadata_covered.module_ctor +// SRC-NOT: __sanitizer_metadata_covered.module_ctor diff --git a/clang/test/Driver/fsanitize-metadata-ignorelist.c b/clang/test/Driver/fsanitize-metadata-ignorelist.c new file mode 100644 --- /dev/null +++ b/clang/test/Driver/fsanitize-metadata-ignorelist.c @@ -0,0 +1,14 @@ +// Verify Driver passes on -fsanitize-metadata-ignorelist. + +// RUN: echo "fun:foo" > %t.1 +// RUN: echo "fun:bar" > %t.2 + +// RUN: %clang -target x86_64-linux-gnu -fexperimental-sanitize-metadata=all -fexperimental-sanitize-metadata-ignorelist=%t.1 -fexperimental-sanitize-metadata-ignorelist=%t.2 %s -### 2>&1 | FileCheck %s +// RUN: %clang -target aarch64-linux-gnu -fexperimental-sanitize-metadata=atomics -fexperimental-sanitize-metadata-ignorelist=%t.1 -fexperimental-sanitize-metadata-ignorelist=%t.2 %s -### 2>&1 | FileCheck %s +// CHECK: "-fexperimental-sanitize-metadata-ignorelist={{.*}}.1" "-fexperimental-sanitize-metadata-ignorelist={{.*}}.2" + +// Verify -fsanitize-metadata-ignorelist flag not passed if there is no -fsanitize-metadata flag. +// RUN: %clang -target x86_64-linux-gnu -fexperimental-sanitize-metadata-ignorelist=%t.1 -fexperimental-sanitize-metadata-ignorelist=%t.2 %s -### 2>&1 | FileCheck %s --check-prefix=NOSANMD +// RUN: %clang -target aarch64-linux-gnu -fexperimental-sanitize-metadata-ignorelist=%t.1 -fexperimental-sanitize-metadata-ignorelist=%t.2 %s -### 2>&1 | FileCheck %s --check-prefix=NOSANMD +// NOSANMD: warning: argument unused during compilation: '-fexperimental-sanitize-metadata-ignorelist +// NOSANMD-NOT: "-fexperimental-sanitize-metadata-ignorelist diff --git a/llvm/include/llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h b/llvm/include/llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h --- a/llvm/include/llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h +++ b/llvm/include/llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h @@ -12,6 +12,7 @@ #ifndef LLVM_TRANSFORMS_INSTRUMENTATION_SANITIZERBINARYMETADATA_H #define LLVM_TRANSFORMS_INSTRUMENTATION_SANITIZERBINARYMETADATA_H +#include "llvm/ADT/ArrayRef.h" #include "llvm/IR/Function.h" #include "llvm/IR/Module.h" #include "llvm/IR/PassManager.h" @@ -50,12 +51,14 @@ : public PassInfoMixin { public: explicit SanitizerBinaryMetadataPass( - SanitizerBinaryMetadataOptions Opts = {}); + SanitizerBinaryMetadataOptions Opts = {}, + ArrayRef IgnorelistFiles = {}); PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); static bool isRequired() { return true; } private: const SanitizerBinaryMetadataOptions Options; + const ArrayRef IgnorelistFiles; }; } // namespace llvm diff --git a/llvm/lib/Transforms/Instrumentation/SanitizerBinaryMetadata.cpp b/llvm/lib/Transforms/Instrumentation/SanitizerBinaryMetadata.cpp --- a/llvm/lib/Transforms/Instrumentation/SanitizerBinaryMetadata.cpp +++ b/llvm/lib/Transforms/Instrumentation/SanitizerBinaryMetadata.cpp @@ -38,13 +38,16 @@ #include "llvm/Support/Allocator.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/SpecialCaseList.h" #include "llvm/Support/StringSaver.h" +#include "llvm/Support/VirtualFileSystem.h" #include "llvm/TargetParser/Triple.h" #include "llvm/Transforms/Instrumentation.h" #include "llvm/Transforms/Utils/ModuleUtils.h" #include #include +#include using namespace llvm; @@ -121,9 +124,11 @@ class SanitizerBinaryMetadata { public: - SanitizerBinaryMetadata(Module &M, SanitizerBinaryMetadataOptions Opts) + SanitizerBinaryMetadata(Module &M, SanitizerBinaryMetadataOptions Opts, + std::unique_ptr Ignorelist) : Mod(M), Options(transformOptionsFromCl(std::move(Opts))), - TargetTriple(M.getTargetTriple()), IRB(M.getContext()) { + Ignorelist(std::move(Ignorelist)), TargetTriple(M.getTargetTriple()), + IRB(M.getContext()) { // FIXME: Make it work with other formats. assert(TargetTriple.isOSBinFormatELF() && "ELF only"); } @@ -168,6 +173,7 @@ Module &Mod; const SanitizerBinaryMetadataOptions Options; + std::unique_ptr Ignorelist; const Triple TargetTriple; IRBuilder<> IRB; BumpPtrAllocator Alloc; @@ -243,6 +249,8 @@ return; if (F.hasFnAttribute(Attribute::DisableSanitizerInstrumentation)) return; + if (Ignorelist && Ignorelist->inSection("metadata", "fun", F.getName())) + return; // Don't touch available_externally functions, their actual body is elsewhere. if (F.getLinkage() == GlobalValue::AvailableExternallyLinkage) return; @@ -455,12 +463,20 @@ } // namespace SanitizerBinaryMetadataPass::SanitizerBinaryMetadataPass( - SanitizerBinaryMetadataOptions Opts) - : Options(std::move(Opts)) {} + SanitizerBinaryMetadataOptions Opts, ArrayRef IgnorelistFiles) + : Options(std::move(Opts)), IgnorelistFiles(std::move(IgnorelistFiles)) {} PreservedAnalyses SanitizerBinaryMetadataPass::run(Module &M, AnalysisManager &AM) { - SanitizerBinaryMetadata Pass(M, Options); + std::unique_ptr Ignorelist; + if (!IgnorelistFiles.empty()) { + Ignorelist = SpecialCaseList::createOrDie(IgnorelistFiles, + *vfs::getRealFileSystem()); + if (Ignorelist->inSection("metadata", "src", M.getSourceFileName())) + return PreservedAnalyses::all(); + } + + SanitizerBinaryMetadata Pass(M, Options, std::move(Ignorelist)); if (Pass.run()) return PreservedAnalyses::none(); return PreservedAnalyses::all();