
This patch adds a human readable trap category and message to UBSan traps. The category and message are encoded in a fake frame in the debug info where the function is a fake inline function where the name encodes the trap category and message. This is the same mechanism used by Clang’s `__builtin_verbose_trap()`. This change allows consumers of binaries built with trapping UBSan to more easily identify the reason for trapping. In particular LLDB already has a frame recognizer that recognizes the fake function names emitted in debug info by this patch. A patch testing this behavior in LLDB will be added in a separately. The human readable trap messages are based on the messages currently emitted by the userspace runtime for UBSan in compiler-rt. Note the wording is not identical because the userspace UBSan runtime has access to dynamic information that is not available during Clang’s codegen. Test cases for each UBSan trap kind are included. This complements the [`-fsanitize-annotate-debug-info` feature](https://github.com/llvm/llvm-project/pull/141997). While `-fsanitize-annotate-debug-info` attempts to annotate all UBSan-added instructions, this feature (`-fsanitize-debug-trap-reasons`) only annotates the final trap instruction using SanitizerHandler information. This work is part of a GSoc 2025 project.
128 lines
11 KiB
C
128 lines
11 KiB
C
// 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 -fsanitize-annotate-debug-info=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 -fsanitize-annotate-debug-info=array-bounds -triple x86_64 -debug-info-kind=limited %s -o - | FileCheck --check-prefix=CHECK-NOTRAP %s
|
|
|
|
int f();
|
|
void d(double*);
|
|
|
|
// CHECK-LABEL: @f1
|
|
// CHECK-TRAP-LABEL: define dso_local double @f1(
|
|
// CHECK-TRAP-SAME: i32 noundef [[B:%.*]], i32 noundef [[I:%.*]]) #[[ATTR0:[0-9]+]] !dbg [[DBG4:![0-9]+]] {
|
|
// CHECK-TRAP-NEXT: [[ENTRY:.*:]]
|
|
// CHECK-TRAP-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4
|
|
// CHECK-TRAP-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4
|
|
// CHECK-TRAP-NEXT: [[A:%.*]] = alloca [10 x double], align 16
|
|
// CHECK-TRAP-NEXT: store i32 [[B]], ptr [[B_ADDR]], align 4
|
|
// CHECK-TRAP-NEXT: #dbg_declare(ptr [[B_ADDR]], [[META11:![0-9]+]], !DIExpression(), [[META12:![0-9]+]])
|
|
// CHECK-TRAP-NEXT: store i32 [[I]], ptr [[I_ADDR]], align 4
|
|
// CHECK-TRAP-NEXT: #dbg_declare(ptr [[I_ADDR]], [[META13:![0-9]+]], !DIExpression(), [[META14:![0-9]+]])
|
|
// CHECK-TRAP-NEXT: #dbg_declare(ptr [[A]], [[META15:![0-9]+]], !DIExpression(), [[META19:![0-9]+]])
|
|
// CHECK-TRAP-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x double], ptr [[A]], i64 0, i64 0, !dbg [[DBG20:![0-9]+]]
|
|
// CHECK-TRAP-NEXT: call void @d(ptr noundef [[ARRAYDECAY]]), !dbg [[DBG21:![0-9]+]]
|
|
// CHECK-TRAP-NEXT: [[CALL:%.*]] = call i32 (...) @f(), !dbg [[DBG22:![0-9]+]]
|
|
// CHECK-TRAP-NEXT: [[TMP0:%.*]] = sext i32 [[CALL]] to i64, !dbg [[DBG23:![0-9]+]], !nosanitize [[META10:![0-9]+]]
|
|
// CHECK-TRAP-NEXT: [[TMP1:%.*]] = icmp ult i64 [[TMP0]], 10, !dbg [[DBG23]], !nosanitize [[META10]]
|
|
// CHECK-TRAP-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !dbg [[DBG23]], !prof [[PROF27:![0-9]+]], !nosanitize [[META10]]
|
|
// CHECK-TRAP: [[TRAP]]:
|
|
// CHECK-TRAP-NEXT: call void @llvm.ubsantrap(i8 18) #[[ATTR3:[0-9]+]], !dbg [[DBGTRAP:![0-9]+]], !nosanitize [[META10]]
|
|
// CHECK-TRAP-NEXT: unreachable, !dbg [[DBGTRAP]], !nosanitize [[META10]]
|
|
// CHECK-TRAP: [[CONT]]:
|
|
// 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 [[DBG30:![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]+]] {
|
|
// CHECK-NOTRAP-NEXT: [[ENTRY:.*:]]
|
|
// CHECK-NOTRAP-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4
|
|
// CHECK-NOTRAP-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4
|
|
// CHECK-NOTRAP-NEXT: [[A:%.*]] = alloca [10 x double], align 16
|
|
// CHECK-NOTRAP-NEXT: store i32 [[B]], ptr [[B_ADDR]], align 4
|
|
// CHECK-NOTRAP-NEXT: #dbg_declare(ptr [[B_ADDR]], [[META11:![0-9]+]], !DIExpression(), [[META12:![0-9]+]])
|
|
// CHECK-NOTRAP-NEXT: store i32 [[I]], ptr [[I_ADDR]], align 4
|
|
// CHECK-NOTRAP-NEXT: #dbg_declare(ptr [[I_ADDR]], [[META13:![0-9]+]], !DIExpression(), [[META14:![0-9]+]])
|
|
// CHECK-NOTRAP-NEXT: #dbg_declare(ptr [[A]], [[META15:![0-9]+]], !DIExpression(), [[META19:![0-9]+]])
|
|
// CHECK-NOTRAP-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x double], ptr [[A]], i64 0, i64 0, !dbg [[DBG20:![0-9]+]]
|
|
// CHECK-NOTRAP-NEXT: call void @d(ptr noundef [[ARRAYDECAY]]), !dbg [[DBG21:![0-9]+]]
|
|
// 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 [[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 [[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];
|
|
d(a);
|
|
return a[f()];
|
|
}
|
|
|
|
//.
|
|
// CHECK-TRAP: [[META0:![0-9]+]] = distinct !DICompileUnit(language: DW_LANG_C11, file: [[META1:![0-9]+]], isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
|
|
// CHECK-TRAP: [[META1]] = !DIFile(filename: "<stdin>", directory: {{.*}})
|
|
// CHECK-TRAP: [[DBG4]] = distinct !DISubprogram(name: "f1", scope: [[META5:![0-9]+]], file: [[META5]], line: 63, type: [[META6:![0-9]+]], scopeLine: 63, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: [[META0]], retainedNodes: [[META10]])
|
|
// CHECK-TRAP: [[META5]] = !DIFile(filename: "bounds-checking-debuginfo.c", directory: {{.*}})
|
|
// CHECK-TRAP: [[META6]] = !DISubroutineType(types: [[META7:![0-9]+]])
|
|
// CHECK-TRAP: [[META7]] = !{[[META8:![0-9]+]], [[META9:![0-9]+]], [[META9]]}
|
|
// CHECK-TRAP: [[META8]] = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float)
|
|
// CHECK-TRAP: [[META9]] = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
|
|
// CHECK-TRAP: [[META10]] = !{}
|
|
// CHECK-TRAP: [[META11]] = !DILocalVariable(name: "b", arg: 1, scope: [[DBG4]], file: [[META5]], line: 63, type: [[META9]])
|
|
// CHECK-TRAP: [[META12]] = !DILocation(line: 63, column: 15, scope: [[DBG4]])
|
|
// CHECK-TRAP: [[META13]] = !DILocalVariable(name: "i", arg: 2, scope: [[DBG4]], file: [[META5]], line: 63, type: [[META9]])
|
|
// CHECK-TRAP: [[META14]] = !DILocation(line: 63, column: 22, scope: [[DBG4]])
|
|
// CHECK-TRAP: [[META15]] = !DILocalVariable(name: "a", scope: [[DBG4]], file: [[META5]], line: 64, type: [[META16:![0-9]+]])
|
|
// CHECK-TRAP: [[META16]] = !DICompositeType(tag: DW_TAG_array_type, baseType: [[META8]], size: 640, elements: [[META17:![0-9]+]])
|
|
// CHECK-TRAP: [[META17]] = !{[[META18:![0-9]+]]}
|
|
// CHECK-TRAP: [[META18]] = !DISubrange(count: 10)
|
|
// CHECK-TRAP: [[META19]] = !DILocation(line: 64, column: 10, scope: [[DBG4]])
|
|
// 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: 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: [[PROF27]] = !{!"branch_weights", i32 1048575, i32 1}
|
|
// CHECK-TRAP: [[DBGTRAP]] = !DILocation(line: 0, scope: [[TRAPMSG:![0-9]+]], inlinedAt: [[DBG23]])
|
|
// CHECK-TRAP: [[TRAPMSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Array index out of bounds", scope: [[META5]], file: [[META5]], type: [[META25]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META0]])
|
|
// CHECK-TRAP: [[DBG30]] = !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: {{.*}})
|
|
// CHECK-NOTRAP: [[DBG4]] = distinct !DISubprogram(name: "f1", scope: [[META5:![0-9]+]], file: [[META5]], line: 63, type: [[META6:![0-9]+]], scopeLine: 63, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: [[META0]], retainedNodes: [[META10]])
|
|
// CHECK-NOTRAP: [[META5]] = !DIFile(filename: "bounds-checking-debuginfo.c", directory: {{.*}})
|
|
// CHECK-NOTRAP: [[META6]] = !DISubroutineType(types: [[META7:![0-9]+]])
|
|
// CHECK-NOTRAP: [[META7]] = !{[[META8:![0-9]+]], [[META9:![0-9]+]], [[META9]]}
|
|
// CHECK-NOTRAP: [[META8]] = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float)
|
|
// CHECK-NOTRAP: [[META9]] = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
|
|
// CHECK-NOTRAP: [[META10]] = !{}
|
|
// CHECK-NOTRAP: [[META11]] = !DILocalVariable(name: "b", arg: 1, scope: [[DBG4]], file: [[META5]], line: 63, type: [[META9]])
|
|
// CHECK-NOTRAP: [[META12]] = !DILocation(line: 63, column: 15, scope: [[DBG4]])
|
|
// CHECK-NOTRAP: [[META13]] = !DILocalVariable(name: "i", arg: 2, scope: [[DBG4]], file: [[META5]], line: 63, type: [[META9]])
|
|
// CHECK-NOTRAP: [[META14]] = !DILocation(line: 63, column: 22, scope: [[DBG4]])
|
|
// CHECK-NOTRAP: [[META15]] = !DILocalVariable(name: "a", scope: [[DBG4]], file: [[META5]], line: 64, type: [[META16:![0-9]+]])
|
|
// CHECK-NOTRAP: [[META16]] = !DICompositeType(tag: DW_TAG_array_type, baseType: [[META8]], size: 640, elements: [[META17:![0-9]+]])
|
|
// CHECK-NOTRAP: [[META17]] = !{[[META18:![0-9]+]]}
|
|
// CHECK-NOTRAP: [[META18]] = !DISubrange(count: 10)
|
|
// CHECK-NOTRAP: [[META19]] = !DILocation(line: 64, column: 10, scope: [[DBG4]])
|
|
// 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: 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]])
|
|
//.
|