llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Kalvin (j39m)

<details>
<summary>Changes</summary>

This is a follow-up to pull 148323. It mints
`-fsanitize-ignore-for-ubsan-feature=...`, accepting a list of (UBSan) 
sanitizers that should not cause
`__has_feature(undefined_behavior_sanitizer)` to evaluate true.

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


8 Files Affected:

- (modified) clang/include/clang/Basic/DiagnosticDriverKinds.td (+1) 
- (modified) clang/include/clang/Basic/Features.def (+1-1) 
- (modified) clang/include/clang/Basic/LangOptions.h (+3) 
- (modified) clang/include/clang/Driver/SanitizerArgs.h (+1) 
- (modified) clang/include/clang/Options/Options.td (+8) 
- (modified) clang/lib/Driver/SanitizerArgs.cpp (+29) 
- (modified) clang/lib/Frontend/CompilerInvocation.cpp (+15) 
- (modified) clang/test/Lexer/has_feature_undefined_behavior_sanitizer.cpp 
(+30) 


``````````diff
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td 
b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index aeffe96e806bd..9e5ca9f76397b 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -277,6 +277,7 @@ def err_drv_malformed_sanitizer_metadata_ignorelist : Error<
   "malformed sanitizer metadata ignorelist: '%0'">;
 def err_drv_unsupported_static_sanitizer_darwin : Error<
   "static %0 runtime is not supported on darwin">;
+def err_drv_not_a_ubsan_sanitizer : Error<"not a UBSan sanitizer: '%0'">;
 def err_drv_duplicate_config : Error<
   "no more than one option '--config' is allowed">;
 def err_drv_cannot_open_config_file : Error<
diff --git a/clang/include/clang/Basic/Features.def 
b/clang/include/clang/Basic/Features.def
index 0e91b42a132c1..52dd3a72392ee 100644
--- a/clang/include/clang/Basic/Features.def
+++ b/clang/include/clang/Basic/Features.def
@@ -53,7 +53,7 @@ FEATURE(memtag_globals,
         LangOpts.Sanitize.has(SanitizerKind::MemtagGlobals))
 FEATURE(xray_instrument, LangOpts.XRayInstrument)
 FEATURE(undefined_behavior_sanitizer,
-        LangOpts.Sanitize.hasOneOf(SanitizerKind::Undefined))
+        LangOpts.Sanitize.hasOneOf(SanitizerKind::Undefined & 
~LangOpts.UBSanFeatureSuppressedSanitize.Mask))
 FEATURE(undefined_behavior_sanitizer_finegrained_feature_checks, true)
 // These are all part of undefined_behavior_sanitizer:
 FEATURE(alignment_sanitizer,
diff --git a/clang/include/clang/Basic/LangOptions.h 
b/clang/include/clang/Basic/LangOptions.h
index 3f042f8ddb5a1..66fe01e877cb1 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -441,6 +441,9 @@ class LangOptions : public LangOptionsBase {
   SanitizerSet Sanitize;
   /// Is at least one coverage instrumentation type enabled.
   bool SanitizeCoverage = false;
+  /// Set of enabled (undefined behavior) sanitizers that do not cause
+  /// `__has_feature(undefined_behavior_sanitizer)` to evaluate true.
+  SanitizerSet UBSanFeatureSuppressedSanitize;
 
   /// Paths to files specifying which objects
   /// (files, functions, variables) should not be instrumented.
diff --git a/clang/include/clang/Driver/SanitizerArgs.h 
b/clang/include/clang/Driver/SanitizerArgs.h
index 84fb66e16bee3..aa8aa9be287c6 100644
--- a/clang/include/clang/Driver/SanitizerArgs.h
+++ b/clang/include/clang/Driver/SanitizerArgs.h
@@ -28,6 +28,7 @@ class SanitizerArgs {
   SanitizerSet MergeHandlers;
   SanitizerMaskCutoffs SkipHotCutoffs;
   SanitizerSet AnnotateDebugInfo;
+  SanitizerSet SuppressUBSanFeature;
 
   std::vector<std::string> UserIgnorelistFiles;
   std::vector<std::string> SystemIgnorelistFiles;
diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index c6841937c8d39..84243ad80f2a1 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -2404,6 +2404,14 @@ def fsanitize_EQ : CommaJoined<["-"], "fsanitize=">, 
Group<f_clang_Group>,
                             "or suspicious behavior. See user manual for 
available checks">;
 def fno_sanitize_EQ : CommaJoined<["-"], "fno-sanitize=">, 
Group<f_clang_Group>,
                       Visibility<[ClangOption, CLOption]>;
+def fsanitize_ignore_for_ubsan_feature_EQ
+    : CommaJoined<["-"], "fsanitize-ignore-for-ubsan-feature=">,
+      Group<f_clang_Group>,
+      MetaVarName<"<check>">,
+      HelpText<
+          "Prevents `__has_feature(undefined_behavior_sanitizer)` from "
+          "evaluating true for "
+          "these UBSan checks. See user manual for available UBSan checks">;
 
 def fsanitize_ignorelist_EQ : Joined<["-"], "fsanitize-ignorelist=">,
   Group<f_clang_Group>, HelpText<"Path to ignorelist file for sanitizers">;
diff --git a/clang/lib/Driver/SanitizerArgs.cpp 
b/clang/lib/Driver/SanitizerArgs.cpp
index be068b2381d06..08db579626437 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -408,6 +408,8 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
                                 // unused-argument diagnostics.
   SanitizerMask DiagnosedKinds; // All Kinds we have diagnosed up to now.
                                 // Used to deduplicate diagnostics.
+  SanitizerMask IgnoreForUbsanFeature; // Accumulated set of values passed to
+                                       // 
`-fsanitize-ignore-for-ubsan-feature`.
   SanitizerMask Kinds;
   const SanitizerMask Supported = setGroupBits(TC.getSupportedSanitizers());
 
@@ -612,6 +614,11 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
       Arg->claim();
       SanitizerMask Remove = parseArgValues(D, Arg, DiagnoseErrors);
       AllRemove |= expandSanitizerGroups(Remove);
+    } else if (Arg->getOption().matches(
+                   options::OPT_fsanitize_ignore_for_ubsan_feature_EQ)) {
+      Arg->claim();
+      SanitizerMask Suppress = parseArgValues(D, Arg, DiagnoseErrors);
+      IgnoreForUbsanFeature |= expandSanitizerGroups(Suppress);
     }
   }
 
@@ -736,6 +743,22 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
   // -f(-no)sanitize=leak should change whether leak detection is enabled by
   // default in ASan?
 
+  // Error if a non-UBSan sanitizer is passed to
+  // `-fsanitize-ignore-for-ubsan-feature=`.
+  //
+  // `shift` is a `SANITIZER_GROUP()`, and so is expanded into its constituents
+  // by `expandSanitizerGroups()` above, though the physical bit is not 
included
+  // in `SanitizerKind::Undefined`.
+  const SanitizerMask not_ubsan_mask =
+      IgnoreForUbsanFeature &
+      ~(SanitizerKind::Undefined | SanitizerKind::ShiftGroup);
+  if (not_ubsan_mask && DiagnoseErrors) {
+    SanitizerSet not_ubsan;
+    not_ubsan.set(not_ubsan_mask);
+    D.Diag(clang::diag::err_drv_not_a_ubsan_sanitizer) << toString(not_ubsan);
+  }
+  IgnoreForUbsanFeature &= SanitizerKind::Undefined;
+
   // Parse -f(no-)?sanitize-recover flags.
   SanitizerMask RecoverableKinds = parseSanitizeArgs(
       D, Args, DiagnoseErrors, RecoverableByDefault, AlwaysRecoverable,
@@ -1212,6 +1235,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
   MergeHandlers.Mask |= MergeKinds;
 
   AnnotateDebugInfo.Mask |= AnnotateDebugInfoKinds;
+  SuppressUBSanFeature.Mask |= IgnoreForUbsanFeature;
 
   // Zero out SkipHotCutoffs for unused sanitizers
   SkipHotCutoffs.clear(~Sanitizers.Mask);
@@ -1392,6 +1416,11 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const 
llvm::opt::ArgList &Args,
     return;
   CmdArgs.push_back(Args.MakeArgString("-fsanitize=" + toString(Sanitizers)));
 
+  if (!SuppressUBSanFeature.empty())
+    CmdArgs.push_back(
+        Args.MakeArgString("-fsanitize-ignore-for-ubsan-feature=" +
+                           toString(SuppressUBSanFeature)));
+
   if (!RecoverableSanitizers.empty())
     CmdArgs.push_back(Args.MakeArgString("-fsanitize-recover=" +
                                          toString(RecoverableSanitizers)));
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp 
b/clang/lib/Frontend/CompilerInvocation.cpp
index b42c263abd2c7..66dd3bc458efe 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -3693,6 +3693,10 @@ void CompilerInvocationBase::GenerateLangArgs(const 
LangOptions &Opts,
       GenerateArg(Consumer, OPT_pic_is_pie);
     for (StringRef Sanitizer : serializeSanitizerKinds(Opts.Sanitize))
       GenerateArg(Consumer, OPT_fsanitize_EQ, Sanitizer);
+    for (StringRef Sanitizer :
+         serializeSanitizerKinds(Opts.UBSanFeatureSuppressedSanitize))
+      GenerateArg(Consumer, OPT_fsanitize_ignore_for_ubsan_feature_EQ,
+                  Sanitizer);
 
     return;
   }
@@ -3892,6 +3896,9 @@ void CompilerInvocationBase::GenerateLangArgs(const 
LangOptions &Opts,
 
   for (StringRef Sanitizer : serializeSanitizerKinds(Opts.Sanitize))
     GenerateArg(Consumer, OPT_fsanitize_EQ, Sanitizer);
+  for (StringRef Sanitizer :
+       serializeSanitizerKinds(Opts.UBSanFeatureSuppressedSanitize))
+    GenerateArg(Consumer, OPT_fsanitize_ignore_for_ubsan_feature_EQ, 
Sanitizer);
 
   // Conflating '-fsanitize-system-ignorelist' and '-fsanitize-ignorelist'.
   for (const std::string &F : Opts.NoSanitizeFiles)
@@ -3973,6 +3980,10 @@ bool CompilerInvocation::ParseLangArgs(LangOptions 
&Opts, ArgList &Args,
     Opts.PIE = Args.hasArg(OPT_pic_is_pie);
     parseSanitizerKinds("-fsanitize=", Args.getAllArgValues(OPT_fsanitize_EQ),
                         Diags, Opts.Sanitize);
+    parseSanitizerKinds(
+        "-fsanitize-ignore-for-ubsan-feature=",
+        Args.getAllArgValues(OPT_fsanitize_ignore_for_ubsan_feature_EQ), Diags,
+        Opts.UBSanFeatureSuppressedSanitize);
 
     return Diags.getNumErrors() == NumErrorsBefore;
   }
@@ -4390,6 +4401,10 @@ bool CompilerInvocation::ParseLangArgs(LangOptions 
&Opts, ArgList &Args,
   // Parse -fsanitize= arguments.
   parseSanitizerKinds("-fsanitize=", Args.getAllArgValues(OPT_fsanitize_EQ),
                       Diags, Opts.Sanitize);
+  parseSanitizerKinds(
+      "-fsanitize-ignore-for-ubsan-feature=",
+      Args.getAllArgValues(OPT_fsanitize_ignore_for_ubsan_feature_EQ), Diags,
+      Opts.UBSanFeatureSuppressedSanitize);
   Opts.NoSanitizeFiles = Args.getAllArgValues(OPT_fsanitize_ignorelist_EQ);
   std::vector<std::string> systemIgnorelists =
       Args.getAllArgValues(OPT_fsanitize_system_ignorelist_EQ);
diff --git a/clang/test/Lexer/has_feature_undefined_behavior_sanitizer.cpp 
b/clang/test/Lexer/has_feature_undefined_behavior_sanitizer.cpp
index e1a07d215b549..de4dd1f1cfebc 100644
--- a/clang/test/Lexer/has_feature_undefined_behavior_sanitizer.cpp
+++ b/clang/test/Lexer/has_feature_undefined_behavior_sanitizer.cpp
@@ -23,6 +23,36 @@
 
 // RUN: %clang -E  %s -o - | FileCheck --check-prefix=CHECK-NO-UBSAN %s
 
+// Specifying a specific sanitizer under UBSan and immediately suppressing
+// `__has_feature(undefined_behavior_sanitizer)` for the same should result in
+// "no-UBSan."
+// BUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=undefined 
-fsanitize-ignore-for-ubsan-feature=undefined %s -o - | FileCheck 
--check-prefix=CHECK-NO-UBSAN %s
+// RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=alignment 
-fsanitize-ignore-for-ubsan-feature=alignment %s -o - | FileCheck 
--check-prefixes=CHECK-NO-UBSAN %s
+// RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=bool 
-fsanitize-ignore-for-ubsan-feature=bool %s -o - | FileCheck 
--check-prefixes=CHECK-NO-UBSAN %s
+// RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=builtin 
-fsanitize-ignore-for-ubsan-feature=builtin %s -o - | FileCheck 
--check-prefixes=CHECK-NO-UBSAN %s
+// RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=array-bounds 
-fsanitize-ignore-for-ubsan-feature=array-bounds %s -o - | FileCheck 
--check-prefixes=CHECK-NO-UBSAN %s
+// RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=enum 
-fsanitize-ignore-for-ubsan-feature=enum %s -o - | FileCheck 
--check-prefixes=CHECK-NO-UBSAN %s
+// RUN: %clang -E -target x86_64-unknown-linux-gnu 
-fsanitize=float-cast-overflow 
-fsanitize-ignore-for-ubsan-feature=float-cast-overflow %s -o - | FileCheck 
--check-prefixes=CHECK-NO-UBSAN %s
+// RUN: %clang -E -target x86_64-unknown-linux-gnu 
-fsanitize=integer-divide-by-zero 
-fsanitize-ignore-for-ubsan-feature=integer-divide-by-zero %s -o - | FileCheck 
--check-prefixes=CHECK-NO-UBSAN %s
+// RUN: %clang -E -target x86_64-unknown-linux-gnu 
-fsanitize=nonnull-attribute 
-fsanitize-ignore-for-ubsan-feature=nonnull-attribute %s -o - | FileCheck 
--check-prefixes=CHECK-NO-UBSAN %s
+// RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=null 
-fsanitize-ignore-for-ubsan-feature=null %s -o - | FileCheck 
--check-prefixes=CHECK-NO-UBSAN %s
+// object-size is a no-op at O0.
+// RUN: %clang -E -target x86_64-unknown-linux-gnu -O2 -fsanitize=object-size 
-fsanitize-ignore-for-ubsan-feature=object-size %s -o - | FileCheck 
--check-prefixes=CHECK-NO-UBSAN %s
+// RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=pointer-overflow 
-fsanitize-ignore-for-ubsan-feature=pointer-overflow %s -o - | FileCheck 
--check-prefixes=CHECK-NO-UBSAN %s
+// RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=return 
-fsanitize-ignore-for-ubsan-feature=return %s -o - | FileCheck 
--check-prefixes=CHECK-NO-UBSAN %s
+// RUN: %clang -E -target x86_64-unknown-linux-gnu 
-fsanitize=returns-nonnull-attribute 
-fsanitize-ignore-for-ubsan-feature=returns-nonnull-attribute %s -o - | 
FileCheck --check-prefixes=CHECK-NO-UBSAN %s
+// RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=shift-base 
-fsanitize-ignore-for-ubsan-feature=shift-base %s -o - | FileCheck 
--check-prefixes=CHECK-NO-UBSAN %s
+// RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=shift-exponent 
-fsanitize-ignore-for-ubsan-feature=shift-exponent %s -o - | FileCheck 
--check-prefixes=CHECK-NO-UBSAN %s
+// RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=shift 
-fsanitize-ignore-for-ubsan-feature=shift %s -o - | FileCheck 
--check-prefixes=CHECK-NO-UBSAN %s
+// RUN: %clang -E -target x86_64-unknown-linux-gnu 
-fsanitize=signed-integer-overflow 
-fsanitize-ignore-for-ubsan-feature=signed-integer-overflow %s -o - | FileCheck 
--check-prefixes=CHECK-NO-UBSAN %s
+// RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=unreachable 
-fsanitize-ignore-for-ubsan-feature=unreachable %s -o - | FileCheck 
--check-prefixes=CHECK-NO-UBSAN %s
+// RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=vla-bound 
-fsanitize-ignore-for-ubsan-feature=vla-bound %s -o - | FileCheck 
--check-prefixes=CHECK-NO-UBSAN %s
+// RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=function 
-fsanitize-ignore-for-ubsan-feature=function %s -o - | FileCheck 
--check-prefixes=CHECK-NO-UBSAN %s
+
+// Spot check: suppressing an unrelated sanitizer should still result in a "has
+// UBSan" configuration.
+// RUN: %clang -E -target x86_64-unknown-linux-gnu -fsanitize=function 
-fsanitize-ignore-for-ubsan-feature=alignment %s -o - | FileCheck 
--check-prefixes=CHECK-UBSAN,CHECK-FUNCTION %s
+
 // REQUIRES: x86-registered-target
 
 #if !__has_feature(undefined_behavior_sanitizer_finegrained_feature_checks)

``````````

</details>


https://github.com/llvm/llvm-project/pull/170822
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to