[clang] c4842bb - [Clang] Introduce -fexperimental-sanitize-metadata=
Marco Elver via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 7 12:26:42 PDT 2022
Author: Marco Elver
Date: 2022-09-07T21:25:40+02:00
New Revision: c4842bb2e98e2f1ee23bb3bc753752816927b7b3
URL: https://github.com/llvm/llvm-project/commit/c4842bb2e98e2f1ee23bb3bc753752816927b7b3
DIFF: https://github.com/llvm/llvm-project/commit/c4842bb2e98e2f1ee23bb3bc753752816927b7b3.diff
LOG: [Clang] Introduce -fexperimental-sanitize-metadata=
Introduces the frontend flag -fexperimental-sanitize-metadata=, which
enables SanitizerBinaryMetadata instrumentation.
The first intended user of the binary metadata emitted will be a variant
of GWP-TSan [1]. The plan is to open source a stable and production
quality version of GWP-TSan. The development of which, however, requires
upstream compiler support.
[1] https://llvm.org/devmtg/2020-09/slides/Morehouse-GWP-Tsan.pdf
Until the tool has been open sourced, we mark this kind of
instrumentation as "experimental", and reserve the option to change
binary format, remove features, and similar.
Reviewed By: vitalybuka, MaskRay
Differential Revision: https://reviews.llvm.org/D130888
Added:
clang/test/CodeGen/sanitize-metadata.c
clang/test/Driver/fsanitize-metadata.c
Modified:
clang/include/clang/Basic/CodeGenOptions.def
clang/include/clang/Basic/CodeGenOptions.h
clang/include/clang/Driver/Options.td
clang/include/clang/Driver/SanitizerArgs.h
clang/lib/CodeGen/BackendUtil.cpp
clang/lib/Driver/SanitizerArgs.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index 55c6940fb34c1..ca2aa0d09aaf9 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -286,6 +286,8 @@ CODEGENOPT(SanitizeCoverageNoPrune, 1, 0) ///< Disable coverage pruning.
CODEGENOPT(SanitizeCoverageStackDepth, 1, 0) ///< Enable max stack depth tracing
CODEGENOPT(SanitizeCoverageTraceLoads, 1, 0) ///< Enable tracing of loads.
CODEGENOPT(SanitizeCoverageTraceStores, 1, 0) ///< Enable tracing of stores.
+CODEGENOPT(SanitizeBinaryMetadataCovered, 1, 0) ///< Emit PCs for covered functions.
+CODEGENOPT(SanitizeBinaryMetadataAtomics, 1, 0) ///< Emit PCs for atomic operations.
CODEGENOPT(SanitizeStats , 1, 0) ///< Collect statistics for sanitizers.
CODEGENOPT(SimplifyLibCalls , 1, 1) ///< Set when -fbuiltin is enabled.
CODEGENOPT(SoftFloat , 1, 0) ///< -soft-float.
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 7fffb38338889..0504012badb04 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -485,6 +485,11 @@ class CodeGenOptions : public CodeGenOptionsBase {
SanitizeCoverageTraceCmp || SanitizeCoverageTraceLoads ||
SanitizeCoverageTraceStores;
}
+
+ // Check if any one of SanitizeBinaryMetadata* is enabled.
+ bool hasSanitizeBinaryMetadata() const {
+ return SanitizeBinaryMetadataCovered || SanitizeBinaryMetadataAtomics;
+ }
};
} // end namespace clang
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index f9dfb8104395e..cfc0994d69d4e 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -1673,6 +1673,12 @@ def fsanitize_coverage_ignorelist : Joined<["-"], "fsanitize-coverage-ignorelist
HelpText<"Disable sanitizer coverage instrumentation for modules and functions "
"that match the provided special case list, even the allowed ones">,
MarshallingInfoStringVector<CodeGenOpts<"SanitizeCoverageIgnorelistFiles">>;
+def fexperimental_sanitize_metadata_EQ : CommaJoined<["-"], "fexperimental-sanitize-metadata=">,
+ Group<f_Group>,
+ HelpText<"Specify the type of metadata to emit for binary analysis sanitizers">;
+def fno_experimental_sanitize_metadata_EQ : CommaJoined<["-"], "fno-experimental-sanitize-metadata=">,
+ Group<f_Group>, Flags<[CoreOption]>,
+ HelpText<"Disable emitting metadata for binary analysis sanitizers">;
def fsanitize_memory_track_origins_EQ : Joined<["-"], "fsanitize-memory-track-origins=">,
Group<f_clang_Group>,
HelpText<"Enable origins tracking in MemorySanitizer">,
@@ -5497,6 +5503,14 @@ def fsanitize_coverage_trace_stores
: Flag<["-"], "fsanitize-coverage-trace-stores">,
HelpText<"Enable tracing of stores">,
MarshallingInfoFlag<CodeGenOpts<"SanitizeCoverageTraceStores">>;
+def fexperimental_sanitize_metadata_EQ_covered
+ : Flag<["-"], "fexperimental-sanitize-metadata=covered">,
+ HelpText<"Emit PCs for code covered with binary analysis sanitizers">,
+ MarshallingInfoFlag<CodeGenOpts<"SanitizeBinaryMetadataCovered">>;
+def fexperimental_sanitize_metadata_EQ_atomics
+ : Flag<["-"], "fexperimental-sanitize-metadata=atomics">,
+ HelpText<"Emit PCs for atomic operations used by binary analysis sanitizers">,
+ MarshallingInfoFlag<CodeGenOpts<"SanitizeBinaryMetadataAtomics">>;
def fpatchable_function_entry_offset_EQ
: Joined<["-"], "fpatchable-function-entry-offset=">, MetaVarName<"<M>">,
HelpText<"Generate M NOPs before function entry">,
diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h
index 1b29b1151224f..65677f79742bc 100644
--- a/clang/include/clang/Driver/SanitizerArgs.h
+++ b/clang/include/clang/Driver/SanitizerArgs.h
@@ -31,6 +31,7 @@ class SanitizerArgs {
std::vector<std::string> CoverageAllowlistFiles;
std::vector<std::string> CoverageIgnorelistFiles;
int CoverageFeatures = 0;
+ int BinaryMetadataFeatures = 0;
int MsanTrackOrigins = 0;
bool MsanUseAfterDtor = true;
bool MsanParamRetval = false;
diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 1bb734863c7df..ccde687eba09c 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -73,6 +73,7 @@
#include "llvm/Transforms/Instrumentation/MemProfiler.h"
#include "llvm/Transforms/Instrumentation/MemorySanitizer.h"
#include "llvm/Transforms/Instrumentation/SanitizerCoverage.h"
+#include "llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h"
#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
#include "llvm/Transforms/ObjCARC.h"
#include "llvm/Transforms/Scalar.h"
@@ -218,6 +219,14 @@ getSancovOptsFromCGOpts(const CodeGenOptions &CGOpts) {
return Opts;
}
+static SanitizerBinaryMetadataOptions
+getSanitizerBinaryMetadataOptions(const CodeGenOptions &CGOpts) {
+ SanitizerBinaryMetadataOptions Opts;
+ Opts.Covered = CGOpts.SanitizeBinaryMetadataCovered;
+ Opts.Atomics = CGOpts.SanitizeBinaryMetadataAtomics;
+ return Opts;
+}
+
// Check if ASan should use GC-friendly instrumentation for globals.
// First of all, there is no point if -fdata-sections is off (expect for MachO,
// where this is not a factor). Also, on ELF this feature requires an assembler
@@ -632,6 +641,11 @@ static void addSanitizers(const Triple &TargetTriple,
CodeGenOpts.SanitizeCoverageIgnorelistFiles));
}
+ if (CodeGenOpts.hasSanitizeBinaryMetadata()) {
+ MPM.addPass(SanitizerBinaryMetadataPass(
+ getSanitizerBinaryMetadataOptions(CodeGenOpts)));
+ }
+
auto MSanPass = [&](SanitizerMask Mask, bool CompileKernel) {
if (LangOpts.Sanitize.has(Mask)) {
int TrackOrigins = CodeGenOpts.SanitizeMemoryTrackOrigins;
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index b6ebc8ad1842b..6319e3c433ab1 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -100,6 +100,11 @@ enum CoverageFeature {
CoverageTraceStores = 1 << 17,
};
+enum BinaryMetadataFeature {
+ BinaryMetadataCovered = 1 << 0,
+ BinaryMetadataAtomics = 1 << 1,
+};
+
/// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any
/// invalid components. Returns a SanitizerMask.
static SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
@@ -110,6 +115,11 @@ static SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
static int parseCoverageFeatures(const Driver &D, const llvm::opt::Arg *A,
bool DiagnoseErrors);
+/// Parse -f(no-)?sanitize-metadata= flag values, diagnosing any invalid
+/// components. Returns OR of members of \c BinaryMetadataFeature enumeration.
+static int parseBinaryMetadataFeatures(const Driver &D, const llvm::opt::Arg *A,
+ bool DiagnoseErrors);
+
/// Produce an argument string from ArgList \p Args, which shows how it
/// provides some sanitizer kind from \p Mask. For example, the argument list
/// "-fsanitize=thread,vptr -fsanitize=address" with mask \c NeedsUbsanRt
@@ -834,6 +844,22 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
DiagnoseErrors);
}
+ // Parse -f(no-)?sanitize-metadata.
+ for (const auto *Arg :
+ Args.filtered(options::OPT_fexperimental_sanitize_metadata_EQ,
+ options::OPT_fno_experimental_sanitize_metadata_EQ)) {
+ if (Arg->getOption().matches(
+ options::OPT_fexperimental_sanitize_metadata_EQ)) {
+ Arg->claim();
+ BinaryMetadataFeatures |=
+ parseBinaryMetadataFeatures(D, Arg, DiagnoseErrors);
+ } else {
+ Arg->claim();
+ BinaryMetadataFeatures &=
+ ~parseBinaryMetadataFeatures(D, Arg, DiagnoseErrors);
+ }
+ }
+
SharedRuntime =
Args.hasFlag(options::OPT_shared_libsan, options::OPT_static_libsan,
TC.getTriple().isAndroid() || TC.getTriple().isOSFuchsia() ||
@@ -1095,6 +1121,17 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
addSpecialCaseListOpt(Args, CmdArgs, "-fsanitize-coverage-ignorelist=",
CoverageIgnorelistFiles);
+ // Translate available BinaryMetadataFeatures to corresponding clang-cc1
+ // flags. Does not depend on any other sanitizers.
+ const std::pair<int, std::string> BinaryMetadataFlags[] = {
+ std::make_pair(BinaryMetadataCovered, "covered"),
+ std::make_pair(BinaryMetadataAtomics, "atomics")};
+ for (const auto &F : BinaryMetadataFlags) {
+ if (BinaryMetadataFeatures & F.first)
+ CmdArgs.push_back(
+ Args.MakeArgString("-fexperimental-sanitize-metadata=" + F.second));
+ }
+
if (TC.getTriple().isOSWindows() && needsUbsanRt()) {
// Instruct the code generator to embed linker directives in the object file
// that cause the required runtime libraries to be linked.
@@ -1341,6 +1378,28 @@ int parseCoverageFeatures(const Driver &D, const llvm::opt::Arg *A,
return Features;
}
+int parseBinaryMetadataFeatures(const Driver &D, const llvm::opt::Arg *A,
+ bool DiagnoseErrors) {
+ assert(
+ A->getOption().matches(options::OPT_fexperimental_sanitize_metadata_EQ) ||
+ A->getOption().matches(
+ options::OPT_fno_experimental_sanitize_metadata_EQ));
+ int Features = 0;
+ for (int i = 0, n = A->getNumValues(); i != n; ++i) {
+ const char *Value = A->getValue(i);
+ int F = llvm::StringSwitch<int>(Value)
+ .Case("covered", BinaryMetadataCovered)
+ .Case("atomics", BinaryMetadataAtomics)
+ .Case("all", ~0)
+ .Default(0);
+ if (F == 0 && DiagnoseErrors)
+ D.Diag(clang::diag::err_drv_unsupported_option_argument)
+ << A->getOption().getName() << Value;
+ Features |= F;
+ }
+ return Features;
+}
+
std::string lastArgumentForMask(const Driver &D, const llvm::opt::ArgList &Args,
SanitizerMask Mask) {
for (llvm::opt::ArgList::const_reverse_iterator I = Args.rbegin(),
diff --git a/clang/test/CodeGen/sanitize-metadata.c b/clang/test/CodeGen/sanitize-metadata.c
new file mode 100644
index 0000000000000..58d47ff84f517
--- /dev/null
+++ b/clang/test/CodeGen/sanitize-metadata.c
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -O -fexperimental-sanitize-metadata=atomics -triple x86_64-gnu-linux -x c -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,ATOMICS
+// RUN: %clang_cc1 -O -fexperimental-sanitize-metadata=atomics -triple aarch64-gnu-linux -x c -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,ATOMICS
+
+int x, y;
+
+void empty() {
+// CHECK-NOT: define dso_local void @empty() {{.*}} !pcsections
+}
+
+int atomics() {
+// ATOMICS-LABEL: define dso_local i32 @atomics()
+// ATOMICS-SAME: !pcsections ![[ATOMICS_COVERED:[0-9]+]]
+// ATOMICS-NEXT: entry:
+// ATOMICS-NEXT: atomicrmw add {{.*}} !pcsections ![[ATOMIC_OP:[0-9]+]]
+// ATOMICS-NOT: load {{.*}} !pcsections
+ __atomic_fetch_add(&x, 1, __ATOMIC_RELAXED);
+ return y;
+}
+// ATOMICS-LABEL: __sanitizer_metadata_atomics.module_ctor
+// ATOMICS: call void @__sanitizer_metadata_atomics_add(i32 1, ptr @__start_sanmd_atomics, ptr @__stop_sanmd_atomics)
+// ATOMICS-LABEL: __sanitizer_metadata_atomics.module_dtor
+// ATOMICS: call void @__sanitizer_metadata_atomics_del(i32 1, ptr @__start_sanmd_atomics, ptr @__stop_sanmd_atomics)
+
+// CHECK-LABEL: __sanitizer_metadata_covered.module_ctor
+// CHECK: call void @__sanitizer_metadata_covered_add(i32 1, ptr @__start_sanmd_covered, ptr @__stop_sanmd_covered)
+// CHECK-LABEL: __sanitizer_metadata_covered.module_dtor
+// CHECK: call void @__sanitizer_metadata_covered_del(i32 1, ptr @__start_sanmd_covered, ptr @__stop_sanmd_covered)
+
+// ATOMICS: ![[ATOMICS_COVERED]] = !{!"sanmd_covered", ![[ATOMICS_COVERED_AUX:[0-9]+]]}
+// ATOMICS: ![[ATOMICS_COVERED_AUX]] = !{i32 1}
+// ATOMICS: ![[ATOMIC_OP]] = !{!"sanmd_atomics"}
diff --git a/clang/test/Driver/fsanitize-metadata.c b/clang/test/Driver/fsanitize-metadata.c
new file mode 100644
index 0000000000000..ce572fe73ea38
--- /dev/null
+++ b/clang/test/Driver/fsanitize-metadata.c
@@ -0,0 +1,23 @@
+// RUN: %clang --target=x86_64-linux-gnu -fexperimental-sanitize-metadata=all -fno-experimental-sanitize-metadata=all %s -### 2>&1 | FileCheck %s
+// CHECK-NOT: -fexperimental-sanitize-metadata
+
+// RUN: %clang --target=x86_64-linux-gnu -fexperimental-sanitize-metadata=bad_arg %s -### 2>&1 | FileCheck -check-prefix=CHECK-INVALID %s
+// CHECK-INVALID: error: unsupported argument 'bad_arg' to option '-fexperimental-sanitize-metadata='
+
+// RUN: %clang --target=x86_64-linux-gnu -fexperimental-sanitize-metadata=covered %s -### 2>&1 | FileCheck -check-prefix=CHECK-COVERED %s
+// RUN: %clang --target=x86_64-linux-gnu -fexperimental-sanitize-metadata=atomics -fno-experimental-sanitize-metadata=atomics -fexperimental-sanitize-metadata=covered %s -### 2>&1 | FileCheck -check-prefix=CHECK-COVERED %s
+// RUN: %clang --target=x86_64-linux-gnu -fexperimental-sanitize-metadata=all -fno-experimental-sanitize-metadata=atomics %s -### 2>&1 | FileCheck -check-prefix=CHECK-COVERED %s
+// CHECK-COVERED: "-fexperimental-sanitize-metadata=covered"
+// CHECK-COVERED-NOT: "-fexperimental-sanitize-metadata=atomics"
+
+// RUN: %clang --target=x86_64-linux-gnu -fexperimental-sanitize-metadata=atomics %s -### 2>&1 | FileCheck -check-prefix=CHECK-ATOMICS %s
+// RUN: %clang --target=x86_64-linux-gnu -fexperimental-sanitize-metadata=covered -fno-experimental-sanitize-metadata=covered -fexperimental-sanitize-metadata=atomics %s -### 2>&1 | FileCheck -check-prefix=CHECK-ATOMICS %s
+// RUN: %clang --target=x86_64-linux-gnu -fexperimental-sanitize-metadata=all -fno-experimental-sanitize-metadata=covered %s -### 2>&1 | FileCheck -check-prefix=CHECK-ATOMICS %s
+// CHECK-ATOMICS: "-fexperimental-sanitize-metadata=atomics"
+// CHECK-ATOMICS-NOT: "-fexperimental-sanitize-metadata=covered"
+
+// RUN: %clang --target=x86_64-linux-gnu -fexperimental-sanitize-metadata=covered,atomics %s -### 2>&1 | FileCheck -check-prefix=CHECK-ALL %s
+// RUN: %clang --target=x86_64-linux-gnu -fexperimental-sanitize-metadata=covered -fexperimental-sanitize-metadata=atomics %s -### 2>&1 | FileCheck -check-prefix=CHECK-ALL %s
+// RUN: %clang --target=x86_64-linux-gnu -fexperimental-sanitize-metadata=all %s -### 2>&1 | FileCheck -check-prefix=CHECK-ALL %s
+// CHECK-ALL: "-fexperimental-sanitize-metadata=covered"
+// CHECK-ALL: "-fexperimental-sanitize-metadata=atomics"
More information about the cfe-commits
mailing list