[llvm] [clang] [ASan][Driver] Add sanitize-target flag to support enabling ASan in device or host compilation (PR #76127)

via cfe-commits cfe-commits at lists.llvm.org
Wed Dec 20 23:23:57 PST 2023


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: None (jinge90)

<details>
<summary>Changes</summary>

In offloading scenario, the whole compiling process consists of device and host compilation phase, device and host code will be bundled together. Address sanitizer can be enabled in either one or both compilation phases via fsanitize-target=host|device|both. The meaning of each value is:
-fsanitize-target=host       AsanPass only enabled in host code
-fsanitize-target=device     AsanPass only enabled in device code
-fsanitize-target=both       AsanPass enabled in both host and device code
The default value is 'both'.

---
Full diff: https://github.com/llvm/llvm-project/pull/76127.diff


9 Files Affected:

- (modified) clang/include/clang/Basic/CodeGenOptions.def (+4) 
- (modified) clang/include/clang/Basic/Sanitizers.h (+4) 
- (modified) clang/include/clang/Driver/Options.td (+12) 
- (modified) clang/include/clang/Driver/SanitizerArgs.h (+6-1) 
- (modified) clang/lib/Basic/Sanitizers.cpp (+23) 
- (modified) clang/lib/CodeGen/BackendUtil.cpp (+18-1) 
- (modified) clang/lib/Driver/SanitizerArgs.cpp (+34) 
- (added) clang/test/Driver/sycl-sanitize-target.cpp (+29) 
- (modified) llvm/include/llvm/Transforms/Instrumentation/AddressSanitizerOptions.h (+8) 


``````````diff
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index 0acb5ae134ea24..af44b98e9d046b 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -224,6 +224,10 @@ ENUM_CODEGENOPT(SanitizeAddressUseAfterReturn,
                 llvm::AsanDetectStackUseAfterReturnMode, 2,
                 llvm::AsanDetectStackUseAfterReturnMode::Runtime
                 ) ///< Set detection mode for stack-use-after-return.
+ENUM_CODEGENOPT(
+    SanitizeTargetsToEnable, llvm::AsanTargetsToEnable, 2,
+    llvm::AsanTargetsToEnable::Both) ///< Set targets to enable sanitizer
+                                     /// passes in offloading scenario.
 CODEGENOPT(SanitizeAddressPoisonCustomArrayCookie, 1,
            0) ///< Enable poisoning operator new[] which is not a replaceable
               ///< global allocation function in AddressSanitizer
diff --git a/clang/include/clang/Basic/Sanitizers.h b/clang/include/clang/Basic/Sanitizers.h
index c890242269b334..e3fe35ed285dc3 100644
--- a/clang/include/clang/Basic/Sanitizers.h
+++ b/clang/include/clang/Basic/Sanitizers.h
@@ -211,6 +211,10 @@ StringRef AsanDetectStackUseAfterReturnModeToString(
 llvm::AsanDetectStackUseAfterReturnMode
 AsanDetectStackUseAfterReturnModeFromString(StringRef modeStr);
 
+llvm::AsanTargetsToEnable
+AsanTargetsToEnableFromString(StringRef asanTargetsStr);
+
+StringRef AsanTargetsToEnableToString(llvm::AsanTargetsToEnable target);
 } // 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 1b02087425b751..b9a3b3b05b48af 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2462,6 +2462,18 @@ def fsanitize_undefined_strip_path_components_EQ : Joined<["-"], "fsanitize-unde
            "when emitting check metadata.">,
   MarshallingInfoInt<CodeGenOpts<"EmitCheckPathComponentsToStrip">, "0", "int">;
 
+def sanitize_targets_EQ
+      : Joined<["-"], "fsanitize-target=">,
+        MetaVarName<"<target>">,
+        Visibility<[ClangOption, CC1Option]>,
+        HelpText<"Select target to enable sanitizer in offloading scenario, "
+                 "valid targets are host, device, both(both host and device "
+                 "enabled).">,
+        Group<f_clang_Group>,
+        Values<"host,device,both">,
+        NormalizedValuesScope<"llvm::AsanTargetsToEnable">,
+        NormalizedValues<["Host", "Device", "Both"]>,
+        MarshallingInfoEnum<CodeGenOpts<"SanitizeTargetsToEnable">, "Both">;
 } // end -f[no-]sanitize* flags
 
 def funsafe_math_optimizations : Flag<["-"], "funsafe-math-optimizations">,
diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h
index 07070ec4fc0653..aaa896a02eb142 100644
--- a/clang/include/clang/Driver/SanitizerArgs.h
+++ b/clang/include/clang/Driver/SanitizerArgs.h
@@ -67,6 +67,8 @@ class SanitizerArgs {
   bool HwasanUseAliases = false;
   llvm::AsanDetectStackUseAfterReturnMode AsanUseAfterReturn =
       llvm::AsanDetectStackUseAfterReturnMode::Invalid;
+  llvm::AsanTargetsToEnable AsanTargetsToEnable =
+      llvm::AsanTargetsToEnable::Both;
 
   std::string MemtagMode;
 
@@ -79,7 +81,10 @@ class SanitizerArgs {
   bool needsStableAbi() const { return StableABI; }
 
   bool needsMemProfRt() const { return NeedsMemProfRt; }
-  bool needsAsanRt() const { return Sanitizers.has(SanitizerKind::Address); }
+  bool needsAsanRt() const {
+    bool AsanIsNotDeviceOnly =
+        !(AsanTargetsToEnable == llvm::AsanTargetsToEnable::Device);
+    return Sanitizers.has(SanitizerKind::Address) && AsanIsNotDeviceOnly; }
   bool needsHwasanRt() const {
     return Sanitizers.has(SanitizerKind::HWAddress);
   }
diff --git a/clang/lib/Basic/Sanitizers.cpp b/clang/lib/Basic/Sanitizers.cpp
index 62ccdf8e9bbf28..05eda31c3f7ffd 100644
--- a/clang/lib/Basic/Sanitizers.cpp
+++ b/clang/lib/Basic/Sanitizers.cpp
@@ -112,4 +112,27 @@ AsanDetectStackUseAfterReturnModeFromString(StringRef modeStr) {
       .Default(llvm::AsanDetectStackUseAfterReturnMode::Invalid);
 }
 
+llvm::AsanTargetsToEnable
+AsanTargetsToEnableFromString(StringRef asanTargetsStr) {
+  return llvm::StringSwitch<llvm::AsanTargetsToEnable>(asanTargetsStr)
+      .Case("host", llvm::AsanTargetsToEnable::Host)
+      .Case("device", llvm::AsanTargetsToEnable::Device)
+      .Case("both", llvm::AsanTargetsToEnable::Both)
+      .Default(llvm::AsanTargetsToEnable::Invalid);
+}
+
+StringRef AsanTargetsToEnableToString(llvm::AsanTargetsToEnable target) {
+  switch (target) {
+  case llvm::AsanTargetsToEnable::Host:
+    return "host";
+  case llvm::AsanTargetsToEnable::Device:
+    return "device";
+  case llvm::AsanTargetsToEnable::Both:
+    return "both";
+  case llvm::AsanTargetsToEnable::Invalid:
+    return "invalid";
+  }
+  return "invalid";
+}
+
 } // namespace clang
diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 480410db1021b7..f4006270a13229 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -700,7 +700,24 @@ static void addSanitizers(const Triple &TargetTriple,
                                          DestructorKind));
       }
     };
-    ASanPass(SanitizerKind::Address, false);
+    // AddressSanitizer can be enabled in offloading scenario to detect bugs in
+    // both host and device code.  Users may use '-fsanitizer-targets' flag to
+    // enable this only on the host or only on the device.  The default behavior
+    // is to enable AddressSanitizer in both host and device compilation.
+    // Currently, we support this in SYCL compiler.
+    bool IgnoreAsanPass = false;
+    if (LangOpts.isSYCL()) {
+      llvm::AsanTargetsToEnable AsanTarget =
+          CodeGenOpts.getSanitizeTargetsToEnable();
+      if ((AsanTarget == llvm::AsanTargetsToEnable::Device) &&
+          LangOpts.SYCLIsHost)
+        IgnoreAsanPass = true;
+      if ((AsanTarget == llvm::AsanTargetsToEnable::Host) &&
+          LangOpts.SYCLIsDevice)
+        IgnoreAsanPass = true;
+    }
+    if (!IgnoreAsanPass)
+      ASanPass(SanitizerKind::Address, false);
     ASanPass(SanitizerKind::KernelAddress, true);
 
     auto HWASanPass = [&](SanitizerMask Mask, bool CompileKernel) {
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index ad68c086b71790..8805e4c915cd6e 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -1010,6 +1010,17 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
       AsanUseAfterReturn = parsedAsanUseAfterReturn;
     }
 
+    if (const auto *Arg = Args.getLastArg(options::OPT_sanitize_targets_EQ)) {
+      auto parsedAsanTargetsToEnable =
+          AsanTargetsToEnableFromString(Arg->getValue());
+      if (parsedAsanTargetsToEnable == llvm::AsanTargetsToEnable::Invalid &&
+          DiagnoseErrors) {
+        TC.getDriver().Diag(clang::diag::err_drv_unsupported_option_argument)
+            << Arg->getSpelling() << Arg->getValue();
+      }
+      AsanTargetsToEnable = parsedAsanTargetsToEnable;
+    }
+
   } else {
     AsanUseAfterScope = false;
     // -fsanitize=pointer-compare/pointer-subtract requires -fsanitize=address.
@@ -1138,6 +1149,29 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
     GPUSanitize = true;
   }
 
+  // In offloading scenario, the whole compiling process consists of device and
+  // host compilation phase, device and host code will be bundled together.
+  // Address sanitizer can be enabled in either one or both compilation phases
+  // via fsanitize-target=host|device|both. This flag must be passed to both
+  // device and host compilation to indicate whether to enable ASanPass in
+  // current phase. In SYCL, when SYCL compiler's target triple is SPIR, we are
+  // in device compilation phase, if target triple is not SPIR and '-fsycl' is
+  // used, we are in host compilation phase.
+  if (TC.getTriple().isSPIR()) {
+    if (Sanitizers.has(SanitizerKind::Address)) {
+      CmdArgs.push_back(
+          Args.MakeArgString("-fsanitize-target=" +
+                             AsanTargetsToEnableToString(AsanTargetsToEnable)));
+    }
+  } else {
+    if (Args.hasFlag(options::OPT_fsycl, options::OPT_fno_sycl, false) &&
+        Sanitizers.has(SanitizerKind::Address)) {
+      CmdArgs.push_back(
+          Args.MakeArgString("-fsanitize-target=" +
+                             AsanTargetsToEnableToString(AsanTargetsToEnable)));
+    }
+  }
+
   // Translate available CoverageFeatures to corresponding clang-cc1 flags.
   // Do it even if Sanitizers.empty() since some forms of coverage don't require
   // sanitizers.
diff --git a/clang/test/Driver/sycl-sanitize-target.cpp b/clang/test/Driver/sycl-sanitize-target.cpp
new file mode 100644
index 00000000000000..c927d3b4e0f1a8
--- /dev/null
+++ b/clang/test/Driver/sycl-sanitize-target.cpp
@@ -0,0 +1,29 @@
+// UNSUPPORTED: system-windows
+
+// Check whether ASan runtime libraries are linked or not when
+// fsanitize-target=host|device|both. The ASan runtime libraries
+// are NOT needed for device compilation in offloading scenario.
+
+// RUN: %clangxx -fsycl -fsanitize=address %s -### 2>&1 \
+// RUN:   | FileCheck --check-prefix=SYCL-ASAN-DEFAULT %s
+// SYCL-ASAN-DEFAULT:  "{{.*}}/ld"
+// SYCL-ASAN-DEFAULT-SAME: "{{.*}}/libclang_rt.asan{{.*}}.a"
+
+// RUN: %clangxx -fsycl -fsanitize=address -fsanitize-target=host %s -### 2>&1 \
+// RUN:   | FileCheck --check-prefix=SYCL-ASAN-HOST %s
+// SYCL-ASAN-HOST:  "{{.*}}/ld"
+// SYCL-ASAN-HOST-SAME: "{{.*}}/libclang_rt.asan{{.*}}.a"
+
+// RUN: %clangxx -fsycl -fsanitize=address -fsanitize-target=both %s -### 2>&1 \
+// RUN:   | FileCheck --check-prefix=SYCL-ASAN-BOTH %s
+// SYCL-ASAN-BOTH:  "{{.*}}/ld"
+// SYCL-ASAN-BOTH-SAME: "{{.*}}/libclang_rt.asan{{.*}}.a"
+
+// RUN: %clangxx -fsycl -fsanitize=address -fsanitize-target=device %s -### 2>&1 \
+// RUN:   | FileCheck --check-prefix=SYCL-ASAN-DEVICE %s
+// SYCL-ASAN-DEVICE:  "{{.*}}/ld"
+// SYCL-ASAN-DEVICE-NOT: "{{.*}}/libclang_rt.asan{{.*}}.a"
+
+// RUN: not %clangxx -fsycl -fsanitize=address -fsanitize-target=YYY %s -### 2>&1 \
+// RUN:   | FileCheck --check-prefix=SYCL-ASAN-INVALID %s
+// SYCL-ASAN-INVALID: error: unsupported argument 'YYY'
diff --git a/llvm/include/llvm/Transforms/Instrumentation/AddressSanitizerOptions.h b/llvm/include/llvm/Transforms/Instrumentation/AddressSanitizerOptions.h
index d697b72cde05e9..80984c2bd3e6a3 100644
--- a/llvm/include/llvm/Transforms/Instrumentation/AddressSanitizerOptions.h
+++ b/llvm/include/llvm/Transforms/Instrumentation/AddressSanitizerOptions.h
@@ -34,6 +34,14 @@ enum class AsanDetectStackUseAfterReturnMode {
   Invalid, ///< Not a valid detect mode.
 };
 
+/// Targets to enable address sanitizer pass in offloading scenario.
+enum class AsanTargetsToEnable {
+  Host,   ///< Only enable AddressSanitizerPass for host compilation.
+  Device, ///< Only enable AddressSanitizerPass for device compilation.
+  Both,   ///< Enable AddressSanitizerPass for both host and device compilation.
+  Invalid ///< Not a valid detect mode.
+};
+
 } // namespace llvm
 
 #endif

``````````

</details>


https://github.com/llvm/llvm-project/pull/76127


More information about the cfe-commits mailing list