Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ASan][Driver] Add sanitize-target flag to support enabling ASan in device or host compilation #76127

Closed
wants to merge 2 commits into from

Conversation

jinge90
Copy link
Contributor

@jinge90 jinge90 commented Dec 21, 2023

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'.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:codegen llvm:transforms labels Dec 21, 2023
@llvmbot
Copy link
Collaborator

llvmbot commented Dec 21, 2023

@llvm/pr-subscribers-llvm-transforms
@llvm/pr-subscribers-clang-driver
@llvm/pr-subscribers-clang-codegen

@llvm/pr-subscribers-clang

Author: None (jinge90)

Changes

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 --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

@jinge90 jinge90 requested a review from bader December 21, 2023 07:23
Copy link

github-actions bot commented Dec 21, 2023

✅ With the latest revision this PR passed the C/C++ code formatter.

@jinge90
Copy link
Contributor Author

jinge90 commented Dec 21, 2023

Hi, @vitalybuka and @bader
Recently, we are working to enable address sanitizer for SYCL and OMP offloading compiler. After enabling Asan instrumentation pass, ASanPass will insert "_asan*" functions into user code in both device and host compilation. Although host code sanitizer is useful, sometimes we care more about device code sanitizer in offloading scenario and a flag to disable/enable ASan in host or device compilation phase will help. If offloading compiler's user wants to use sanitizer to detect bugs in their device code target for GPU, FPGA or other accelerators, they can add '-fsanitize-target=device' to disable ASanPass in host code(CPU code).
Thanks very much.

…evice or host compilation

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'.
Signed-off-by: jinge90 <ge.jin@intel.com>
@jinge90 jinge90 requested a review from MaskRay December 21, 2023 08:37
@jinge90
Copy link
Contributor Author

jinge90 commented Dec 21, 2023

Hi, @MaskRay and @AaronBallman
Could you help review this patch?
Thanks very much.

@vitalybuka
Copy link
Collaborator

It seems as the build system business to control this logic, not driver.
@MaskRay and @AaronBallman WDYT?

@jinge90
Copy link
Contributor Author

jinge90 commented Jan 8, 2024

Hi, @MaskRay and @AaronBallman
Kind ping~.
Thanks very much.

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had some conversations with @jinge90 offline and we're now thinking that perhaps a better approach is to have a more general feature for specifying host-only and target-only options instead of having each option split into two. Marking this review as requesting changes so it doesn't get accidentally accepted.

Copy link
Collaborator

@vitalybuka vitalybuka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing from my pending reviews

@jinge90 jinge90 closed this Mar 13, 2024
@jinge90
Copy link
Contributor Author

jinge90 commented Mar 13, 2024

Using general option to pass device specific flags to device compilation should be better approach, so this pr is not needed any more, thanks very much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:codegen clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category llvm:transforms
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants