[clang] [compiler-rt] [UBSan] Diagnose assumption violation (PR #104741)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 24 22:11:06 PDT 2024


https://github.com/dtcxzyw updated https://github.com/llvm/llvm-project/pull/104741

>From fdadb0fdc2288b18d4dfe4f4510d057a7552ee39 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 19 Aug 2024 15:22:39 +0800
Subject: [PATCH 1/4] [UBSan] Diagnose assumption violation

---
 clang/lib/CodeGen/CGBuiltin.cpp                | 18 ++++++++++++------
 clang/lib/CodeGen/CGStmt.cpp                   |  3 ++-
 clang/lib/CodeGen/CodeGenFunction.h            |  1 +
 clang/test/CodeGen/ubsan-builtin-checks.c      | 17 +++++++++++++++++
 compiler-rt/lib/ubsan/ubsan_handlers.cpp       |  9 ++++++---
 compiler-rt/lib/ubsan/ubsan_handlers.h         |  1 +
 .../test/ubsan/TestCases/Misc/builtins.cpp     | 16 ++++++++++++++--
 7 files changed, 53 insertions(+), 12 deletions(-)

diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 942468204f054c..7e93e46d3f92b9 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -1997,16 +1997,21 @@ struct CallObjCArcUse final : EHScopeStack::Cleanup {
 
 Value *CodeGenFunction::EmitCheckedArgForBuiltin(const Expr *E,
                                                  BuiltinCheckKind Kind) {
-  assert((Kind == BCK_CLZPassedZero || Kind == BCK_CTZPassedZero)
-          && "Unsupported builtin check kind");
+  assert((Kind == BCK_CLZPassedZero || Kind == BCK_CTZPassedZero ||
+          Kind == BCK_AssumePassedFalse) &&
+         "Unsupported builtin check kind");
 
-  Value *ArgValue = EmitScalarExpr(E);
+  Value *ArgValue =
+      Kind == BCK_AssumePassedFalse ? EvaluateExprAsBool(E) : EmitScalarExpr(E);
   if (!SanOpts.has(SanitizerKind::Builtin))
     return ArgValue;
 
   SanitizerScope SanScope(this);
-  Value *Cond = Builder.CreateICmpNE(
-      ArgValue, llvm::Constant::getNullValue(ArgValue->getType()));
+  Value *Cond =
+      Kind == BCK_AssumePassedFalse
+          ? ArgValue
+          : Builder.CreateICmpNE(
+                ArgValue, llvm::Constant::getNullValue(ArgValue->getType()));
   EmitCheck(std::make_pair(Cond, SanitizerKind::Builtin),
             SanitizerHandler::InvalidBuiltin,
             {EmitCheckSourceLocation(E->getExprLoc()),
@@ -3428,7 +3433,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     if (E->getArg(0)->HasSideEffects(getContext()))
       return RValue::get(nullptr);
 
-    Value *ArgValue = EmitScalarExpr(E->getArg(0));
+    Value *ArgValue =
+        EmitCheckedArgForBuiltin(E->getArg(0), BCK_AssumePassedFalse);
     Function *FnAssume = CGM.getIntrinsic(Intrinsic::assume);
     Builder.CreateCall(FnAssume, ArgValue);
     return RValue::get(nullptr);
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 623857b43a5575..470ba3af0edc9b 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -754,7 +754,8 @@ void CodeGenFunction::EmitAttributedStmt(const AttributedStmt &S) {
       const Expr *Assumption = cast<CXXAssumeAttr>(A)->getAssumption();
       if (getLangOpts().CXXAssumptions && Builder.GetInsertBlock() &&
           !Assumption->HasSideEffects(getContext())) {
-        llvm::Value *AssumptionVal = EvaluateExprAsBool(Assumption);
+        llvm::Value *AssumptionVal =
+            EmitCheckedArgForBuiltin(Assumption, BCK_AssumePassedFalse);
         Builder.CreateAssumption(AssumptionVal);
       }
     } break;
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 2df17e83bae2ee..aa312e055ce71f 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -5084,6 +5084,7 @@ class CodeGenFunction : public CodeGenTypeCache {
   enum BuiltinCheckKind {
     BCK_CTZPassedZero,
     BCK_CLZPassedZero,
+    BCK_AssumePassedFalse,
   };
 
   /// Emits an argument for a call to a builtin. If the builtin sanitizer is
diff --git a/clang/test/CodeGen/ubsan-builtin-checks.c b/clang/test/CodeGen/ubsan-builtin-checks.c
index c7f6078f903bad..8535ec915ac346 100644
--- a/clang/test/CodeGen/ubsan-builtin-checks.c
+++ b/clang/test/CodeGen/ubsan-builtin-checks.c
@@ -51,3 +51,20 @@ void check_clz(int n) {
   // CHECK: call void @__ubsan_handle_invalid_builtin
   __builtin_clzg((unsigned int)n);
 }
+
+// CHECK: define{{.*}} void @check_assume
+void check_assume(int n) {
+  // CHECK: [[TOBOOL:%.*]] = icmp ne i32 [[N:%.*]], 0
+  // CHECK-NEXT: br i1 [[TOBOOL]]
+  //
+  // Handler block:
+  // CHECK: call void @__ubsan_handle_invalid_builtin
+  // CHECK-NEXT: unreachable
+  //
+  // Continuation block:
+  // CHECK: call void @llvm.assume(i1 [[TOBOOL]])
+  __builtin_assume(n);
+
+  // CHECK: call void @__ubsan_handle_invalid_builtin
+  __attribute__((assume(n)));
+}
diff --git a/compiler-rt/lib/ubsan/ubsan_handlers.cpp b/compiler-rt/lib/ubsan/ubsan_handlers.cpp
index 9dbe8e6c0c1745..bc8fc02d4d203e 100644
--- a/compiler-rt/lib/ubsan/ubsan_handlers.cpp
+++ b/compiler-rt/lib/ubsan/ubsan_handlers.cpp
@@ -633,9 +633,12 @@ static void handleInvalidBuiltin(InvalidBuiltinData *Data, ReportOptions Opts) {
 
   ScopedReport R(Opts, Loc, ET);
 
-  Diag(Loc, DL_Error, ET,
-       "passing zero to __builtin_%0(), which is not a valid argument")
-      << ((Data->Kind == BCK_CTZPassedZero) ? "ctz" : "clz");
+  if (Data->Kind == BCK_AssumePassedFalse)
+    Diag(Loc, DL_Error, ET, "assumption is violated during execution");
+  else
+    Diag(Loc, DL_Error, ET,
+        "passing zero to __builtin_%0(), which is not a valid argument")
+        << ((Data->Kind == BCK_CTZPassedZero) ? "ctz" : "clz");
 }
 
 void __ubsan::__ubsan_handle_invalid_builtin(InvalidBuiltinData *Data) {
diff --git a/compiler-rt/lib/ubsan/ubsan_handlers.h b/compiler-rt/lib/ubsan/ubsan_handlers.h
index bae661a56833dd..4ffa1439a1323f 100644
--- a/compiler-rt/lib/ubsan/ubsan_handlers.h
+++ b/compiler-rt/lib/ubsan/ubsan_handlers.h
@@ -159,6 +159,7 @@ RECOVERABLE(implicit_conversion, ImplicitConversionData *Data, ValueHandle Src,
 enum BuiltinCheckKind : unsigned char {
   BCK_CTZPassedZero,
   BCK_CLZPassedZero,
+  BCK_AssumePassedFalse,
 };
 
 struct InvalidBuiltinData {
diff --git a/compiler-rt/test/ubsan/TestCases/Misc/builtins.cpp b/compiler-rt/test/ubsan/TestCases/Misc/builtins.cpp
index a635f7fcc686ed..2702065bce0678 100644
--- a/compiler-rt/test/ubsan/TestCases/Misc/builtins.cpp
+++ b/compiler-rt/test/ubsan/TestCases/Misc/builtins.cpp
@@ -1,8 +1,8 @@
 // REQUIRES: target={{x86_64.*}}
 //
-// RUN: %clangxx -fsanitize=builtin -w %s -O3 -o %t
+// RUN: %clangxx -fsanitize=builtin -fno-inline -w %s -O3 -o %t
 // RUN: %run %t 2>&1 | FileCheck %s --check-prefix=RECOVER
-// RUN: %clangxx -fsanitize=builtin -fno-sanitize-recover=builtin -w %s -O3 -o %t.abort
+// RUN: %clangxx -fsanitize=builtin -fno-inline -fno-sanitize-recover=builtin -w %s -O3 -o %t.abort
 // RUN: not %run %t.abort 2>&1 | FileCheck %s --check-prefix=ABORT
 
 void check_ctz(int n) {
@@ -28,8 +28,20 @@ void check_clz(int n) {
   __builtin_clzll(n);
 }
 
+void check_assume(int n) {
+  // RECOVER: builtins.cpp:[[@LINE+1]]:20: runtime error: assumption is violated during execution
+  __builtin_assume(n);
+}
+
+void check_assume_attr(int n) {
+  // RECOVER: builtins.cpp:[[@LINE+1]]:25: runtime error: assumption is violated during execution
+  __attribute__((assume(n)));
+}
+
 int main() {
   check_ctz(0);
   check_clz(0);
+  check_assume(0);
+  check_assume_attr(0);
   return 0;
 }

>From 6cce5061d91345182b295696cc2f2abb84ff39a5 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Tue, 24 Sep 2024 15:54:18 -0700
Subject: [PATCH 2/4] Extract EmitCheckedArgForAssume

---
 clang/lib/CodeGen/CGBuiltin.cpp     | 33 ++++++++++++++++++-----------
 clang/lib/CodeGen/CGStmt.cpp        |  3 +--
 clang/lib/CodeGen/CodeGenFunction.h |  4 ++++
 3 files changed, 26 insertions(+), 14 deletions(-)

diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 7e93e46d3f92b9..ac7b54a32c8009 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -1997,21 +1997,16 @@ struct CallObjCArcUse final : EHScopeStack::Cleanup {
 
 Value *CodeGenFunction::EmitCheckedArgForBuiltin(const Expr *E,
                                                  BuiltinCheckKind Kind) {
-  assert((Kind == BCK_CLZPassedZero || Kind == BCK_CTZPassedZero ||
-          Kind == BCK_AssumePassedFalse) &&
-         "Unsupported builtin check kind");
+  assert((Kind == BCK_CLZPassedZero || Kind == BCK_CTZPassedZero)
+          && "Unsupported builtin check kind");
 
-  Value *ArgValue =
-      Kind == BCK_AssumePassedFalse ? EvaluateExprAsBool(E) : EmitScalarExpr(E);
+  Value *ArgValue = EmitScalarExpr(E);
   if (!SanOpts.has(SanitizerKind::Builtin))
     return ArgValue;
 
   SanitizerScope SanScope(this);
-  Value *Cond =
-      Kind == BCK_AssumePassedFalse
-          ? ArgValue
-          : Builder.CreateICmpNE(
-                ArgValue, llvm::Constant::getNullValue(ArgValue->getType()));
+  Value *Cond = Builder.CreateICmpNE(
+      ArgValue, llvm::Constant::getNullValue(ArgValue->getType()));
   EmitCheck(std::make_pair(Cond, SanitizerKind::Builtin),
             SanitizerHandler::InvalidBuiltin,
             {EmitCheckSourceLocation(E->getExprLoc()),
@@ -2020,6 +2015,21 @@ Value *CodeGenFunction::EmitCheckedArgForBuiltin(const Expr *E,
   return ArgValue;
 }
 
+Value *CodeGenFunction::EmitCheckedArgForAssume(const Expr *E) {
+  Value *ArgValue = EvaluateExprAsBool(E);
+  if (!SanOpts.has(SanitizerKind::Builtin))
+    return ArgValue;
+
+  SanitizerScope SanScope(this);
+  EmitCheck(
+      std::make_pair(ArgValue, SanitizerKind::Builtin),
+      SanitizerHandler::InvalidBuiltin,
+      {EmitCheckSourceLocation(E->getExprLoc()),
+       llvm::ConstantInt::get(Builder.getInt8Ty(), BCK_AssumePassedFalse)},
+      std::nullopt);
+  return ArgValue;
+}
+
 static Value *EmitAbs(CodeGenFunction &CGF, Value *ArgValue, bool HasNSW) {
   return CGF.Builder.CreateBinaryIntrinsic(
       Intrinsic::abs, ArgValue,
@@ -3433,8 +3443,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     if (E->getArg(0)->HasSideEffects(getContext()))
       return RValue::get(nullptr);
 
-    Value *ArgValue =
-        EmitCheckedArgForBuiltin(E->getArg(0), BCK_AssumePassedFalse);
+    Value *ArgValue = EmitCheckedArgForAssume(E->getArg(0));
     Function *FnAssume = CGM.getIntrinsic(Intrinsic::assume);
     Builder.CreateCall(FnAssume, ArgValue);
     return RValue::get(nullptr);
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 470ba3af0edc9b..9bf15fca0de489 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -754,8 +754,7 @@ void CodeGenFunction::EmitAttributedStmt(const AttributedStmt &S) {
       const Expr *Assumption = cast<CXXAssumeAttr>(A)->getAssumption();
       if (getLangOpts().CXXAssumptions && Builder.GetInsertBlock() &&
           !Assumption->HasSideEffects(getContext())) {
-        llvm::Value *AssumptionVal =
-            EmitCheckedArgForBuiltin(Assumption, BCK_AssumePassedFalse);
+        llvm::Value *AssumptionVal = EmitCheckedArgForAssume(Assumption);
         Builder.CreateAssumption(AssumptionVal);
       }
     } break;
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index aa312e055ce71f..d4e46f77868eca 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -5091,6 +5091,10 @@ class CodeGenFunction : public CodeGenTypeCache {
   /// enabled, a runtime check specified by \p Kind is also emitted.
   llvm::Value *EmitCheckedArgForBuiltin(const Expr *E, BuiltinCheckKind Kind);
 
+  /// Emits an argument for a call to a `__builtin_assume`. If the builtin
+  /// sanitizer is enabled, a runtime check is also emitted.
+  llvm::Value *EmitCheckedArgForAssume(const Expr *E);
+
   /// Emit a description of a type in a format suitable for passing to
   /// a runtime sanitizer handler.
   llvm::Constant *EmitCheckTypeDescriptor(QualType T);

>From 7bcaa3bb81e54edc440abc2b4d5ac85289c6b522 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Wed, 25 Sep 2024 12:58:16 +0800
Subject: [PATCH 3/4] format

---
 clang/lib/CodeGen/CGBuiltin.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index ac7b54a32c8009..7bbf22569e73ca 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -1997,8 +1997,8 @@ struct CallObjCArcUse final : EHScopeStack::Cleanup {
 
 Value *CodeGenFunction::EmitCheckedArgForBuiltin(const Expr *E,
                                                  BuiltinCheckKind Kind) {
-  assert((Kind == BCK_CLZPassedZero || Kind == BCK_CTZPassedZero)
-          && "Unsupported builtin check kind");
+  assert((Kind == BCK_CLZPassedZero || Kind == BCK_CTZPassedZero) &&
+         "Unsupported builtin check kind");
 
   Value *ArgValue = EmitScalarExpr(E);
   if (!SanOpts.has(SanitizerKind::Builtin))

>From 78c2bcc6a5e2cc0181cd210414401d4f738b9954 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Wed, 25 Sep 2024 13:10:31 +0800
Subject: [PATCH 4/4] Format

---
 compiler-rt/lib/ubsan/ubsan_handlers.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/compiler-rt/lib/ubsan/ubsan_handlers.cpp b/compiler-rt/lib/ubsan/ubsan_handlers.cpp
index bc8fc02d4d203e..a419cf0b2b5557 100644
--- a/compiler-rt/lib/ubsan/ubsan_handlers.cpp
+++ b/compiler-rt/lib/ubsan/ubsan_handlers.cpp
@@ -637,7 +637,7 @@ static void handleInvalidBuiltin(InvalidBuiltinData *Data, ReportOptions Opts) {
     Diag(Loc, DL_Error, ET, "assumption is violated during execution");
   else
     Diag(Loc, DL_Error, ET,
-        "passing zero to __builtin_%0(), which is not a valid argument")
+         "passing zero to __builtin_%0(), which is not a valid argument")
         << ((Data->Kind == BCK_CTZPassedZero) ? "ctz" : "clz");
 }
 



More information about the llvm-commits mailing list