[clang] [compiler-rt] [ubsan_minimal] Allow UBSan handler from Minimal runtime to acceprt arguments (PR #152192)
Andrew Lazarev via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 5 14:03:23 PDT 2025
https://github.com/alazarev updated https://github.com/llvm/llvm-project/pull/152192
>From adf6d324a6681acb9fc837dd08afd180eaf46306 Mon Sep 17 00:00:00 2001
From: Andrew Lazarev <alazarev at google.com>
Date: Tue, 5 Aug 2025 18:16:00 +0000
Subject: [PATCH 1/3] [ubsan_minimal] Allow UBSan handler from Minimal runtime
to acceprt arguments
Also renamed minimal-specific WEAK_DEF anchor from __ubsan_report_error to __ubsan_minimal_report_error.
A new __ubsan_minimal_report_error accepts address of the variable being inspected.
---
clang/lib/CodeGen/CGExpr.cpp | 78 ++++++++++++++-----
.../ubsan_minimal/ubsan_minimal_handlers.cpp | 46 ++++++++---
2 files changed, 92 insertions(+), 32 deletions(-)
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 5a3d4e447b229..ae7e336e0e119 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -3717,6 +3717,33 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
}
}
+// Adapts the arguments to the handler function.
+// It is expected that {StaticArgs..., DynamicArgs...} sequence matches the
+// corresponding XxxData type from the ubsan_handlers.h file.
+// Minimal hadler can use a subset of the arguments.
+static void
+AdaptArgsToHandler(CodeGenModule &CGM, SanitizerHandler CheckHandler,
+ ArrayRef<llvm::Constant *> StaticArgs,
+ ArrayRef<llvm::Value *> DynamicArgs,
+ SmallVectorImpl<llvm::Constant *> &HandlerStaticArgs,
+ SmallVectorImpl<llvm::Value *> &HandlerDynamicArgs) {
+ if (!CGM.getCodeGenOpts().SanitizeMinimalRuntime) {
+ HandlerStaticArgs.assign(StaticArgs.begin(), StaticArgs.end());
+ HandlerDynamicArgs.assign(DynamicArgs.begin(), DynamicArgs.end());
+ return;
+ }
+
+ switch (CheckHandler) {
+ case SanitizerHandler::TypeMismatch:
+ // Pass value pointer only. It adds minimal overhead.
+ HandlerDynamicArgs.assign(DynamicArgs.begin(), DynamicArgs.end());
+ break;
+ default:
+ // No arguments for other checks.
+ break;
+ }
+}
+
void CodeGenFunction::EmitCheck(
ArrayRef<std::pair<llvm::Value *, SanitizerKind::SanitizerOrdinal>> Checked,
SanitizerHandler CheckHandler, ArrayRef<llvm::Constant *> StaticArgs,
@@ -3794,28 +3821,37 @@ void CodeGenFunction::EmitCheck(
// representing operand values.
SmallVector<llvm::Value *, 4> Args;
SmallVector<llvm::Type *, 4> ArgTypes;
- if (!CGM.getCodeGenOpts().SanitizeMinimalRuntime) {
- Args.reserve(DynamicArgs.size() + 1);
- ArgTypes.reserve(DynamicArgs.size() + 1);
-
- // Emit handler arguments and create handler function type.
- if (!StaticArgs.empty()) {
- llvm::Constant *Info = llvm::ConstantStruct::getAnon(StaticArgs);
- auto *InfoPtr = new llvm::GlobalVariable(
- CGM.getModule(), Info->getType(), false,
- llvm::GlobalVariable::PrivateLinkage, Info, "", nullptr,
- llvm::GlobalVariable::NotThreadLocal,
- CGM.getDataLayout().getDefaultGlobalsAddressSpace());
- InfoPtr->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
- CGM.getSanitizerMetadata()->disableSanitizerForGlobal(InfoPtr);
- Args.push_back(InfoPtr);
- ArgTypes.push_back(Args.back()->getType());
- }
- for (llvm::Value *DynamicArg : DynamicArgs) {
- Args.push_back(EmitCheckValue(DynamicArg));
- ArgTypes.push_back(IntPtrTy);
- }
+ SmallVector<llvm::Constant *, 4> HandlerStaticArgs;
+ SmallVector<llvm::Value *, 4> HandlerDynamicArgs;
+ AdaptArgsToHandler(CGM, CheckHandler, StaticArgs, DynamicArgs,
+ HandlerStaticArgs, HandlerDynamicArgs);
+
+ Args.reserve(HandlerDynamicArgs.size() + 1);
+ ArgTypes.reserve(HandlerDynamicArgs.size() + 1);
+
+ // Emit handler arguments and create handler function type.
+ if (!HandlerStaticArgs.empty()) {
+ llvm::Constant *Info = llvm::ConstantStruct::getAnon(HandlerStaticArgs);
+ auto *InfoPtr = new llvm::GlobalVariable(
+ CGM.getModule(), Info->getType(),
+ // Non-constant global is used in non-minimal handler to deduplicate
+ // reports. Minimal handler stores history statically.
+ // TODO: make it constant for non-minimal handler.
+ /*isConstant=*/
+ CGM.getCodeGenOpts().SanitizeMinimalRuntime,
+ llvm::GlobalVariable::PrivateLinkage, Info, "", nullptr,
+ llvm::GlobalVariable::NotThreadLocal,
+ CGM.getDataLayout().getDefaultGlobalsAddressSpace());
+ InfoPtr->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
+ CGM.getSanitizerMetadata()->disableSanitizerForGlobal(InfoPtr);
+ Args.push_back(InfoPtr);
+ ArgTypes.push_back(Args.back()->getType());
+ }
+
+ for (llvm::Value *DynamicArg : HandlerDynamicArgs) {
+ Args.push_back(EmitCheckValue(DynamicArg));
+ ArgTypes.push_back(IntPtrTy);
}
llvm::FunctionType *FnType =
diff --git a/compiler-rt/lib/ubsan_minimal/ubsan_minimal_handlers.cpp b/compiler-rt/lib/ubsan_minimal/ubsan_minimal_handlers.cpp
index ebc36a8583e05..e3a9d55ca3cbd 100644
--- a/compiler-rt/lib/ubsan_minimal/ubsan_minimal_handlers.cpp
+++ b/compiler-rt/lib/ubsan_minimal/ubsan_minimal_handlers.cpp
@@ -34,20 +34,25 @@ static char *append_hex(uintptr_t d, char *buf, const char *end) {
return buf;
}
-static void format_msg(const char *kind, uintptr_t caller, char *buf,
- const char *end) {
+static void format_msg(const char *kind, uintptr_t caller,
+ const uintptr_t *address, char *buf, const char *end) {
buf = append_str("ubsan: ", buf, end);
buf = append_str(kind, buf, end);
buf = append_str(" by 0x", buf, end);
buf = append_hex(caller, buf, end);
+ if (address) {
+ buf = append_str(" address 0x", buf, end);
+ buf = append_hex(*address, buf, end);
+ }
buf = append_str("\n", buf, end);
if (buf == end)
--buf; // Make sure we don't cause a buffer overflow.
*buf = '\0';
}
-SANITIZER_INTERFACE_WEAK_DEF(void, __ubsan_report_error, const char *kind,
- uintptr_t caller) {
+SANITIZER_INTERFACE_WEAK_DEF(void, __ubsan_minimal_report_error,
+ const char *kind, uintptr_t caller,
+ const uintptr_t *address) {
if (caller == 0)
return;
while (true) {
@@ -80,15 +85,16 @@ SANITIZER_INTERFACE_WEAK_DEF(void, __ubsan_report_error, const char *kind,
__sanitizer::atomic_store_relaxed(&caller_pcs[sz], caller);
char msg_buf[128];
- format_msg(kind, caller, msg_buf, msg_buf + sizeof(msg_buf));
+ format_msg(kind, caller, address, msg_buf, msg_buf + sizeof(msg_buf));
message(msg_buf);
}
}
-SANITIZER_INTERFACE_WEAK_DEF(void, __ubsan_report_error_fatal, const char *kind,
- uintptr_t caller) {
+SANITIZER_INTERFACE_WEAK_DEF(void, __ubsan_minimal_report_error_fatal,
+ const char *kind, uintptr_t caller,
+ const uintptr_t *address) {
// Use another handlers, in case it's already overriden.
- __ubsan_report_error(kind, caller);
+ __ubsan_minimal_report_error(kind, caller, address);
}
#if defined(__ANDROID__)
@@ -121,13 +127,13 @@ void NORETURN CheckFailed(const char *file, int, const char *cond, u64, u64) {
#define HANDLER_RECOVER(name, kind) \
INTERFACE void __ubsan_handle_##name##_minimal() { \
- __ubsan_report_error(kind, GET_CALLER_PC()); \
+ __ubsan_minimal_report_error(kind, GET_CALLER_PC(), nullptr); \
}
#define HANDLER_NORECOVER(name, kind) \
INTERFACE void __ubsan_handle_##name##_minimal_abort() { \
uintptr_t caller = GET_CALLER_PC(); \
- __ubsan_report_error_fatal(kind, caller); \
+ __ubsan_minimal_report_error_fatal(kind, caller, nullptr); \
abort_with_message(kind, caller); \
}
@@ -135,7 +141,25 @@ void NORETURN CheckFailed(const char *file, int, const char *cond, u64, u64) {
HANDLER_RECOVER(name, kind) \
HANDLER_NORECOVER(name, kind)
-HANDLER(type_mismatch, "type-mismatch")
+#define HANDLER_RECOVER_PTR(name, kind) \
+ INTERFACE void __ubsan_handle_##name##_minimal(const uintptr_t address) { \
+ __ubsan_minimal_report_error(kind, GET_CALLER_PC(), &address); \
+ }
+
+#define HANDLER_NORECOVER_PTR(name, kind) \
+ INTERFACE void __ubsan_handle_##name##_minimal_abort( \
+ const uintptr_t address) { \
+ uintptr_t caller = GET_CALLER_PC(); \
+ __ubsan_minimal_report_error_fatal(kind, caller, &address); \
+ abort_with_message(kind, caller); \
+ }
+
+// A version of a handler that takes a pointer to a value.
+#define HANDLER_PTR(name, kind) \
+ HANDLER_RECOVER_PTR(name, kind) \
+ HANDLER_NORECOVER_PTR(name, kind)
+
+HANDLER_PTR(type_mismatch, "type-mismatch")
HANDLER(alignment_assumption, "alignment-assumption")
HANDLER(add_overflow, "add-overflow")
HANDLER(sub_overflow, "sub-overflow")
>From 8d9e3cfa6e7af57dd0ae23835efb2d565681a8b5 Mon Sep 17 00:00:00 2001
From: Andrew Lazarev <alazarev at google.com>
Date: Tue, 5 Aug 2025 19:47:08 +0000
Subject: [PATCH 2/3] Fix and add tests
---
.../test/ubsan_minimal/TestCases/misalignment.cpp | 14 ++++++++++++++
compiler-rt/test/ubsan_minimal/TestCases/null.cpp | 11 +++++++++++
.../ubsan_minimal/TestCases/override-callback.c | 4 ++--
3 files changed, 27 insertions(+), 2 deletions(-)
create mode 100644 compiler-rt/test/ubsan_minimal/TestCases/misalignment.cpp
create mode 100644 compiler-rt/test/ubsan_minimal/TestCases/null.cpp
diff --git a/compiler-rt/test/ubsan_minimal/TestCases/misalignment.cpp b/compiler-rt/test/ubsan_minimal/TestCases/misalignment.cpp
new file mode 100644
index 0000000000000..e5780b5b3e3f0
--- /dev/null
+++ b/compiler-rt/test/ubsan_minimal/TestCases/misalignment.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang_min_runtime -fsanitize=alignment %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
+
+void f(int& n) {}
+
+int* t;
+
+int main() {
+ int r;
+ t = (int*)(((char*)&r) + 1);
+ // CHECK: ubsan: type-mismatch by 0x{{[[:xdigit:]]+}} address 0x{{[[:xdigit:]]+$}}
+ // CHECK-NOT: type-mismatch
+
+ f(*t);
+}
\ No newline at end of file
diff --git a/compiler-rt/test/ubsan_minimal/TestCases/null.cpp b/compiler-rt/test/ubsan_minimal/TestCases/null.cpp
new file mode 100644
index 0000000000000..19bc880bcaa47
--- /dev/null
+++ b/compiler-rt/test/ubsan_minimal/TestCases/null.cpp
@@ -0,0 +1,11 @@
+// RUN: %clang_min_runtime -fsanitize=null %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
+
+void f(int& n) {}
+
+int* t;
+
+int main() {
+ // CHECK: ubsan: type-mismatch by 0x{{[[:xdigit:]]+}} address 0x{{[[:xdigit:]]+$}}
+ // CHECK-NOT: type-mismatch
+ f(*t);
+}
\ No newline at end of file
diff --git a/compiler-rt/test/ubsan_minimal/TestCases/override-callback.c b/compiler-rt/test/ubsan_minimal/TestCases/override-callback.c
index aaed134b3ae81..96fd93d0ed5bf 100644
--- a/compiler-rt/test/ubsan_minimal/TestCases/override-callback.c
+++ b/compiler-rt/test/ubsan_minimal/TestCases/override-callback.c
@@ -8,12 +8,12 @@
static int Result;
-void __ubsan_report_error(const char *kind, uintptr_t caller) {
+void __ubsan_minimal_report_error(const char *kind, uintptr_t caller) {
fprintf(stderr, "CUSTOM_CALLBACK: %s\n", kind);
}
#if OVERRIDE
-void __ubsan_report_error_fatal(const char *kind, uintptr_t caller) {
+void __ubsan_minimal_report_error_fatal(const char *kind, uintptr_t caller) {
fprintf(stderr, "FATAL_CALLBACK: %s\n", kind);
}
#endif
>From e8e84a77d8e297b6cb7e72aac714074332967846 Mon Sep 17 00:00:00 2001
From: Andrew Lazarev <alazarev at google.com>
Date: Tue, 5 Aug 2025 21:01:42 +0000
Subject: [PATCH 3/3] clang-format
---
.../test/ubsan_minimal/TestCases/misalignment.cpp | 14 +++++++-------
compiler-rt/test/ubsan_minimal/TestCases/null.cpp | 10 +++++-----
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/compiler-rt/test/ubsan_minimal/TestCases/misalignment.cpp b/compiler-rt/test/ubsan_minimal/TestCases/misalignment.cpp
index e5780b5b3e3f0..f0b6a7355bc86 100644
--- a/compiler-rt/test/ubsan_minimal/TestCases/misalignment.cpp
+++ b/compiler-rt/test/ubsan_minimal/TestCases/misalignment.cpp
@@ -1,14 +1,14 @@
// RUN: %clang_min_runtime -fsanitize=alignment %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
-void f(int& n) {}
+void f(int &n) {}
-int* t;
+int *t;
int main() {
- int r;
- t = (int*)(((char*)&r) + 1);
- // CHECK: ubsan: type-mismatch by 0x{{[[:xdigit:]]+}} address 0x{{[[:xdigit:]]+$}}
- // CHECK-NOT: type-mismatch
+ int r;
+ t = (int *)(((char *)&r) + 1);
+ // CHECK: ubsan: type-mismatch by 0x{{[[:xdigit:]]+}} address 0x{{[[:xdigit:]]+$}}
+ // CHECK-NOT: type-mismatch
- f(*t);
+ f(*t);
}
\ No newline at end of file
diff --git a/compiler-rt/test/ubsan_minimal/TestCases/null.cpp b/compiler-rt/test/ubsan_minimal/TestCases/null.cpp
index 19bc880bcaa47..73927834cf907 100644
--- a/compiler-rt/test/ubsan_minimal/TestCases/null.cpp
+++ b/compiler-rt/test/ubsan_minimal/TestCases/null.cpp
@@ -1,11 +1,11 @@
// RUN: %clang_min_runtime -fsanitize=null %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
-void f(int& n) {}
+void f(int &n) {}
-int* t;
+int *t;
int main() {
- // CHECK: ubsan: type-mismatch by 0x{{[[:xdigit:]]+}} address 0x{{[[:xdigit:]]+$}}
- // CHECK-NOT: type-mismatch
- f(*t);
+ // CHECK: ubsan: type-mismatch by 0x{{[[:xdigit:]]+}} address 0x{{[[:xdigit:]]+$}}
+ // CHECK-NOT: type-mismatch
+ f(*t);
}
\ No newline at end of file
More information about the llvm-commits
mailing list