[clang] [sanitizer] add pseudofunction to indicate array-bounds check (#128977)

With this, we can:

* use profilers to estimate how many cycles we spend on these checks
(subject to caveats),
* more easily see why we crashed.
This commit is contained in:
Florian Mayer 2025-03-25 18:57:00 -04:00
parent 812efdb094
commit c0952a931c
4 changed files with 56 additions and 24 deletions

View File

@ -1786,8 +1786,8 @@ llvm::DIType *CGDebugInfo::createFieldType(
}
llvm::DISubprogram *
CGDebugInfo::createInlinedTrapSubprogram(StringRef FuncName,
llvm::DIFile *FileScope) {
CGDebugInfo::createInlinedSubprogram(StringRef FuncName,
llvm::DIFile *FileScope) {
// We are caching the subprogram because we don't want to duplicate
// subprograms with the same message. Note that `SPFlagDefinition` prevents
// subprograms from being uniqued.
@ -3614,6 +3614,14 @@ llvm::DIMacroFile *CGDebugInfo::CreateTempMacroFile(llvm::DIMacroFile *Parent,
return DBuilder.createTempMacroFile(Parent, Line, FName);
}
llvm::DILocation *CGDebugInfo::CreateSyntheticInlineAt(llvm::DebugLoc Location,
StringRef FuncName) {
llvm::DISubprogram *SP =
createInlinedSubprogram(FuncName, Location->getFile());
return llvm::DILocation::get(CGM.getLLVMContext(), /*Line=*/0, /*Column=*/0,
/*Scope=*/SP, /*InlinedAt=*/Location);
}
llvm::DILocation *CGDebugInfo::CreateTrapFailureMessageFor(
llvm::DebugLoc TrapLocation, StringRef Category, StringRef FailureMsg) {
// Create a debug location from `TrapLocation` that adds an artificial inline
@ -3625,10 +3633,7 @@ llvm::DILocation *CGDebugInfo::CreateTrapFailureMessageFor(
FuncName += "$";
FuncName += FailureMsg;
llvm::DISubprogram *TrapSP =
createInlinedTrapSubprogram(FuncName, TrapLocation->getFile());
return llvm::DILocation::get(CGM.getLLVMContext(), /*Line=*/0, /*Column=*/0,
/*Scope=*/TrapSP, /*InlinedAt=*/TrapLocation);
return CreateSyntheticInlineAt(TrapLocation, FuncName);
}
static QualType UnwrapTypeForDebugInfo(QualType T, const ASTContext &C) {

View File

@ -359,8 +359,8 @@ class CGDebugInfo {
/// A function that returns the subprogram corresponding to the artificial
/// inlined function for traps.
llvm::DISubprogram *createInlinedTrapSubprogram(StringRef FuncName,
llvm::DIFile *FileScope);
llvm::DISubprogram *createInlinedSubprogram(StringRef FuncName,
llvm::DIFile *FileScope);
/// Helpers for collecting fields of a record.
/// @{
@ -635,6 +635,13 @@ public:
llvm::DILocation *CreateTrapFailureMessageFor(llvm::DebugLoc TrapLocation,
StringRef Category,
StringRef FailureMsg);
/// Create a debug location from `Location` that adds an artificial inline
/// frame where the frame name is FuncName
///
/// This is used to indiciate instructions that come from compiler
/// instrumentation.
llvm::DILocation *CreateSyntheticInlineAt(llvm::DebugLoc Location,
StringRef FuncName);
private:
/// Emit call to llvm.dbg.declare for a variable declaration.

View File

@ -31,6 +31,7 @@
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/CodeGenOptions.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
@ -60,8 +61,14 @@ namespace clang {
llvm::cl::opt<bool> ClSanitizeGuardChecks(
"ubsan-guard-checks", llvm::cl::Optional,
llvm::cl::desc("Guard UBSAN checks with `llvm.allow.ubsan.check()`."));
} // namespace clang
static llvm::cl::opt<bool> ClArrayBoundsPseudoFn(
"array-bounds-pseudofn", llvm::cl::Hidden, llvm::cl::Optional,
llvm::cl::desc("Emit debug info that places array-bounds instrumentation "
"in an inline function called __ubsan_check_array_bounds."));
//===--------------------------------------------------------------------===//
// Defines for metadata
//===--------------------------------------------------------------------===//
@ -1215,6 +1222,13 @@ void CodeGenFunction::EmitBoundsCheckImpl(const Expr *E, llvm::Value *Bound,
SanitizerScope SanScope(this);
llvm::DILocation *CheckDI = Builder.getCurrentDebugLocation();
if (ClArrayBoundsPseudoFn && CheckDI) {
CheckDI = getDebugInfo()->CreateSyntheticInlineAt(
Builder.getCurrentDebugLocation(), "__ubsan_check_array_bounds");
}
ApplyDebugLocation ApplyTrapDI(*this, CheckDI);
bool IndexSigned = IndexType->isSignedIntegerOrEnumerationType();
llvm::Value *IndexVal = Builder.CreateIntCast(Index, SizeTy, IndexSigned);
llvm::Value *BoundVal = Builder.CreateIntCast(Bound, SizeTy, false);

View File

@ -1,6 +1,6 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
// RUN: %clang_cc1 -emit-llvm -fdebug-prefix-map=%S/= -fno-ident -fdebug-compilation-dir=%S -fsanitize=array-bounds -fsanitize-trap=array-bounds -triple x86_64 -debug-info-kind=limited %s -o - | FileCheck --check-prefix=CHECK-TRAP %s
// RUN: %clang_cc1 -emit-llvm -fdebug-prefix-map=%S/= -fno-ident -fdebug-compilation-dir=%S -fsanitize=array-bounds -triple x86_64 -debug-info-kind=limited %s -o - | FileCheck --check-prefix=CHECK-NOTRAP %s
// RUN: %clang_cc1 -mllvm -array-bounds-pseudofn -emit-llvm -fdebug-prefix-map=%S/= -fno-ident -fdebug-compilation-dir=%S -fsanitize=array-bounds -fsanitize-trap=array-bounds -triple x86_64 -debug-info-kind=limited %s -o - | FileCheck --check-prefix=CHECK-TRAP %s
// RUN: %clang_cc1 -mllvm -array-bounds-pseudofn -emit-llvm -fdebug-prefix-map=%S/= -fno-ident -fdebug-compilation-dir=%S -fsanitize=array-bounds -triple x86_64 -debug-info-kind=limited %s -o - | FileCheck --check-prefix=CHECK-NOTRAP %s
int f();
@ -28,10 +28,10 @@ void d(double*);
// CHECK-TRAP-NEXT: call void @llvm.ubsantrap(i8 18) #[[ATTR3:[0-9]+]], !dbg [[DBG23]], !nosanitize [[META10]]
// CHECK-TRAP-NEXT: unreachable, !dbg [[DBG23]], !nosanitize [[META10]]
// CHECK-TRAP: [[CONT]]:
// CHECK-TRAP-NEXT: [[IDXPROM:%.*]] = sext i32 [[CALL]] to i64, !dbg [[DBG23]]
// CHECK-TRAP-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [10 x double], ptr [[A]], i64 0, i64 [[IDXPROM]], !dbg [[DBG23]]
// CHECK-TRAP-NEXT: [[TMP2:%.*]] = load double, ptr [[ARRAYIDX]], align 8, !dbg [[DBG23]]
// CHECK-TRAP-NEXT: ret double [[TMP2]], !dbg [[DBG24:![0-9]+]]
// CHECK-TRAP-NEXT: [[IDXPROM:%.*]] = sext i32 [[CALL]] to i64, !dbg [[DBG26:![0-9]+]]
// CHECK-TRAP-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [10 x double], ptr [[A]], i64 0, i64 [[IDXPROM]], !dbg [[DBG26]]
// CHECK-TRAP-NEXT: [[TMP2:%.*]] = load double, ptr [[ARRAYIDX]], align 8, !dbg [[DBG26]]
// CHECK-TRAP-NEXT: ret double [[TMP2]], !dbg [[DBG27:![0-9]+]]
//
// CHECK-NOTRAP-LABEL: define dso_local double @f1(
// CHECK-NOTRAP-SAME: i32 noundef [[B:%.*]], i32 noundef [[I:%.*]]) #[[ATTR0:[0-9]+]] !dbg [[DBG4:![0-9]+]] {
@ -49,16 +49,16 @@ void d(double*);
// CHECK-NOTRAP-NEXT: [[CALL:%.*]] = call i32 (...) @f(), !dbg [[DBG22:![0-9]+]]
// CHECK-NOTRAP-NEXT: [[TMP0:%.*]] = sext i32 [[CALL]] to i64, !dbg [[DBG23:![0-9]+]], !nosanitize [[META10:![0-9]+]]
// CHECK-NOTRAP-NEXT: [[TMP1:%.*]] = icmp ult i64 [[TMP0]], 10, !dbg [[DBG23]], !nosanitize [[META10]]
// CHECK-NOTRAP-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[HANDLER_OUT_OF_BOUNDS:.*]], !dbg [[DBG23]], !prof [[PROF24:![0-9]+]], !nosanitize [[META10]]
// CHECK-NOTRAP-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[HANDLER_OUT_OF_BOUNDS:.*]], !dbg [[DBG23]], !prof [[PROF27:![0-9]+]], !nosanitize [[META10]]
// CHECK-NOTRAP: [[HANDLER_OUT_OF_BOUNDS]]:
// CHECK-NOTRAP-NEXT: [[TMP2:%.*]] = zext i32 [[CALL]] to i64, !dbg [[DBG23]], !nosanitize [[META10]]
// CHECK-NOTRAP-NEXT: call void @__ubsan_handle_out_of_bounds_abort(ptr @[[GLOB2:[0-9]+]], i64 [[TMP2]]) #[[ATTR3:[0-9]+]], !dbg [[DBG23]], !nosanitize [[META10]]
// CHECK-NOTRAP-NEXT: unreachable, !dbg [[DBG23]], !nosanitize [[META10]]
// CHECK-NOTRAP: [[CONT]]:
// CHECK-NOTRAP-NEXT: [[IDXPROM:%.*]] = sext i32 [[CALL]] to i64, !dbg [[DBG23]]
// CHECK-NOTRAP-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [10 x double], ptr [[A]], i64 0, i64 [[IDXPROM]], !dbg [[DBG23]]
// CHECK-NOTRAP-NEXT: [[TMP3:%.*]] = load double, ptr [[ARRAYIDX]], align 8, !dbg [[DBG23]]
// CHECK-NOTRAP-NEXT: ret double [[TMP3]], !dbg [[DBG25:![0-9]+]]
// CHECK-NOTRAP-NEXT: [[IDXPROM:%.*]] = sext i32 [[CALL]] to i64, !dbg [[DBG26:![0-9]+]]
// CHECK-NOTRAP-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [10 x double], ptr [[A]], i64 0, i64 [[IDXPROM]], !dbg [[DBG26]]
// CHECK-NOTRAP-NEXT: [[TMP3:%.*]] = load double, ptr [[ARRAYIDX]], align 8, !dbg [[DBG26]]
// CHECK-NOTRAP-NEXT: ret double [[TMP3]], !dbg [[DBG28:![0-9]+]]
//
double f1(int b, int i) {
double a[10];
@ -88,8 +88,11 @@ double f1(int b, int i) {
// CHECK-TRAP: [[DBG20]] = !DILocation(line: 65, column: 5, scope: [[DBG4]])
// CHECK-TRAP: [[DBG21]] = !DILocation(line: 65, column: 3, scope: [[DBG4]])
// CHECK-TRAP: [[DBG22]] = !DILocation(line: 66, column: 12, scope: [[DBG4]])
// CHECK-TRAP: [[DBG23]] = !DILocation(line: 66, column: 10, scope: [[DBG4]])
// CHECK-TRAP: [[DBG24]] = !DILocation(line: 66, column: 3, scope: [[DBG4]])
// CHECK-TRAP: [[DBG23]] = !DILocation(line: 0, scope: [[META24:![0-9]+]], inlinedAt: [[DBG26]])
// CHECK-TRAP: [[META24]] = distinct !DISubprogram(name: "__ubsan_check_array_bounds", scope: [[META5]], file: [[META5]], type: [[META25:![0-9]+]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META0]])
// CHECK-TRAP: [[META25]] = !DISubroutineType(types: null)
// CHECK-TRAP: [[DBG26]] = !DILocation(line: 66, column: 10, scope: [[DBG4]])
// CHECK-TRAP: [[DBG27]] = !DILocation(line: 66, column: 3, scope: [[DBG4]])
//.
// CHECK-NOTRAP: [[META0:![0-9]+]] = distinct !DICompileUnit(language: DW_LANG_C11, file: [[META1:![0-9]+]], isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
// CHECK-NOTRAP: [[META1]] = !DIFile(filename: "<stdin>", directory: {{.*}})
@ -112,7 +115,10 @@ double f1(int b, int i) {
// CHECK-NOTRAP: [[DBG20]] = !DILocation(line: 65, column: 5, scope: [[DBG4]])
// CHECK-NOTRAP: [[DBG21]] = !DILocation(line: 65, column: 3, scope: [[DBG4]])
// CHECK-NOTRAP: [[DBG22]] = !DILocation(line: 66, column: 12, scope: [[DBG4]])
// CHECK-NOTRAP: [[DBG23]] = !DILocation(line: 66, column: 10, scope: [[DBG4]])
// CHECK-NOTRAP: [[PROF24]] = !{!"branch_weights", i32 1048575, i32 1}
// CHECK-NOTRAP: [[DBG25]] = !DILocation(line: 66, column: 3, scope: [[DBG4]])
// CHECK-NOTRAP: [[DBG23]] = !DILocation(line: 0, scope: [[META24:![0-9]+]], inlinedAt: [[DBG26]])
// CHECK-NOTRAP: [[META24]] = distinct !DISubprogram(name: "__ubsan_check_array_bounds", scope: [[META5]], file: [[META5]], type: [[META25:![0-9]+]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META0]])
// CHECK-NOTRAP: [[META25]] = !DISubroutineType(types: null)
// CHECK-NOTRAP: [[DBG26]] = !DILocation(line: 66, column: 10, scope: [[DBG4]])
// CHECK-NOTRAP: [[PROF27]] = !{!"branch_weights", i32 1048575, i32 1}
// CHECK-NOTRAP: [[DBG28]] = !DILocation(line: 66, column: 3, scope: [[DBG4]])
//.