[clang] 5d64dd8 - [Clang][ASan] Introduce `-fsanitize-address-destructor-kind=` driver & frontend option.

Dan Liew via cfe-commits cfe-commits at lists.llvm.org
Thu Feb 25 12:03:56 PST 2021


Author: Dan Liew
Date: 2021-02-25T12:02:21-08:00
New Revision: 5d64dd8e3c22e12e4f7b3d08ffe88fc41e727117

URL: https://github.com/llvm/llvm-project/commit/5d64dd8e3c22e12e4f7b3d08ffe88fc41e727117
DIFF: https://github.com/llvm/llvm-project/commit/5d64dd8e3c22e12e4f7b3d08ffe88fc41e727117.diff

LOG: [Clang][ASan] Introduce `-fsanitize-address-destructor-kind=` driver & frontend option.

The new `-fsanitize-address-destructor-kind=` option allows control over how module
destructors are emitted by ASan.

The new option is consumed by both the driver and the frontend and is propagated into
codegen options by the frontend.

Both the legacy and new pass manager code have been updated to consume the new option
from the codegen options.

It would be nice if the new utility functions (`AsanDtorKindToString` and
`AsanDtorKindFromString`) could live in LLVM instead of Clang so they could be
consumed by other language frontends. Unfortunately that doesn't work because
the clang driver doesn't link against the LLVM instrumentation library.

rdar://71609176

Differential Revision: https://reviews.llvm.org/D96572

Added: 
    clang/test/CodeGen/asan-destructor-kind.cpp
    clang/test/Driver/fsanitize-address-destructor-kind.c

Modified: 
    clang/docs/ClangCommandLineReference.rst
    clang/include/clang/Basic/CodeGenOptions.def
    clang/include/clang/Basic/CodeGenOptions.h
    clang/include/clang/Basic/Sanitizers.h
    clang/include/clang/Driver/Options.td
    clang/include/clang/Driver/SanitizerArgs.h
    clang/lib/Basic/Sanitizers.cpp
    clang/lib/CodeGen/BackendUtil.cpp
    clang/lib/Driver/SanitizerArgs.cpp
    clang/lib/Frontend/CompilerInvocation.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ClangCommandLineReference.rst b/clang/docs/ClangCommandLineReference.rst
index f7ac97484d91..0038dccd53f9 100644
--- a/clang/docs/ClangCommandLineReference.rst
+++ b/clang/docs/ClangCommandLineReference.rst
@@ -870,6 +870,17 @@ Enable use-after-scope detection in AddressSanitizer
 
 Enable ODR indicator globals to avoid false ODR violation reports in partially sanitized programs at the cost of an increase in binary size
 
+.. option:: -fsanitize-address-destructor-kind=<arg>
+
+Set the kind of module destructors emitted by AddressSanitizer instrumentation.
+These destructors are emitted to unregister instrumented global variables when
+code is unloaded (e.g. via `dlclose()`).
+
+Valid options are:
+
+* ``global`` - Emit module destructors that are called via a platform specific array (see `llvm.global_dtors`).
+* ``none`` - Do not emit module destructors.
+
 .. option:: -fsanitize-blacklist=<arg>
 
 Path to blacklist file for sanitizers

diff  --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index 9d53b5b923bb..832f7fad3551 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -214,6 +214,9 @@ CODEGENOPT(SanitizeAddressGlobalsDeadStripping, 1, 0) ///< Enable linker dead st
 CODEGENOPT(SanitizeAddressUseOdrIndicator, 1, 0) ///< Enable ODR indicator globals
 CODEGENOPT(SanitizeMemoryTrackOrigins, 2, 0) ///< Enable tracking origins in
                                              ///< MemorySanitizer
+ENUM_CODEGENOPT(SanitizeAddressDtorKind, llvm::AsanDtorKind, 2,
+                llvm::AsanDtorKind::Global)  ///< Set how ASan global
+                                             ///< destructors are emitted.
 CODEGENOPT(SanitizeMemoryUseAfterDtor, 1, 0) ///< Enable use-after-delete detection
                                              ///< in MemorySanitizer
 CODEGENOPT(SanitizeCfiCrossDso, 1, 0) ///< Enable cross-dso support in CFI.

diff  --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 878d55402e32..fe83887ad19d 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -20,6 +20,7 @@
 #include "llvm/Support/CodeGen.h"
 #include "llvm/Support/Regex.h"
 #include "llvm/Target/TargetOptions.h"
+#include "llvm/Transforms/Instrumentation/AddressSanitizerOptions.h"
 #include <map>
 #include <memory>
 #include <string>

diff  --git a/clang/include/clang/Basic/Sanitizers.h b/clang/include/clang/Basic/Sanitizers.h
index 675fa15fdec8..1acce0a6e09e 100644
--- a/clang/include/clang/Basic/Sanitizers.h
+++ b/clang/include/clang/Basic/Sanitizers.h
@@ -17,6 +17,7 @@
 #include "clang/Basic/LLVM.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/MathExtras.h"
+#include "llvm/Transforms/Instrumentation/AddressSanitizerOptions.h"
 #include <cassert>
 #include <cstdint>
 
@@ -193,6 +194,10 @@ inline SanitizerMask getPPTransparentSanitizers() {
          SanitizerKind::Undefined | SanitizerKind::FloatDivideByZero;
 }
 
+StringRef AsanDtorKindToString(llvm::AsanDtorKind kind);
+
+llvm::AsanDtorKind AsanDtorKindFromString(StringRef kind);
+
 } // namespace clang
 
 #endif // LLVM_CLANG_BASIC_SANITIZERS_H

diff  --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 9fb353c36271..fbf27d14258e 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -1500,6 +1500,11 @@ defm sanitize_address_use_odr_indicator : BoolOption<"f", "sanitize-address-use-
             " reports in partially sanitized programs at the cost of an increase in binary size">,
   NegFlag<SetFalse, [], "Disable ODR indicator globals">>,
   Group<f_clang_Group>;
+def sanitize_address_destructor_kind_EQ : Joined<["-"], "fsanitize-address-destructor-kind=">,
+  MetaVarName<"<kind>">,
+  Flags<[CC1Option]>,
+  HelpText<"Set destructor type used in ASan instrumentation">,
+  Group<f_clang_Group>;
 // Note: This flag was introduced when it was necessary to distinguish between
 //       ABI for correct codegen.  This is no longer needed, but the flag is
 //       not removed since targeting either ABI will behave the same.

diff  --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h
index ac2b817be1dc..6ba5aac0b702 100644
--- a/clang/include/clang/Driver/SanitizerArgs.h
+++ b/clang/include/clang/Driver/SanitizerArgs.h
@@ -43,6 +43,7 @@ class SanitizerArgs {
   bool AsanUseOdrIndicator = false;
   bool AsanInvalidPointerCmp = false;
   bool AsanInvalidPointerSub = false;
+  llvm::AsanDtorKind AsanDtorKind = llvm::AsanDtorKind::Invalid;
   std::string HwasanAbi;
   bool LinkRuntimes = true;
   bool LinkCXXRuntimes = false;

diff  --git a/clang/lib/Basic/Sanitizers.cpp b/clang/lib/Basic/Sanitizers.cpp
index 19882cdae0f7..f4cd841b91dc 100644
--- a/clang/lib/Basic/Sanitizers.cpp
+++ b/clang/lib/Basic/Sanitizers.cpp
@@ -60,4 +60,23 @@ namespace clang {
 llvm::hash_code hash_value(const clang::SanitizerMask &Arg) {
   return Arg.hash_value();
 }
+
+StringRef AsanDtorKindToString(llvm::AsanDtorKind kind) {
+  switch (kind) {
+  case llvm::AsanDtorKind::None:
+    return "none";
+  case llvm::AsanDtorKind::Global:
+    return "global";
+  case llvm::AsanDtorKind::Invalid:
+    return "invalid";
+  }
+}
+
+llvm::AsanDtorKind AsanDtorKindFromString(StringRef kindStr) {
+  return llvm::StringSwitch<llvm::AsanDtorKind>(kindStr)
+      .Case("none", llvm::AsanDtorKind::None)
+      .Case("global", llvm::AsanDtorKind::Global)
+      .Default(llvm::AsanDtorKind::Invalid);
+}
+
 } // namespace clang

diff  --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 4edc0f629fee..ba9a13ed1ddf 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -287,10 +287,12 @@ static void addAddressSanitizerPasses(const PassManagerBuilder &Builder,
   bool UseAfterScope = CGOpts.SanitizeAddressUseAfterScope;
   bool UseOdrIndicator = CGOpts.SanitizeAddressUseOdrIndicator;
   bool UseGlobalsGC = asanUseGlobalsGC(T, CGOpts);
+  llvm::AsanDtorKind DestructorKind = CGOpts.getSanitizeAddressDtorKind();
   PM.add(createAddressSanitizerFunctionPass(/*CompileKernel*/ false, Recover,
                                             UseAfterScope));
   PM.add(createModuleAddressSanitizerLegacyPassPass(
-      /*CompileKernel*/ false, Recover, UseGlobalsGC, UseOdrIndicator));
+      /*CompileKernel*/ false, Recover, UseGlobalsGC, UseOdrIndicator,
+      DestructorKind));
 }
 
 static void addKernelAddressSanitizerPasses(const PassManagerBuilder &Builder,
@@ -1111,9 +1113,12 @@ static void addSanitizers(const Triple &TargetTriple,
         bool UseAfterScope = CodeGenOpts.SanitizeAddressUseAfterScope;
         bool ModuleUseAfterScope = asanUseGlobalsGC(TargetTriple, CodeGenOpts);
         bool UseOdrIndicator = CodeGenOpts.SanitizeAddressUseOdrIndicator;
+        llvm::AsanDtorKind DestructorKind =
+            CodeGenOpts.getSanitizeAddressDtorKind();
         MPM.addPass(RequireAnalysisPass<ASanGlobalsMetadataAnalysis, Module>());
         MPM.addPass(ModuleAddressSanitizerPass(
-            CompileKernel, Recover, ModuleUseAfterScope, UseOdrIndicator));
+            CompileKernel, Recover, ModuleUseAfterScope, UseOdrIndicator,
+            DestructorKind));
         MPM.addPass(createModuleToFunctionPassAdaptor(
             AddressSanitizerPass(CompileKernel, Recover, UseAfterScope)));
       }

diff  --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index d9c04f9d91f5..a3d5a2f567c5 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -825,6 +825,16 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
       AsanInvalidPointerSub = true;
     }
 
+    if (const auto *Arg =
+            Args.getLastArg(options::OPT_sanitize_address_destructor_kind_EQ)) {
+      auto parsedAsanDtorKind = AsanDtorKindFromString(Arg->getValue());
+      if (parsedAsanDtorKind == llvm::AsanDtorKind::Invalid) {
+        TC.getDriver().Diag(clang::diag::err_drv_unsupported_option_argument)
+            << Arg->getOption().getName() << Arg->getValue();
+      }
+      AsanDtorKind = parsedAsanDtorKind;
+    }
+
   } else {
     AsanUseAfterScope = false;
     // -fsanitize=pointer-compare/pointer-subtract requires -fsanitize=address.
@@ -1079,6 +1089,13 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
     CmdArgs.push_back("-asan-detect-invalid-pointer-sub");
   }
 
+  // Only pass the option to the frontend if the user requested,
+  // otherwise the frontend will just use the codegen default.
+  if (AsanDtorKind != llvm::AsanDtorKind::Invalid) {
+    CmdArgs.push_back(Args.MakeArgString("-fsanitize-address-destructor-kind=" +
+                                         AsanDtorKindToString(AsanDtorKind)));
+  }
+
   if (!HwasanAbi.empty()) {
     CmdArgs.push_back("-default-function-attr");
     CmdArgs.push_back(Args.MakeArgString("hwasan-abi=" + HwasanAbi));

diff  --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index eb447541124a..12dbd459d07e 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1937,6 +1937,19 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
 
   Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true);
 
+  if (LangOptsRef.Sanitize.has(SanitizerKind::Address)) {
+    if (Arg *A =
+            Args.getLastArg(options::OPT_sanitize_address_destructor_kind_EQ)) {
+      auto destructorKind = AsanDtorKindFromString(A->getValue());
+      if (destructorKind == llvm::AsanDtorKind::Invalid) {
+        Diags.Report(clang::diag::err_drv_unsupported_option_argument)
+            << A->getOption().getName() << A->getValue();
+      } else {
+        Opts.setSanitizeAddressDtorKind(destructorKind);
+      }
+    }
+  }
+
   if (Args.hasArg(options::OPT_ffinite_loops))
     Opts.FiniteLoops = CodeGenOptions::FiniteLoopsKind::Always;
   else if (Args.hasArg(options::OPT_fno_finite_loops))

diff  --git a/clang/test/CodeGen/asan-destructor-kind.cpp b/clang/test/CodeGen/asan-destructor-kind.cpp
new file mode 100644
index 000000000000..2bdb782db2b5
--- /dev/null
+++ b/clang/test/CodeGen/asan-destructor-kind.cpp
@@ -0,0 +1,49 @@
+// Frontend rejects invalid option
+// RUN: not %clang_cc1 -fsanitize=address \
+// RUN:   -fsanitize-address-destructor-kind=bad_arg -emit-llvm -o - \
+// RUN:   -triple x86_64-apple-macosx10.15 %s 2>&1 | \
+// RUN:   FileCheck %s --check-prefixes=CHECK-BAD-ARG
+// CHECK-BAD-ARG: unsupported argument 'bad_arg' to option 'fsanitize-address-destructor-kind='
+
+// Default is global dtor
+// RUN: %clang_cc1 -fsanitize=address -emit-llvm -o - -triple x86_64-apple-macosx10.15 \
+// RUN:   -fno-legacy-pass-manager %s \
+// RUN:   | FileCheck %s --check-prefixes=CHECK-GLOBAL-DTOR
+//
+// RUN: %clang_cc1 -fsanitize=address -emit-llvm -o - -triple x86_64-apple-macosx10.15 \
+// RUN:   -flegacy-pass-manager %s \
+// RUN:   | FileCheck %s --check-prefixes=CHECK-GLOBAL-DTOR
+
+// Explictly ask for global dtor
+// RUN: %clang_cc1 -fsanitize=address \
+// RUN:   -fsanitize-address-destructor-kind=global -emit-llvm -o - \
+// RUN:   -triple x86_64-apple-macosx10.15 -fno-legacy-pass-manager %s | \
+// RUN:   FileCheck %s --check-prefixes=CHECK-GLOBAL-DTOR
+//
+// RUN: %clang_cc1 -fsanitize=address \
+// RUN:   -fsanitize-address-destructor-kind=global -emit-llvm -o - \
+// RUN:   -triple x86_64-apple-macosx10.15 -flegacy-pass-manager %s | \
+// RUN:   FileCheck %s --check-prefixes=CHECK-GLOBAL-DTOR
+
+// CHECK-GLOBAL-DTOR: llvm.global_dtor{{.+}}asan.module_dtor
+// CHECK-GLOBAL-DTOR: define internal void @asan.module_dtor
+
+// Explictly ask for no dtors
+// RUN: %clang_cc1 -fsanitize=address \
+// RUN:   -fsanitize-address-destructor-kind=none -emit-llvm -o - \
+// RUN:   -triple x86_64-apple-macosx10.15 -fno-legacy-pass-manager %s | \
+// RUN:   FileCheck %s --check-prefixes=CHECK-NONE-DTOR
+//
+// RUN: %clang_cc1 -fsanitize=address \
+// RUN:   -fsanitize-address-destructor-kind=none -emit-llvm -o - \
+// RUN:   -triple x86_64-apple-macosx10.15 -flegacy-pass-manager %s | \
+// RUN:   FileCheck %s --check-prefixes=CHECK-NONE-DTOR
+
+int global;
+
+int main() {
+  return global;
+}
+
+// CHECK-NONE-DTOR-NOT: llvm.global_dtor{{.+}}asan.module_dtor
+// CHECK-NONE-DTOR-NOT: define internal void @asan.module_dtor

diff  --git a/clang/test/Driver/fsanitize-address-destructor-kind.c b/clang/test/Driver/fsanitize-address-destructor-kind.c
new file mode 100644
index 000000000000..b1f070bf1ad2
--- /dev/null
+++ b/clang/test/Driver/fsanitize-address-destructor-kind.c
@@ -0,0 +1,20 @@
+// Option should not be passed to the frontend by default.
+// RUN: %clang -target x86_64-apple-macosx10.15-gnu -fsanitize=address %s \
+// RUN:   -### 2>&1 | \
+// RUN:   FileCheck %s
+// CHECK-NOT: -fsanitize-address-destructor-kind
+
+// RUN: %clang -target x86_64-apple-macosx10.15-gnu -fsanitize=address \
+// RUN:   -fsanitize-address-destructor-kind=none %s -### 2>&1 | \
+// RUN:   FileCheck -check-prefix=CHECK-NONE-ARG %s
+// CHECK-NONE-ARG: "-fsanitize-address-destructor-kind=none"
+
+// RUN: %clang -target x86_64-apple-macosx10.15-gnu -fsanitize=address \
+// RUN:   -fsanitize-address-destructor-kind=global %s -### 2>&1 | \
+// RUN:   FileCheck -check-prefix=CHECK-GLOBAL-ARG %s
+// CHECK-GLOBAL-ARG: "-fsanitize-address-destructor-kind=global"
+
+// RUN: %clang -target x86_64-apple-macosx10.15-gnu -fsanitize=address \
+// RUN:   -fsanitize-address-destructor-kind=bad_arg %s -### 2>&1 | \
+// RUN:   FileCheck -check-prefix=CHECK-INVALID-ARG %s
+// CHECK-INVALID-ARG: error: unsupported argument 'bad_arg' to option 'fsanitize-address-destructor-kind='


        


More information about the cfe-commits mailing list