-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[UBSan] Use -fsanitize-handler-preserve-all-regs in codegen #168645
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
[UBSan] Use -fsanitize-handler-preserve-all-regs in codegen #168645
Conversation
Created using spr 1.3.7
|
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clang-codegen Author: Florian Mayer (fmayer) ChangesFull diff: https://github.com/llvm/llvm-project/pull/168645.diff 9 Files Affected:
diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 6f63e6470270e..a243b2e222716 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -1134,6 +1134,8 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
CodeGenOpts.SanitizeMinimalRuntime),
/*MayReturn=*/
CodeGenOpts.SanitizeRecover.has(SanitizerKind::LocalBounds),
+ /*PreserveRt=*/static_cast<bool>(
+ CodeGenOpts.SanitizePreserveRuntime),
};
}
FPM.addPass(BoundsCheckingPass(Options));
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index a837f00732748..9f58c0a77e526 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -3789,6 +3789,7 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
bool NeedsAbortSuffix =
IsFatal && RecoverKind != CheckRecoverableKind::Unrecoverable;
bool MinimalRuntime = CGF.CGM.getCodeGenOpts().SanitizeMinimalRuntime;
+ bool PreserveRuntime = CGF.CGM.getCodeGenOpts().SanitizePreserveRuntime;
const SanitizerHandlerInfo &CheckInfo = SanitizerHandlers[CheckHandler];
const StringRef CheckName = CheckInfo.Name;
std::string FnName = "__ubsan_handle_" + CheckName.str();
@@ -3798,6 +3799,8 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
FnName += "_minimal";
if (NeedsAbortSuffix)
FnName += "_abort";
+ else if (MinimalRuntime && PreserveRuntime)
+ FnName += "_preserve";
bool MayReturn =
!IsFatal || RecoverKind == CheckRecoverableKind::AlwaysRecoverable;
@@ -3818,6 +3821,10 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
(CGF.CurCodeDecl && CGF.CurCodeDecl->hasAttr<OptimizeNoneAttr>());
if (NoMerge)
HandlerCall->addFnAttr(llvm::Attribute::NoMerge);
+ if (MinimalRuntime && PreserveRuntime) {
+ // N.B. there is also a clang::CallingConv which is not what we want here.
+ HandlerCall->setCallingConv(llvm::CallingConv::PreserveAll);
+ }
if (!MayReturn) {
HandlerCall->setDoesNotReturn();
CGF.Builder.CreateUnreachable();
diff --git a/clang/test/CodeGen/cfi-icall-trap-recover-runtime.c b/clang/test/CodeGen/cfi-icall-trap-recover-runtime.c
index 117672a9d4368..33be31e09bb56 100644
--- a/clang/test/CodeGen/cfi-icall-trap-recover-runtime.c
+++ b/clang/test/CodeGen/cfi-icall-trap-recover-runtime.c
@@ -9,6 +9,9 @@
// RUN: %clang_cc1 -fsanitize=cfi-icall -fno-sanitize-trap=cfi-icall -fsanitize-recover=cfi-icall -fsanitize-minimal-runtime -flto -fvisibility=hidden -triple x86_64-unknown-linux -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=RECOVER_MIN %s
+// RUN: %clang_cc1 -fsanitize=cfi-icall -fno-sanitize-trap=cfi-icall -fsanitize-recover=cfi-icall -fsanitize-minimal-runtime -fsanitize-preserve-runtime -flto -fvisibility=hidden -triple x86_64-unknown-linux -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=PRESERVE_MIN %s
+
+
// TRAP-LABEL: define hidden void @f(
// TRAP-SAME: ) #[[ATTR0:[0-9]+]] !type [[META6:![0-9]+]] !type [[META7:![0-9]+]] {
// TRAP-NEXT: [[ENTRY:.*:]]
@@ -34,6 +37,11 @@
// RECOVER_MIN-NEXT: [[ENTRY:.*:]]
// RECOVER_MIN-NEXT: ret void
//
+// PRESERVE_MIN-LABEL: define hidden void @f(
+// PRESERVE_MIN-SAME: ) #[[ATTR0:[0-9]+]] !type [[META6:![0-9]+]] !type [[META7:![0-9]+]] {
+// PRESERVE_MIN-NEXT: [[ENTRY:.*:]]
+// PRESERVE_MIN-NEXT: ret void
+//
void f() {
}
@@ -146,6 +154,27 @@ void xf();
// RECOVER_MIN-NEXT: call void (...) [[TMP2]]()
// RECOVER_MIN-NEXT: ret void
//
+// PRESERVE_MIN-LABEL: define hidden void @g(
+// PRESERVE_MIN-SAME: i32 noundef [[B:%.*]]) #[[ATTR0]] !type [[META8:![0-9]+]] !type [[META9:![0-9]+]] {
+// PRESERVE_MIN-NEXT: [[ENTRY:.*:]]
+// PRESERVE_MIN-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4
+// PRESERVE_MIN-NEXT: [[FP:%.*]] = alloca ptr, align 8
+// PRESERVE_MIN-NEXT: store i32 [[B]], ptr [[B_ADDR]], align 4
+// PRESERVE_MIN-NEXT: [[TMP0:%.*]] = load i32, ptr [[B_ADDR]], align 4
+// PRESERVE_MIN-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[TMP0]], 0
+// PRESERVE_MIN-NEXT: [[TMP1:%.*]] = zext i1 [[TOBOOL]] to i64
+// PRESERVE_MIN-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], ptr @f, ptr @xf
+// PRESERVE_MIN-NEXT: store ptr [[COND]], ptr [[FP]], align 8
+// PRESERVE_MIN-NEXT: [[TMP2:%.*]] = load ptr, ptr [[FP]], align 8
+// PRESERVE_MIN-NEXT: [[TMP3:%.*]] = call i1 @llvm.type.test(ptr [[TMP2]], metadata !"_ZTSFvE"), !nosanitize [[META10:![0-9]+]]
+// PRESERVE_MIN-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[HANDLER_CFI_CHECK_FAIL:.*]], !prof [[PROF11:![0-9]+]], !nosanitize [[META10]]
+// PRESERVE_MIN: [[HANDLER_CFI_CHECK_FAIL]]:
+// PRESERVE_MIN-NEXT: call preserve_allcc void @__ubsan_handle_cfi_check_fail_minimal_preserve() #[[ATTR4:[0-9]+]], !nosanitize [[META10]]
+// PRESERVE_MIN-NEXT: br label %[[CONT]], !nosanitize [[META10]]
+// PRESERVE_MIN: [[CONT]]:
+// PRESERVE_MIN-NEXT: call void (...) [[TMP2]]()
+// PRESERVE_MIN-NEXT: ret void
+//
void g(int b) {
void (*fp)() = b ? f : xf;
fp();
@@ -186,3 +215,10 @@ void g(int b) {
// RECOVER_MIN: [[META10]] = !{}
// RECOVER_MIN: [[PROF11]] = !{!"branch_weights", i32 1048575, i32 1}
//.
+// PRESERVE_MIN: [[META6]] = !{i64 0, !"_ZTSFvE"}
+// PRESERVE_MIN: [[META7]] = !{i64 0, !"_ZTSFvE.generalized"}
+// PRESERVE_MIN: [[META8]] = !{i64 0, !"_ZTSFviE"}
+// PRESERVE_MIN: [[META9]] = !{i64 0, !"_ZTSFviE.generalized"}
+// PRESERVE_MIN: [[META10]] = !{}
+// PRESERVE_MIN: [[PROF11]] = !{!"branch_weights", i32 1048575, i32 1}
+//.
diff --git a/clang/test/CodeGenCXX/cfi-vcall-trap-recover-runtime.cpp b/clang/test/CodeGenCXX/cfi-vcall-trap-recover-runtime.cpp
index 3e9328ac0e3ca..dd6a009cb7409 100644
--- a/clang/test/CodeGenCXX/cfi-vcall-trap-recover-runtime.cpp
+++ b/clang/test/CodeGenCXX/cfi-vcall-trap-recover-runtime.cpp
@@ -9,6 +9,8 @@
// RUN: %clang_cc1 -fsanitize=cfi-vcall -fno-sanitize-trap=cfi-vcall -fsanitize-recover=cfi-vcall -fsanitize-minimal-runtime -flto -fvisibility=hidden -triple x86_64-unknown-linux -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=RECOVER_MIN %s
+// RUN: %clang_cc1 -fsanitize=cfi-vcall -fno-sanitize-trap=cfi-vcall -fsanitize-recover=cfi-vcall -fsanitize-minimal-runtime -flto -fvisibility=hidden -triple x86_64-unknown-linux -fwhole-program-vtables -fsanitize-preserve-runtime -emit-llvm -o - %s | FileCheck --check-prefix=PRESERVE_MIN %s
+
struct S1 {
virtual void f();
};
@@ -111,6 +113,25 @@ struct S1 {
// RECOVER_MIN-NEXT: call void [[TMP3]](ptr noundef nonnull align 8 dereferenceable(8) [[TMP0]])
// RECOVER_MIN-NEXT: ret void
//
+// PRESERVE_MIN-LABEL: define hidden void @_Z3s1fP2S1(
+// PRESERVE_MIN-SAME: ptr noundef [[S1:%.*]]) #[[ATTR0:[0-9]+]] {
+// PRESERVE_MIN-NEXT: [[ENTRY:.*:]]
+// PRESERVE_MIN-NEXT: [[S1_ADDR:%.*]] = alloca ptr, align 8
+// PRESERVE_MIN-NEXT: store ptr [[S1]], ptr [[S1_ADDR]], align 8
+// PRESERVE_MIN-NEXT: [[TMP0:%.*]] = load ptr, ptr [[S1_ADDR]], align 8
+// PRESERVE_MIN-NEXT: [[VTABLE:%.*]] = load ptr, ptr [[TMP0]], align 8
+// PRESERVE_MIN-NEXT: [[TMP1:%.*]] = call i1 @llvm.type.test(ptr [[VTABLE]], metadata !"_ZTS2S1"), !nosanitize [[META5:![0-9]+]]
+// PRESERVE_MIN-NEXT: [[TMP2:%.*]] = call i1 @llvm.type.test(ptr [[VTABLE]], metadata !"all-vtables"), !nosanitize [[META5]]
+// PRESERVE_MIN-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[HANDLER_CFI_CHECK_FAIL:.*]], !prof [[PROF6:![0-9]+]], !nosanitize [[META5]]
+// PRESERVE_MIN: [[HANDLER_CFI_CHECK_FAIL]]:
+// PRESERVE_MIN-NEXT: call preserve_allcc void @__ubsan_handle_cfi_check_fail_minimal_preserve() #[[ATTR3:[0-9]+]], !nosanitize [[META5]]
+// PRESERVE_MIN-NEXT: br label %[[CONT]], !nosanitize [[META5]]
+// PRESERVE_MIN: [[CONT]]:
+// PRESERVE_MIN-NEXT: [[VFN:%.*]] = getelementptr inbounds ptr, ptr [[VTABLE]], i64 0
+// PRESERVE_MIN-NEXT: [[TMP3:%.*]] = load ptr, ptr [[VFN]], align 8
+// PRESERVE_MIN-NEXT: call void [[TMP3]](ptr noundef nonnull align 8 dereferenceable(8) [[TMP0]])
+// PRESERVE_MIN-NEXT: ret void
+//
void s1f(S1 *s1) {
s1->f();
}
@@ -130,3 +151,6 @@ void s1f(S1 *s1) {
// RECOVER_MIN: [[META5]] = !{}
// RECOVER_MIN: [[PROF6]] = !{!"branch_weights", i32 1048575, i32 1}
//.
+// PRESERVE_MIN: [[META5]] = !{}
+// PRESERVE_MIN: [[PROF6]] = !{!"branch_weights", i32 1048575, i32 1}
+//.
diff --git a/compiler-rt/test/ubsan_minimal/TestCases/override-callback.c b/compiler-rt/test/ubsan_minimal/TestCases/override-callback.c
index aaed134b3ae81..45e2980855134 100644
--- a/compiler-rt/test/ubsan_minimal/TestCases/override-callback.c
+++ b/compiler-rt/test/ubsan_minimal/TestCases/override-callback.c
@@ -1,4 +1,5 @@
// RUN: %clang_min_runtime -fsanitize=implicit-integer-sign-change %s -o %t && %run %t 2>&1 | FileCheck %s
+// RUN: %clang_min_runtime -fsanitize=implicit-integer-sign-change -fsanitize-preserve-runtime %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=PRESERVE
// RUN: %clang_min_runtime -fsanitize=implicit-integer-sign-change -fno-sanitize-recover=all %s -o %t && not --crash %run %t 2>&1 | FileCheck %s
// RUN: %clang_min_runtime -fsanitize=implicit-integer-sign-change -fno-sanitize-recover=all -DOVERRIDE=1 %s -o %t && not --crash %run %t 2>&1 | FileCheck %s --check-prefixes=FATAL
@@ -12,6 +13,11 @@ void __ubsan_report_error(const char *kind, uintptr_t caller) {
fprintf(stderr, "CUSTOM_CALLBACK: %s\n", kind);
}
+[[clang::preserve_all]] void __ubsan_report_error_preserve(const char *kind,
+ uintptr_t caller) {
+ fprintf(stderr, "CUSTOM_CALLBACK_PRESERVE: %s\n", kind);
+}
+
#if OVERRIDE
void __ubsan_report_error_fatal(const char *kind, uintptr_t caller) {
fprintf(stderr, "FATAL_CALLBACK: %s\n", kind);
@@ -21,5 +27,6 @@ void __ubsan_report_error_fatal(const char *kind, uintptr_t caller) {
int main(int argc, const char **argv) {
int32_t t0 = (~((uint32_t)0));
// CHECK: CUSTOM_CALLBACK: implicit-conversion
+ // PRESERVE: CUSTOM_CALLBACK_PRESERVE: implicit-conversion
// FATAL: FATAL_CALLBACK: implicit-conversion
}
diff --git a/llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h b/llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h
index 8e7df5e6b10f0..2f5aacf2825f6 100644
--- a/llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h
+++ b/llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h
@@ -23,10 +23,11 @@ class BoundsCheckingPass : public PassInfoMixin<BoundsCheckingPass> {
public:
struct Options {
struct Runtime {
- Runtime(bool MinRuntime, bool MayReturn)
- : MinRuntime(MinRuntime), MayReturn(MayReturn) {}
+ Runtime(bool MinRuntime, bool MayReturn, bool PreserveRt)
+ : MinRuntime(MinRuntime), MayReturn(MayReturn), PreserveRt(PreserveRt) {}
bool MinRuntime;
bool MayReturn;
+ bool PreserveRt;
};
std::optional<Runtime> Rt; // Trap if empty.
bool Merge = false;
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 0d190ea448931..b079fd679f66d 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -1590,21 +1590,31 @@ parseBoundsCheckingOptions(StringRef Params) {
Options.Rt = {
/*MinRuntime=*/false,
/*MayReturn=*/true,
+ /*PreserveRt=*/false,
};
} else if (ParamName == "rt-abort") {
Options.Rt = {
/*MinRuntime=*/false,
/*MayReturn=*/false,
+ /*PreserveRt=*/false,
};
} else if (ParamName == "min-rt") {
Options.Rt = {
/*MinRuntime=*/true,
/*MayReturn=*/true,
+ /*PreserveRt=*/false,
+ };
+ } else if (ParamName == "min-rt-preserve") {
+ Options.Rt = {
+ /*MinRuntime=*/true,
+ /*MayReturn=*/true,
+ /*PreserveRt=*/true,
};
} else if (ParamName == "min-rt-abort") {
Options.Rt = {
/*MinRuntime=*/true,
/*MayReturn=*/false,
+ /*PreserveRt=*/false,
};
} else if (ParamName == "merge") {
Options.Merge = true;
diff --git a/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp b/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp
index 9239ae8741afb..21d502ad95fab 100644
--- a/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp
+++ b/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp
@@ -174,10 +174,13 @@ static void insertBoundsCheck(Value *Or, BuilderTy &IRB, GetTrapBBT GetTrapBB) {
static std::string
getRuntimeCallName(const BoundsCheckingPass::Options::Runtime &Opts) {
std::string Name = "__ubsan_handle_local_out_of_bounds";
- if (Opts.MinRuntime)
+ if (Opts.MinRuntime) {
Name += "_minimal";
+ }
if (!Opts.MayReturn)
Name += "_abort";
+ if (Opts.PreserveRt && Opts.MinRuntime)
+ Name += "_preserve";
return Name;
}
diff --git a/llvm/test/Instrumentation/BoundsChecking/runtimes.ll b/llvm/test/Instrumentation/BoundsChecking/runtimes.ll
index 84dd51cd3fa28..ebfdf5b79fba9 100644
--- a/llvm/test/Instrumentation/BoundsChecking/runtimes.ll
+++ b/llvm/test/Instrumentation/BoundsChecking/runtimes.ll
@@ -111,6 +111,22 @@ define void @f1(i64 %x) nounwind {
; MINRT-NOMERGE-NEXT: call void @__ubsan_handle_local_out_of_bounds_minimal() #[[ATTR1:[0-9]+]], !nosanitize [[META0]]
; MINRT-NOMERGE-NEXT: br label %[[BB7]], !nosanitize [[META0]]
;
+; MINRT-PRESERVE-NOMERGE-LABEL: define void @f1(
+; MINRT-PRESERVE-NOMERGE-SAME: i64 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; MINRT-PRESERVE-NOMERGE-NEXT: [[TMP1:%.*]] = mul i64 16, [[X]]
+; MINRT-PRESERVE-NOMERGE-NEXT: [[TMP2:%.*]] = alloca i128, i64 [[X]], align 8
+; MINRT-PRESERVE-NOMERGE-NEXT: [[TMP3:%.*]] = sub i64 [[TMP1]], 0, !nosanitize [[META0:![0-9]+]]
+; MINRT-PRESERVE-NOMERGE-NEXT: [[TMP4:%.*]] = icmp ult i64 [[TMP3]], 16, !nosanitize [[META0]]
+; MINRT-PRESERVE-NOMERGE-NEXT: [[TMP5:%.*]] = or i1 false, [[TMP4]], !nosanitize [[META0]]
+; MINRT-PRESERVE-NOMERGE-NEXT: [[TMP6:%.*]] = or i1 false, [[TMP5]], !nosanitize [[META0]]
+; MINRT-PRESERVE-NOMERGE-NEXT: br i1 [[TMP6]], label %[[TRAP:.*]], label %[[BB7:.*]]
+; MINRT-PRESERVE-NOMERGE: [[BB7]]:
+; MINRT-PRESERVE-NOMERGE-NEXT: [[TMP8:%.*]] = load i128, ptr [[TMP2]], align 4
+; MINRT-PRESERVE-NOMERGE-NEXT: ret void
+; MINRT-PRESERVE-NOMERGE: [[TRAP]]:
+; MINRT-PRESERVE-NOMERGE-NEXT: call void @__ubsan_handle_local_out_of_bounds_minimal_preserve() #[[ATTR1:[0-9]+]], !nosanitize [[META0]]
+; MINRT-PRESERVE-NOMERGE-NEXT: br label %[[BB7]], !nosanitize [[META0]]
+;
; MINRTABORT-NOMERGE-LABEL: define void @f1(
; MINRTABORT-NOMERGE-SAME: i64 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
; MINRTABORT-NOMERGE-NEXT: [[TMP1:%.*]] = mul i64 16, [[X]]
|
|
@llvm/pr-subscribers-llvm-transforms Author: Florian Mayer (fmayer) ChangesFull diff: https://github.com/llvm/llvm-project/pull/168645.diff 9 Files Affected:
diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 6f63e6470270e..a243b2e222716 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -1134,6 +1134,8 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
CodeGenOpts.SanitizeMinimalRuntime),
/*MayReturn=*/
CodeGenOpts.SanitizeRecover.has(SanitizerKind::LocalBounds),
+ /*PreserveRt=*/static_cast<bool>(
+ CodeGenOpts.SanitizePreserveRuntime),
};
}
FPM.addPass(BoundsCheckingPass(Options));
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index a837f00732748..9f58c0a77e526 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -3789,6 +3789,7 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
bool NeedsAbortSuffix =
IsFatal && RecoverKind != CheckRecoverableKind::Unrecoverable;
bool MinimalRuntime = CGF.CGM.getCodeGenOpts().SanitizeMinimalRuntime;
+ bool PreserveRuntime = CGF.CGM.getCodeGenOpts().SanitizePreserveRuntime;
const SanitizerHandlerInfo &CheckInfo = SanitizerHandlers[CheckHandler];
const StringRef CheckName = CheckInfo.Name;
std::string FnName = "__ubsan_handle_" + CheckName.str();
@@ -3798,6 +3799,8 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
FnName += "_minimal";
if (NeedsAbortSuffix)
FnName += "_abort";
+ else if (MinimalRuntime && PreserveRuntime)
+ FnName += "_preserve";
bool MayReturn =
!IsFatal || RecoverKind == CheckRecoverableKind::AlwaysRecoverable;
@@ -3818,6 +3821,10 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
(CGF.CurCodeDecl && CGF.CurCodeDecl->hasAttr<OptimizeNoneAttr>());
if (NoMerge)
HandlerCall->addFnAttr(llvm::Attribute::NoMerge);
+ if (MinimalRuntime && PreserveRuntime) {
+ // N.B. there is also a clang::CallingConv which is not what we want here.
+ HandlerCall->setCallingConv(llvm::CallingConv::PreserveAll);
+ }
if (!MayReturn) {
HandlerCall->setDoesNotReturn();
CGF.Builder.CreateUnreachable();
diff --git a/clang/test/CodeGen/cfi-icall-trap-recover-runtime.c b/clang/test/CodeGen/cfi-icall-trap-recover-runtime.c
index 117672a9d4368..33be31e09bb56 100644
--- a/clang/test/CodeGen/cfi-icall-trap-recover-runtime.c
+++ b/clang/test/CodeGen/cfi-icall-trap-recover-runtime.c
@@ -9,6 +9,9 @@
// RUN: %clang_cc1 -fsanitize=cfi-icall -fno-sanitize-trap=cfi-icall -fsanitize-recover=cfi-icall -fsanitize-minimal-runtime -flto -fvisibility=hidden -triple x86_64-unknown-linux -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=RECOVER_MIN %s
+// RUN: %clang_cc1 -fsanitize=cfi-icall -fno-sanitize-trap=cfi-icall -fsanitize-recover=cfi-icall -fsanitize-minimal-runtime -fsanitize-preserve-runtime -flto -fvisibility=hidden -triple x86_64-unknown-linux -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=PRESERVE_MIN %s
+
+
// TRAP-LABEL: define hidden void @f(
// TRAP-SAME: ) #[[ATTR0:[0-9]+]] !type [[META6:![0-9]+]] !type [[META7:![0-9]+]] {
// TRAP-NEXT: [[ENTRY:.*:]]
@@ -34,6 +37,11 @@
// RECOVER_MIN-NEXT: [[ENTRY:.*:]]
// RECOVER_MIN-NEXT: ret void
//
+// PRESERVE_MIN-LABEL: define hidden void @f(
+// PRESERVE_MIN-SAME: ) #[[ATTR0:[0-9]+]] !type [[META6:![0-9]+]] !type [[META7:![0-9]+]] {
+// PRESERVE_MIN-NEXT: [[ENTRY:.*:]]
+// PRESERVE_MIN-NEXT: ret void
+//
void f() {
}
@@ -146,6 +154,27 @@ void xf();
// RECOVER_MIN-NEXT: call void (...) [[TMP2]]()
// RECOVER_MIN-NEXT: ret void
//
+// PRESERVE_MIN-LABEL: define hidden void @g(
+// PRESERVE_MIN-SAME: i32 noundef [[B:%.*]]) #[[ATTR0]] !type [[META8:![0-9]+]] !type [[META9:![0-9]+]] {
+// PRESERVE_MIN-NEXT: [[ENTRY:.*:]]
+// PRESERVE_MIN-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4
+// PRESERVE_MIN-NEXT: [[FP:%.*]] = alloca ptr, align 8
+// PRESERVE_MIN-NEXT: store i32 [[B]], ptr [[B_ADDR]], align 4
+// PRESERVE_MIN-NEXT: [[TMP0:%.*]] = load i32, ptr [[B_ADDR]], align 4
+// PRESERVE_MIN-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[TMP0]], 0
+// PRESERVE_MIN-NEXT: [[TMP1:%.*]] = zext i1 [[TOBOOL]] to i64
+// PRESERVE_MIN-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], ptr @f, ptr @xf
+// PRESERVE_MIN-NEXT: store ptr [[COND]], ptr [[FP]], align 8
+// PRESERVE_MIN-NEXT: [[TMP2:%.*]] = load ptr, ptr [[FP]], align 8
+// PRESERVE_MIN-NEXT: [[TMP3:%.*]] = call i1 @llvm.type.test(ptr [[TMP2]], metadata !"_ZTSFvE"), !nosanitize [[META10:![0-9]+]]
+// PRESERVE_MIN-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[HANDLER_CFI_CHECK_FAIL:.*]], !prof [[PROF11:![0-9]+]], !nosanitize [[META10]]
+// PRESERVE_MIN: [[HANDLER_CFI_CHECK_FAIL]]:
+// PRESERVE_MIN-NEXT: call preserve_allcc void @__ubsan_handle_cfi_check_fail_minimal_preserve() #[[ATTR4:[0-9]+]], !nosanitize [[META10]]
+// PRESERVE_MIN-NEXT: br label %[[CONT]], !nosanitize [[META10]]
+// PRESERVE_MIN: [[CONT]]:
+// PRESERVE_MIN-NEXT: call void (...) [[TMP2]]()
+// PRESERVE_MIN-NEXT: ret void
+//
void g(int b) {
void (*fp)() = b ? f : xf;
fp();
@@ -186,3 +215,10 @@ void g(int b) {
// RECOVER_MIN: [[META10]] = !{}
// RECOVER_MIN: [[PROF11]] = !{!"branch_weights", i32 1048575, i32 1}
//.
+// PRESERVE_MIN: [[META6]] = !{i64 0, !"_ZTSFvE"}
+// PRESERVE_MIN: [[META7]] = !{i64 0, !"_ZTSFvE.generalized"}
+// PRESERVE_MIN: [[META8]] = !{i64 0, !"_ZTSFviE"}
+// PRESERVE_MIN: [[META9]] = !{i64 0, !"_ZTSFviE.generalized"}
+// PRESERVE_MIN: [[META10]] = !{}
+// PRESERVE_MIN: [[PROF11]] = !{!"branch_weights", i32 1048575, i32 1}
+//.
diff --git a/clang/test/CodeGenCXX/cfi-vcall-trap-recover-runtime.cpp b/clang/test/CodeGenCXX/cfi-vcall-trap-recover-runtime.cpp
index 3e9328ac0e3ca..dd6a009cb7409 100644
--- a/clang/test/CodeGenCXX/cfi-vcall-trap-recover-runtime.cpp
+++ b/clang/test/CodeGenCXX/cfi-vcall-trap-recover-runtime.cpp
@@ -9,6 +9,8 @@
// RUN: %clang_cc1 -fsanitize=cfi-vcall -fno-sanitize-trap=cfi-vcall -fsanitize-recover=cfi-vcall -fsanitize-minimal-runtime -flto -fvisibility=hidden -triple x86_64-unknown-linux -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=RECOVER_MIN %s
+// RUN: %clang_cc1 -fsanitize=cfi-vcall -fno-sanitize-trap=cfi-vcall -fsanitize-recover=cfi-vcall -fsanitize-minimal-runtime -flto -fvisibility=hidden -triple x86_64-unknown-linux -fwhole-program-vtables -fsanitize-preserve-runtime -emit-llvm -o - %s | FileCheck --check-prefix=PRESERVE_MIN %s
+
struct S1 {
virtual void f();
};
@@ -111,6 +113,25 @@ struct S1 {
// RECOVER_MIN-NEXT: call void [[TMP3]](ptr noundef nonnull align 8 dereferenceable(8) [[TMP0]])
// RECOVER_MIN-NEXT: ret void
//
+// PRESERVE_MIN-LABEL: define hidden void @_Z3s1fP2S1(
+// PRESERVE_MIN-SAME: ptr noundef [[S1:%.*]]) #[[ATTR0:[0-9]+]] {
+// PRESERVE_MIN-NEXT: [[ENTRY:.*:]]
+// PRESERVE_MIN-NEXT: [[S1_ADDR:%.*]] = alloca ptr, align 8
+// PRESERVE_MIN-NEXT: store ptr [[S1]], ptr [[S1_ADDR]], align 8
+// PRESERVE_MIN-NEXT: [[TMP0:%.*]] = load ptr, ptr [[S1_ADDR]], align 8
+// PRESERVE_MIN-NEXT: [[VTABLE:%.*]] = load ptr, ptr [[TMP0]], align 8
+// PRESERVE_MIN-NEXT: [[TMP1:%.*]] = call i1 @llvm.type.test(ptr [[VTABLE]], metadata !"_ZTS2S1"), !nosanitize [[META5:![0-9]+]]
+// PRESERVE_MIN-NEXT: [[TMP2:%.*]] = call i1 @llvm.type.test(ptr [[VTABLE]], metadata !"all-vtables"), !nosanitize [[META5]]
+// PRESERVE_MIN-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[HANDLER_CFI_CHECK_FAIL:.*]], !prof [[PROF6:![0-9]+]], !nosanitize [[META5]]
+// PRESERVE_MIN: [[HANDLER_CFI_CHECK_FAIL]]:
+// PRESERVE_MIN-NEXT: call preserve_allcc void @__ubsan_handle_cfi_check_fail_minimal_preserve() #[[ATTR3:[0-9]+]], !nosanitize [[META5]]
+// PRESERVE_MIN-NEXT: br label %[[CONT]], !nosanitize [[META5]]
+// PRESERVE_MIN: [[CONT]]:
+// PRESERVE_MIN-NEXT: [[VFN:%.*]] = getelementptr inbounds ptr, ptr [[VTABLE]], i64 0
+// PRESERVE_MIN-NEXT: [[TMP3:%.*]] = load ptr, ptr [[VFN]], align 8
+// PRESERVE_MIN-NEXT: call void [[TMP3]](ptr noundef nonnull align 8 dereferenceable(8) [[TMP0]])
+// PRESERVE_MIN-NEXT: ret void
+//
void s1f(S1 *s1) {
s1->f();
}
@@ -130,3 +151,6 @@ void s1f(S1 *s1) {
// RECOVER_MIN: [[META5]] = !{}
// RECOVER_MIN: [[PROF6]] = !{!"branch_weights", i32 1048575, i32 1}
//.
+// PRESERVE_MIN: [[META5]] = !{}
+// PRESERVE_MIN: [[PROF6]] = !{!"branch_weights", i32 1048575, i32 1}
+//.
diff --git a/compiler-rt/test/ubsan_minimal/TestCases/override-callback.c b/compiler-rt/test/ubsan_minimal/TestCases/override-callback.c
index aaed134b3ae81..45e2980855134 100644
--- a/compiler-rt/test/ubsan_minimal/TestCases/override-callback.c
+++ b/compiler-rt/test/ubsan_minimal/TestCases/override-callback.c
@@ -1,4 +1,5 @@
// RUN: %clang_min_runtime -fsanitize=implicit-integer-sign-change %s -o %t && %run %t 2>&1 | FileCheck %s
+// RUN: %clang_min_runtime -fsanitize=implicit-integer-sign-change -fsanitize-preserve-runtime %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=PRESERVE
// RUN: %clang_min_runtime -fsanitize=implicit-integer-sign-change -fno-sanitize-recover=all %s -o %t && not --crash %run %t 2>&1 | FileCheck %s
// RUN: %clang_min_runtime -fsanitize=implicit-integer-sign-change -fno-sanitize-recover=all -DOVERRIDE=1 %s -o %t && not --crash %run %t 2>&1 | FileCheck %s --check-prefixes=FATAL
@@ -12,6 +13,11 @@ void __ubsan_report_error(const char *kind, uintptr_t caller) {
fprintf(stderr, "CUSTOM_CALLBACK: %s\n", kind);
}
+[[clang::preserve_all]] void __ubsan_report_error_preserve(const char *kind,
+ uintptr_t caller) {
+ fprintf(stderr, "CUSTOM_CALLBACK_PRESERVE: %s\n", kind);
+}
+
#if OVERRIDE
void __ubsan_report_error_fatal(const char *kind, uintptr_t caller) {
fprintf(stderr, "FATAL_CALLBACK: %s\n", kind);
@@ -21,5 +27,6 @@ void __ubsan_report_error_fatal(const char *kind, uintptr_t caller) {
int main(int argc, const char **argv) {
int32_t t0 = (~((uint32_t)0));
// CHECK: CUSTOM_CALLBACK: implicit-conversion
+ // PRESERVE: CUSTOM_CALLBACK_PRESERVE: implicit-conversion
// FATAL: FATAL_CALLBACK: implicit-conversion
}
diff --git a/llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h b/llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h
index 8e7df5e6b10f0..2f5aacf2825f6 100644
--- a/llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h
+++ b/llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h
@@ -23,10 +23,11 @@ class BoundsCheckingPass : public PassInfoMixin<BoundsCheckingPass> {
public:
struct Options {
struct Runtime {
- Runtime(bool MinRuntime, bool MayReturn)
- : MinRuntime(MinRuntime), MayReturn(MayReturn) {}
+ Runtime(bool MinRuntime, bool MayReturn, bool PreserveRt)
+ : MinRuntime(MinRuntime), MayReturn(MayReturn), PreserveRt(PreserveRt) {}
bool MinRuntime;
bool MayReturn;
+ bool PreserveRt;
};
std::optional<Runtime> Rt; // Trap if empty.
bool Merge = false;
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 0d190ea448931..b079fd679f66d 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -1590,21 +1590,31 @@ parseBoundsCheckingOptions(StringRef Params) {
Options.Rt = {
/*MinRuntime=*/false,
/*MayReturn=*/true,
+ /*PreserveRt=*/false,
};
} else if (ParamName == "rt-abort") {
Options.Rt = {
/*MinRuntime=*/false,
/*MayReturn=*/false,
+ /*PreserveRt=*/false,
};
} else if (ParamName == "min-rt") {
Options.Rt = {
/*MinRuntime=*/true,
/*MayReturn=*/true,
+ /*PreserveRt=*/false,
+ };
+ } else if (ParamName == "min-rt-preserve") {
+ Options.Rt = {
+ /*MinRuntime=*/true,
+ /*MayReturn=*/true,
+ /*PreserveRt=*/true,
};
} else if (ParamName == "min-rt-abort") {
Options.Rt = {
/*MinRuntime=*/true,
/*MayReturn=*/false,
+ /*PreserveRt=*/false,
};
} else if (ParamName == "merge") {
Options.Merge = true;
diff --git a/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp b/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp
index 9239ae8741afb..21d502ad95fab 100644
--- a/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp
+++ b/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp
@@ -174,10 +174,13 @@ static void insertBoundsCheck(Value *Or, BuilderTy &IRB, GetTrapBBT GetTrapBB) {
static std::string
getRuntimeCallName(const BoundsCheckingPass::Options::Runtime &Opts) {
std::string Name = "__ubsan_handle_local_out_of_bounds";
- if (Opts.MinRuntime)
+ if (Opts.MinRuntime) {
Name += "_minimal";
+ }
if (!Opts.MayReturn)
Name += "_abort";
+ if (Opts.PreserveRt && Opts.MinRuntime)
+ Name += "_preserve";
return Name;
}
diff --git a/llvm/test/Instrumentation/BoundsChecking/runtimes.ll b/llvm/test/Instrumentation/BoundsChecking/runtimes.ll
index 84dd51cd3fa28..ebfdf5b79fba9 100644
--- a/llvm/test/Instrumentation/BoundsChecking/runtimes.ll
+++ b/llvm/test/Instrumentation/BoundsChecking/runtimes.ll
@@ -111,6 +111,22 @@ define void @f1(i64 %x) nounwind {
; MINRT-NOMERGE-NEXT: call void @__ubsan_handle_local_out_of_bounds_minimal() #[[ATTR1:[0-9]+]], !nosanitize [[META0]]
; MINRT-NOMERGE-NEXT: br label %[[BB7]], !nosanitize [[META0]]
;
+; MINRT-PRESERVE-NOMERGE-LABEL: define void @f1(
+; MINRT-PRESERVE-NOMERGE-SAME: i64 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; MINRT-PRESERVE-NOMERGE-NEXT: [[TMP1:%.*]] = mul i64 16, [[X]]
+; MINRT-PRESERVE-NOMERGE-NEXT: [[TMP2:%.*]] = alloca i128, i64 [[X]], align 8
+; MINRT-PRESERVE-NOMERGE-NEXT: [[TMP3:%.*]] = sub i64 [[TMP1]], 0, !nosanitize [[META0:![0-9]+]]
+; MINRT-PRESERVE-NOMERGE-NEXT: [[TMP4:%.*]] = icmp ult i64 [[TMP3]], 16, !nosanitize [[META0]]
+; MINRT-PRESERVE-NOMERGE-NEXT: [[TMP5:%.*]] = or i1 false, [[TMP4]], !nosanitize [[META0]]
+; MINRT-PRESERVE-NOMERGE-NEXT: [[TMP6:%.*]] = or i1 false, [[TMP5]], !nosanitize [[META0]]
+; MINRT-PRESERVE-NOMERGE-NEXT: br i1 [[TMP6]], label %[[TRAP:.*]], label %[[BB7:.*]]
+; MINRT-PRESERVE-NOMERGE: [[BB7]]:
+; MINRT-PRESERVE-NOMERGE-NEXT: [[TMP8:%.*]] = load i128, ptr [[TMP2]], align 4
+; MINRT-PRESERVE-NOMERGE-NEXT: ret void
+; MINRT-PRESERVE-NOMERGE: [[TRAP]]:
+; MINRT-PRESERVE-NOMERGE-NEXT: call void @__ubsan_handle_local_out_of_bounds_minimal_preserve() #[[ATTR1:[0-9]+]], !nosanitize [[META0]]
+; MINRT-PRESERVE-NOMERGE-NEXT: br label %[[BB7]], !nosanitize [[META0]]
+;
; MINRTABORT-NOMERGE-LABEL: define void @f1(
; MINRTABORT-NOMERGE-SAME: i64 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
; MINRTABORT-NOMERGE-NEXT: [[TMP1:%.*]] = mul i64 16, [[X]]
|
|
@llvm/pr-subscribers-compiler-rt-sanitizer Author: Florian Mayer (fmayer) ChangesFull diff: https://github.com/llvm/llvm-project/pull/168645.diff 9 Files Affected:
diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 6f63e6470270e..a243b2e222716 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -1134,6 +1134,8 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
CodeGenOpts.SanitizeMinimalRuntime),
/*MayReturn=*/
CodeGenOpts.SanitizeRecover.has(SanitizerKind::LocalBounds),
+ /*PreserveRt=*/static_cast<bool>(
+ CodeGenOpts.SanitizePreserveRuntime),
};
}
FPM.addPass(BoundsCheckingPass(Options));
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index a837f00732748..9f58c0a77e526 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -3789,6 +3789,7 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
bool NeedsAbortSuffix =
IsFatal && RecoverKind != CheckRecoverableKind::Unrecoverable;
bool MinimalRuntime = CGF.CGM.getCodeGenOpts().SanitizeMinimalRuntime;
+ bool PreserveRuntime = CGF.CGM.getCodeGenOpts().SanitizePreserveRuntime;
const SanitizerHandlerInfo &CheckInfo = SanitizerHandlers[CheckHandler];
const StringRef CheckName = CheckInfo.Name;
std::string FnName = "__ubsan_handle_" + CheckName.str();
@@ -3798,6 +3799,8 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
FnName += "_minimal";
if (NeedsAbortSuffix)
FnName += "_abort";
+ else if (MinimalRuntime && PreserveRuntime)
+ FnName += "_preserve";
bool MayReturn =
!IsFatal || RecoverKind == CheckRecoverableKind::AlwaysRecoverable;
@@ -3818,6 +3821,10 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
(CGF.CurCodeDecl && CGF.CurCodeDecl->hasAttr<OptimizeNoneAttr>());
if (NoMerge)
HandlerCall->addFnAttr(llvm::Attribute::NoMerge);
+ if (MinimalRuntime && PreserveRuntime) {
+ // N.B. there is also a clang::CallingConv which is not what we want here.
+ HandlerCall->setCallingConv(llvm::CallingConv::PreserveAll);
+ }
if (!MayReturn) {
HandlerCall->setDoesNotReturn();
CGF.Builder.CreateUnreachable();
diff --git a/clang/test/CodeGen/cfi-icall-trap-recover-runtime.c b/clang/test/CodeGen/cfi-icall-trap-recover-runtime.c
index 117672a9d4368..33be31e09bb56 100644
--- a/clang/test/CodeGen/cfi-icall-trap-recover-runtime.c
+++ b/clang/test/CodeGen/cfi-icall-trap-recover-runtime.c
@@ -9,6 +9,9 @@
// RUN: %clang_cc1 -fsanitize=cfi-icall -fno-sanitize-trap=cfi-icall -fsanitize-recover=cfi-icall -fsanitize-minimal-runtime -flto -fvisibility=hidden -triple x86_64-unknown-linux -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=RECOVER_MIN %s
+// RUN: %clang_cc1 -fsanitize=cfi-icall -fno-sanitize-trap=cfi-icall -fsanitize-recover=cfi-icall -fsanitize-minimal-runtime -fsanitize-preserve-runtime -flto -fvisibility=hidden -triple x86_64-unknown-linux -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=PRESERVE_MIN %s
+
+
// TRAP-LABEL: define hidden void @f(
// TRAP-SAME: ) #[[ATTR0:[0-9]+]] !type [[META6:![0-9]+]] !type [[META7:![0-9]+]] {
// TRAP-NEXT: [[ENTRY:.*:]]
@@ -34,6 +37,11 @@
// RECOVER_MIN-NEXT: [[ENTRY:.*:]]
// RECOVER_MIN-NEXT: ret void
//
+// PRESERVE_MIN-LABEL: define hidden void @f(
+// PRESERVE_MIN-SAME: ) #[[ATTR0:[0-9]+]] !type [[META6:![0-9]+]] !type [[META7:![0-9]+]] {
+// PRESERVE_MIN-NEXT: [[ENTRY:.*:]]
+// PRESERVE_MIN-NEXT: ret void
+//
void f() {
}
@@ -146,6 +154,27 @@ void xf();
// RECOVER_MIN-NEXT: call void (...) [[TMP2]]()
// RECOVER_MIN-NEXT: ret void
//
+// PRESERVE_MIN-LABEL: define hidden void @g(
+// PRESERVE_MIN-SAME: i32 noundef [[B:%.*]]) #[[ATTR0]] !type [[META8:![0-9]+]] !type [[META9:![0-9]+]] {
+// PRESERVE_MIN-NEXT: [[ENTRY:.*:]]
+// PRESERVE_MIN-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4
+// PRESERVE_MIN-NEXT: [[FP:%.*]] = alloca ptr, align 8
+// PRESERVE_MIN-NEXT: store i32 [[B]], ptr [[B_ADDR]], align 4
+// PRESERVE_MIN-NEXT: [[TMP0:%.*]] = load i32, ptr [[B_ADDR]], align 4
+// PRESERVE_MIN-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[TMP0]], 0
+// PRESERVE_MIN-NEXT: [[TMP1:%.*]] = zext i1 [[TOBOOL]] to i64
+// PRESERVE_MIN-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], ptr @f, ptr @xf
+// PRESERVE_MIN-NEXT: store ptr [[COND]], ptr [[FP]], align 8
+// PRESERVE_MIN-NEXT: [[TMP2:%.*]] = load ptr, ptr [[FP]], align 8
+// PRESERVE_MIN-NEXT: [[TMP3:%.*]] = call i1 @llvm.type.test(ptr [[TMP2]], metadata !"_ZTSFvE"), !nosanitize [[META10:![0-9]+]]
+// PRESERVE_MIN-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[HANDLER_CFI_CHECK_FAIL:.*]], !prof [[PROF11:![0-9]+]], !nosanitize [[META10]]
+// PRESERVE_MIN: [[HANDLER_CFI_CHECK_FAIL]]:
+// PRESERVE_MIN-NEXT: call preserve_allcc void @__ubsan_handle_cfi_check_fail_minimal_preserve() #[[ATTR4:[0-9]+]], !nosanitize [[META10]]
+// PRESERVE_MIN-NEXT: br label %[[CONT]], !nosanitize [[META10]]
+// PRESERVE_MIN: [[CONT]]:
+// PRESERVE_MIN-NEXT: call void (...) [[TMP2]]()
+// PRESERVE_MIN-NEXT: ret void
+//
void g(int b) {
void (*fp)() = b ? f : xf;
fp();
@@ -186,3 +215,10 @@ void g(int b) {
// RECOVER_MIN: [[META10]] = !{}
// RECOVER_MIN: [[PROF11]] = !{!"branch_weights", i32 1048575, i32 1}
//.
+// PRESERVE_MIN: [[META6]] = !{i64 0, !"_ZTSFvE"}
+// PRESERVE_MIN: [[META7]] = !{i64 0, !"_ZTSFvE.generalized"}
+// PRESERVE_MIN: [[META8]] = !{i64 0, !"_ZTSFviE"}
+// PRESERVE_MIN: [[META9]] = !{i64 0, !"_ZTSFviE.generalized"}
+// PRESERVE_MIN: [[META10]] = !{}
+// PRESERVE_MIN: [[PROF11]] = !{!"branch_weights", i32 1048575, i32 1}
+//.
diff --git a/clang/test/CodeGenCXX/cfi-vcall-trap-recover-runtime.cpp b/clang/test/CodeGenCXX/cfi-vcall-trap-recover-runtime.cpp
index 3e9328ac0e3ca..dd6a009cb7409 100644
--- a/clang/test/CodeGenCXX/cfi-vcall-trap-recover-runtime.cpp
+++ b/clang/test/CodeGenCXX/cfi-vcall-trap-recover-runtime.cpp
@@ -9,6 +9,8 @@
// RUN: %clang_cc1 -fsanitize=cfi-vcall -fno-sanitize-trap=cfi-vcall -fsanitize-recover=cfi-vcall -fsanitize-minimal-runtime -flto -fvisibility=hidden -triple x86_64-unknown-linux -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=RECOVER_MIN %s
+// RUN: %clang_cc1 -fsanitize=cfi-vcall -fno-sanitize-trap=cfi-vcall -fsanitize-recover=cfi-vcall -fsanitize-minimal-runtime -flto -fvisibility=hidden -triple x86_64-unknown-linux -fwhole-program-vtables -fsanitize-preserve-runtime -emit-llvm -o - %s | FileCheck --check-prefix=PRESERVE_MIN %s
+
struct S1 {
virtual void f();
};
@@ -111,6 +113,25 @@ struct S1 {
// RECOVER_MIN-NEXT: call void [[TMP3]](ptr noundef nonnull align 8 dereferenceable(8) [[TMP0]])
// RECOVER_MIN-NEXT: ret void
//
+// PRESERVE_MIN-LABEL: define hidden void @_Z3s1fP2S1(
+// PRESERVE_MIN-SAME: ptr noundef [[S1:%.*]]) #[[ATTR0:[0-9]+]] {
+// PRESERVE_MIN-NEXT: [[ENTRY:.*:]]
+// PRESERVE_MIN-NEXT: [[S1_ADDR:%.*]] = alloca ptr, align 8
+// PRESERVE_MIN-NEXT: store ptr [[S1]], ptr [[S1_ADDR]], align 8
+// PRESERVE_MIN-NEXT: [[TMP0:%.*]] = load ptr, ptr [[S1_ADDR]], align 8
+// PRESERVE_MIN-NEXT: [[VTABLE:%.*]] = load ptr, ptr [[TMP0]], align 8
+// PRESERVE_MIN-NEXT: [[TMP1:%.*]] = call i1 @llvm.type.test(ptr [[VTABLE]], metadata !"_ZTS2S1"), !nosanitize [[META5:![0-9]+]]
+// PRESERVE_MIN-NEXT: [[TMP2:%.*]] = call i1 @llvm.type.test(ptr [[VTABLE]], metadata !"all-vtables"), !nosanitize [[META5]]
+// PRESERVE_MIN-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[HANDLER_CFI_CHECK_FAIL:.*]], !prof [[PROF6:![0-9]+]], !nosanitize [[META5]]
+// PRESERVE_MIN: [[HANDLER_CFI_CHECK_FAIL]]:
+// PRESERVE_MIN-NEXT: call preserve_allcc void @__ubsan_handle_cfi_check_fail_minimal_preserve() #[[ATTR3:[0-9]+]], !nosanitize [[META5]]
+// PRESERVE_MIN-NEXT: br label %[[CONT]], !nosanitize [[META5]]
+// PRESERVE_MIN: [[CONT]]:
+// PRESERVE_MIN-NEXT: [[VFN:%.*]] = getelementptr inbounds ptr, ptr [[VTABLE]], i64 0
+// PRESERVE_MIN-NEXT: [[TMP3:%.*]] = load ptr, ptr [[VFN]], align 8
+// PRESERVE_MIN-NEXT: call void [[TMP3]](ptr noundef nonnull align 8 dereferenceable(8) [[TMP0]])
+// PRESERVE_MIN-NEXT: ret void
+//
void s1f(S1 *s1) {
s1->f();
}
@@ -130,3 +151,6 @@ void s1f(S1 *s1) {
// RECOVER_MIN: [[META5]] = !{}
// RECOVER_MIN: [[PROF6]] = !{!"branch_weights", i32 1048575, i32 1}
//.
+// PRESERVE_MIN: [[META5]] = !{}
+// PRESERVE_MIN: [[PROF6]] = !{!"branch_weights", i32 1048575, i32 1}
+//.
diff --git a/compiler-rt/test/ubsan_minimal/TestCases/override-callback.c b/compiler-rt/test/ubsan_minimal/TestCases/override-callback.c
index aaed134b3ae81..45e2980855134 100644
--- a/compiler-rt/test/ubsan_minimal/TestCases/override-callback.c
+++ b/compiler-rt/test/ubsan_minimal/TestCases/override-callback.c
@@ -1,4 +1,5 @@
// RUN: %clang_min_runtime -fsanitize=implicit-integer-sign-change %s -o %t && %run %t 2>&1 | FileCheck %s
+// RUN: %clang_min_runtime -fsanitize=implicit-integer-sign-change -fsanitize-preserve-runtime %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=PRESERVE
// RUN: %clang_min_runtime -fsanitize=implicit-integer-sign-change -fno-sanitize-recover=all %s -o %t && not --crash %run %t 2>&1 | FileCheck %s
// RUN: %clang_min_runtime -fsanitize=implicit-integer-sign-change -fno-sanitize-recover=all -DOVERRIDE=1 %s -o %t && not --crash %run %t 2>&1 | FileCheck %s --check-prefixes=FATAL
@@ -12,6 +13,11 @@ void __ubsan_report_error(const char *kind, uintptr_t caller) {
fprintf(stderr, "CUSTOM_CALLBACK: %s\n", kind);
}
+[[clang::preserve_all]] void __ubsan_report_error_preserve(const char *kind,
+ uintptr_t caller) {
+ fprintf(stderr, "CUSTOM_CALLBACK_PRESERVE: %s\n", kind);
+}
+
#if OVERRIDE
void __ubsan_report_error_fatal(const char *kind, uintptr_t caller) {
fprintf(stderr, "FATAL_CALLBACK: %s\n", kind);
@@ -21,5 +27,6 @@ void __ubsan_report_error_fatal(const char *kind, uintptr_t caller) {
int main(int argc, const char **argv) {
int32_t t0 = (~((uint32_t)0));
// CHECK: CUSTOM_CALLBACK: implicit-conversion
+ // PRESERVE: CUSTOM_CALLBACK_PRESERVE: implicit-conversion
// FATAL: FATAL_CALLBACK: implicit-conversion
}
diff --git a/llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h b/llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h
index 8e7df5e6b10f0..2f5aacf2825f6 100644
--- a/llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h
+++ b/llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h
@@ -23,10 +23,11 @@ class BoundsCheckingPass : public PassInfoMixin<BoundsCheckingPass> {
public:
struct Options {
struct Runtime {
- Runtime(bool MinRuntime, bool MayReturn)
- : MinRuntime(MinRuntime), MayReturn(MayReturn) {}
+ Runtime(bool MinRuntime, bool MayReturn, bool PreserveRt)
+ : MinRuntime(MinRuntime), MayReturn(MayReturn), PreserveRt(PreserveRt) {}
bool MinRuntime;
bool MayReturn;
+ bool PreserveRt;
};
std::optional<Runtime> Rt; // Trap if empty.
bool Merge = false;
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 0d190ea448931..b079fd679f66d 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -1590,21 +1590,31 @@ parseBoundsCheckingOptions(StringRef Params) {
Options.Rt = {
/*MinRuntime=*/false,
/*MayReturn=*/true,
+ /*PreserveRt=*/false,
};
} else if (ParamName == "rt-abort") {
Options.Rt = {
/*MinRuntime=*/false,
/*MayReturn=*/false,
+ /*PreserveRt=*/false,
};
} else if (ParamName == "min-rt") {
Options.Rt = {
/*MinRuntime=*/true,
/*MayReturn=*/true,
+ /*PreserveRt=*/false,
+ };
+ } else if (ParamName == "min-rt-preserve") {
+ Options.Rt = {
+ /*MinRuntime=*/true,
+ /*MayReturn=*/true,
+ /*PreserveRt=*/true,
};
} else if (ParamName == "min-rt-abort") {
Options.Rt = {
/*MinRuntime=*/true,
/*MayReturn=*/false,
+ /*PreserveRt=*/false,
};
} else if (ParamName == "merge") {
Options.Merge = true;
diff --git a/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp b/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp
index 9239ae8741afb..21d502ad95fab 100644
--- a/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp
+++ b/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp
@@ -174,10 +174,13 @@ static void insertBoundsCheck(Value *Or, BuilderTy &IRB, GetTrapBBT GetTrapBB) {
static std::string
getRuntimeCallName(const BoundsCheckingPass::Options::Runtime &Opts) {
std::string Name = "__ubsan_handle_local_out_of_bounds";
- if (Opts.MinRuntime)
+ if (Opts.MinRuntime) {
Name += "_minimal";
+ }
if (!Opts.MayReturn)
Name += "_abort";
+ if (Opts.PreserveRt && Opts.MinRuntime)
+ Name += "_preserve";
return Name;
}
diff --git a/llvm/test/Instrumentation/BoundsChecking/runtimes.ll b/llvm/test/Instrumentation/BoundsChecking/runtimes.ll
index 84dd51cd3fa28..ebfdf5b79fba9 100644
--- a/llvm/test/Instrumentation/BoundsChecking/runtimes.ll
+++ b/llvm/test/Instrumentation/BoundsChecking/runtimes.ll
@@ -111,6 +111,22 @@ define void @f1(i64 %x) nounwind {
; MINRT-NOMERGE-NEXT: call void @__ubsan_handle_local_out_of_bounds_minimal() #[[ATTR1:[0-9]+]], !nosanitize [[META0]]
; MINRT-NOMERGE-NEXT: br label %[[BB7]], !nosanitize [[META0]]
;
+; MINRT-PRESERVE-NOMERGE-LABEL: define void @f1(
+; MINRT-PRESERVE-NOMERGE-SAME: i64 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; MINRT-PRESERVE-NOMERGE-NEXT: [[TMP1:%.*]] = mul i64 16, [[X]]
+; MINRT-PRESERVE-NOMERGE-NEXT: [[TMP2:%.*]] = alloca i128, i64 [[X]], align 8
+; MINRT-PRESERVE-NOMERGE-NEXT: [[TMP3:%.*]] = sub i64 [[TMP1]], 0, !nosanitize [[META0:![0-9]+]]
+; MINRT-PRESERVE-NOMERGE-NEXT: [[TMP4:%.*]] = icmp ult i64 [[TMP3]], 16, !nosanitize [[META0]]
+; MINRT-PRESERVE-NOMERGE-NEXT: [[TMP5:%.*]] = or i1 false, [[TMP4]], !nosanitize [[META0]]
+; MINRT-PRESERVE-NOMERGE-NEXT: [[TMP6:%.*]] = or i1 false, [[TMP5]], !nosanitize [[META0]]
+; MINRT-PRESERVE-NOMERGE-NEXT: br i1 [[TMP6]], label %[[TRAP:.*]], label %[[BB7:.*]]
+; MINRT-PRESERVE-NOMERGE: [[BB7]]:
+; MINRT-PRESERVE-NOMERGE-NEXT: [[TMP8:%.*]] = load i128, ptr [[TMP2]], align 4
+; MINRT-PRESERVE-NOMERGE-NEXT: ret void
+; MINRT-PRESERVE-NOMERGE: [[TRAP]]:
+; MINRT-PRESERVE-NOMERGE-NEXT: call void @__ubsan_handle_local_out_of_bounds_minimal_preserve() #[[ATTR1:[0-9]+]], !nosanitize [[META0]]
+; MINRT-PRESERVE-NOMERGE-NEXT: br label %[[BB7]], !nosanitize [[META0]]
+;
; MINRTABORT-NOMERGE-LABEL: define void @f1(
; MINRTABORT-NOMERGE-SAME: i64 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
; MINRTABORT-NOMERGE-NEXT: [[TMP1:%.*]] = mul i64 16, [[X]]
|
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
Created using spr 1.3.7
Created using spr 1.3.7
Created using spr 1.3.7
Created using spr 1.3.7
|
@vitalybuka PTAL. I updated the tests, and fixed the logic to not apply the preserve_all cc for abort functions (though it shouldn't really matter) |
🐧 Linux x64 Test Results
|
Created using spr 1.3.7
|
@vitalybuka PTAL. I restricted to aarch64 and x86_64 |
clang/lib/CodeGen/CGExpr.cpp
Outdated
| // local-bounds. Make sure to change that too. | ||
| const auto &T = CGF.CGM.getTriple(); | ||
| bool HandlerPreserveAllRegs = | ||
| CGF.CGM.getCodeGenOpts().SanitizeHandlerPreserveAllRegs && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same, can you make sure SanitizeHandlerPreserveAllRegs already set only on supported arches?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but what do we gain from that? it makes the logic harder to follow, because now it is split between codegen and driver. also now the driver and frontend flag have different meanings
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be arch specific logic in codegen
Eg. there are some UBSAN/sanitizer file which flip on/of features deppending on context
your patch is exporting the logic outside that thing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eg. there are some UBSAN/sanitizer file which flip on/of features deppending on context
your patch is exporting the logic outside that thing
Please clarify
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clang/lib/Driver/SanitizerArgs.cpp does some filtering by triple and many other factors
This should go there as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clang/lib/Driver/SanitizerArgs.cpp does some filtering by triple and many other factors
This should go there as well
How does this work if we have some check trapping and some recoverable? Then it depends on which check we are looking at whether we should have preserve-all. I can handle Abort / NoAbort in the CGExpr / BoundsChecking, but then the logic is split
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did that. Lmk what you think
9782413
into
users/fmayer/spr/main.ubsan-use-fsanitize-recover-runtime-in-codegen
|
Actually submitted: e2a29ec |
No description provided.