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, "0", "int">; +def sanitize_targets_EQ + : Joined<["-"], "fsanitize-target=">, + MetaVarName<"">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Select target to enable sanitizer in offloading scenario, " + "valid targets are host, device, both(both host and device " + "enabled).">, + Group, + Values<"host,device,both">, + NormalizedValuesScope<"llvm::AsanTargetsToEnable">, + NormalizedValues<["Host", "Device", "Both"]>, + MarshallingInfoEnum, "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..25da511f02f2ad 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,11 @@ 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(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