From 228319f3e65ca89ec8918b9c5a4d48cf234df8c2 Mon Sep 17 00:00:00 2001 From: Anthony Tran Date: Sat, 26 Jul 2025 08:50:25 -0700 Subject: [PATCH 1/6] =?UTF-8?q?[Clang][CodeGen]=20Emit=20=E2=80=9Ctrap=20r?= =?UTF-8?q?easons=E2=80=9D=20on=20UBSan=20traps=20(#145967)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. (cherry picked from commit ba477b9e1b00191166044cb706f5bd1b4765fd29) (cherry picked from commit 29992cfd628ed5b968ccb73b17ed0521382ba317) --- clang/docs/ReleaseNotes.rst | 9 ++ clang/include/clang/Basic/CodeGenOptions.def | 1 + clang/include/clang/Driver/Options.td | 10 ++ clang/lib/CodeGen/CGDebugInfo.cpp | 2 +- clang/lib/CodeGen/CGExpr.cpp | 31 ++++- clang/lib/CodeGen/SanitizerHandler.h | 88 +++++++++---- clang/lib/Driver/SanitizerArgs.cpp | 6 + .../test/CodeGen/bounds-checking-debuginfo.c | 10 +- .../CodeGen/cfi-icall-generalize-debuginfo.c | 20 +-- .../CodeGen/cfi-icall-normalize2-debuginfo.c | 118 +++++++++--------- clang/test/CodeGen/ubsan-trap-debugloc.c | 7 +- .../CodeGen/ubsan-trap-reason-add-overflow.c | 9 ++ .../ubsan-trap-reason-alignment-assumption.c | 15 +++ .../ubsan-trap-reason-builtin-unreachable.c | 9 ++ .../ubsan-trap-reason-cfi-check-fail.c | 25 ++++ .../test/CodeGen/ubsan-trap-reason-crash.cpp | 20 +++ .../ubsan-trap-reason-div-rem-overflow.c | 9 ++ ...an-trap-reason-dynamic-type-cache-miss.cpp | 26 ++++ clang/test/CodeGen/ubsan-trap-reason-flag.c | 22 ++++ .../ubsan-trap-reason-float-cast-overflow.c | 9 ++ ...ubsan-trap-reason-function-type-mismatch.c | 18 +++ .../ubsan-trap-reason-implicit-conversion.c | 11 ++ .../ubsan-trap-reason-invalid-builtin.c | 9 ++ .../ubsan-trap-reason-invalid-objc-cast.m | 32 +++++ .../ubsan-trap-reason-load-invalid-value.c | 13 ++ .../ubsan-trap-reason-missing-return.cpp | 12 ++ .../CodeGen/ubsan-trap-reason-mul-overflow.c | 9 ++ .../ubsan-trap-reason-negate-overflow.c | 12 ++ .../CodeGen/ubsan-trap-reason-nonnull-arg.c | 12 ++ .../ubsan-trap-reason-nonnull-return.c | 14 +++ .../ubsan-trap-reason-nullability-arg.c | 14 +++ .../ubsan-trap-reason-nullability-return.c | 18 +++ .../CodeGen/ubsan-trap-reason-out-of-bounds.c | 12 ++ .../ubsan-trap-reason-pointer-overflow.c | 16 +++ .../ubsan-trap-reason-shift-out-of-bounds.c | 12 ++ .../CodeGen/ubsan-trap-reason-sub-overflow.c | 9 ++ .../CodeGen/ubsan-trap-reason-type-mismatch.c | 9 ++ ...ubsan-trap-reason-vla-bound-not-positive.c | 14 +++ 38 files changed, 589 insertions(+), 103 deletions(-) create mode 100644 clang/test/CodeGen/ubsan-trap-reason-add-overflow.c create mode 100644 clang/test/CodeGen/ubsan-trap-reason-alignment-assumption.c create mode 100644 clang/test/CodeGen/ubsan-trap-reason-builtin-unreachable.c create mode 100644 clang/test/CodeGen/ubsan-trap-reason-cfi-check-fail.c create mode 100644 clang/test/CodeGen/ubsan-trap-reason-crash.cpp create mode 100644 clang/test/CodeGen/ubsan-trap-reason-div-rem-overflow.c create mode 100644 clang/test/CodeGen/ubsan-trap-reason-dynamic-type-cache-miss.cpp create mode 100644 clang/test/CodeGen/ubsan-trap-reason-flag.c create mode 100644 clang/test/CodeGen/ubsan-trap-reason-float-cast-overflow.c create mode 100644 clang/test/CodeGen/ubsan-trap-reason-function-type-mismatch.c create mode 100644 clang/test/CodeGen/ubsan-trap-reason-implicit-conversion.c create mode 100644 clang/test/CodeGen/ubsan-trap-reason-invalid-builtin.c create mode 100644 clang/test/CodeGen/ubsan-trap-reason-invalid-objc-cast.m create mode 100644 clang/test/CodeGen/ubsan-trap-reason-load-invalid-value.c create mode 100644 clang/test/CodeGen/ubsan-trap-reason-missing-return.cpp create mode 100644 clang/test/CodeGen/ubsan-trap-reason-mul-overflow.c create mode 100644 clang/test/CodeGen/ubsan-trap-reason-negate-overflow.c create mode 100644 clang/test/CodeGen/ubsan-trap-reason-nonnull-arg.c create mode 100644 clang/test/CodeGen/ubsan-trap-reason-nonnull-return.c create mode 100644 clang/test/CodeGen/ubsan-trap-reason-nullability-arg.c create mode 100644 clang/test/CodeGen/ubsan-trap-reason-nullability-return.c create mode 100644 clang/test/CodeGen/ubsan-trap-reason-out-of-bounds.c create mode 100644 clang/test/CodeGen/ubsan-trap-reason-pointer-overflow.c create mode 100644 clang/test/CodeGen/ubsan-trap-reason-shift-out-of-bounds.c create mode 100644 clang/test/CodeGen/ubsan-trap-reason-sub-overflow.c create mode 100644 clang/test/CodeGen/ubsan-trap-reason-type-mismatch.c create mode 100644 clang/test/CodeGen/ubsan-trap-reason-vla-bound-not-positive.c diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 9400be296e7c2..0286c46f19624 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -366,9 +366,18 @@ Non-comprehensive list of changes in this release correct method to check for these features is to test for the ``__PTRAUTH__`` macro. +- Trapping UBSan (e.g. ``-fsanitize-trap=undefined``) now emits a string describing the reason for + trapping into the generated debug info. This feature allows debuggers (e.g. LLDB) to display + the reason for trapping if the trap is reached. The string is currently encoded in the debug + info as an artificial frame that claims to be inlined at the trap location. The function used + for the artificial frame is an artificial function whose name encodes the reason for trapping. + The encoding used is currently the same as ``__builtin_verbose_trap`` but might change in the future. + This feature is enabled by default but can be disabled by compiling with + ``-fno-sanitize-annotate-debug-info-traps``. New Compiler Flags ------------------ +- New option ``-fno-sanitize-annotate-debug-info-traps`` added to disable emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``). - New option ``-Wundef-true`` added and enabled by default to warn when `true` is used in the C preprocessor without being defined before C23. diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index 2c24223692ffb..47caec13824d2 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -306,6 +306,7 @@ CODEGENOPT(SanitizeBinaryMetadataAtomics, 1, 0, Benign) ///< Emit PCs for atomic CODEGENOPT(SanitizeBinaryMetadataUAR, 1, 0, Benign) ///< Emit PCs for start of functions ///< that are subject for use-after-return checking. CODEGENOPT(SanitizeStats , 1, 0, Benign) ///< Collect statistics for sanitizers. +CODEGENOPT(SanitizeDebugTrapReasons, 1, 1 , Benign) ///< Enable UBSan trapping messages CODEGENOPT(SimplifyLibCalls , 1, 1, Benign) ///< Set when -fbuiltin is enabled. CODEGENOPT(SoftFloat , 1, 0, Benign) ///< -soft-float. CODEGENOPT(SpeculativeLoadHardening, 1, 0, Benign) ///< Enable speculative load hardening. diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 015ad31d7db18..4e5605b822023 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2717,6 +2717,16 @@ def fsanitize_undefined_trap_on_error def fno_sanitize_undefined_trap_on_error : Flag<["-"], "fno-sanitize-undefined-trap-on-error">, Group, Alias, AliasArgs<["undefined"]>; +defm sanitize_debug_trap_reasons + : BoolFOption< + "sanitize-debug-trap-reasons", + CodeGenOpts<"SanitizeDebugTrapReasons">, DefaultTrue, + PosFlag, + NegFlag>; + defm sanitize_minimal_runtime : BoolOption<"f", "sanitize-minimal-runtime", CodeGenOpts<"SanitizeMinimalRuntime">, DefaultFalse, PosFlag, diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 2501879efe833..3d38dca987258 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -6535,7 +6535,7 @@ CodeGenFunction::LexicalScope::~LexicalScope() { static std::string SanitizerHandlerToCheckLabel(SanitizerHandler Handler) { std::string Label; switch (Handler) { -#define SANITIZER_CHECK(Enum, Name, Version) \ +#define SANITIZER_CHECK(Enum, Name, Version, Msg) \ case Enum: \ Label = "__ubsan_check_" #Name; \ break; diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 2f75509d41f50..10f43f697e2b0 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -312,6 +312,17 @@ void CodeGenFunction::EmitValueTerminatedAssignmentCheck( EmitBoundsSafetyTrapCheck(Check, BNS_TRAP_TERMINATED_BY_TERM_ASSIGN); } + +static llvm::StringRef GetUBSanTrapForHandler(SanitizerHandler ID) { + switch (ID) { +#define SANITIZER_CHECK(Enum, Name, Version, Msg) \ + case SanitizerHandler::Enum: \ + return Msg; + LIST_SANITIZER_CHECKS +#undef SANITIZER_CHECK + } +} + /// CreateTempAlloca - This creates a alloca and inserts it into the entry /// block. RawAddress @@ -4162,7 +4173,7 @@ struct SanitizerHandlerInfo { } const SanitizerHandlerInfo SanitizerHandlers[] = { -#define SANITIZER_CHECK(Enum, Name, Version) {#Name, Version}, +#define SANITIZER_CHECK(Enum, Name, Version, Msg) {#Name, Version}, LIST_SANITIZER_CHECKS #undef SANITIZER_CHECK }; @@ -4467,6 +4478,8 @@ void CodeGenFunction::EmitCfiCheckFail() { StartFunction(GlobalDecl(), CGM.getContext().VoidTy, F, FI, Args, SourceLocation()); + ApplyDebugLocation ADL = ApplyDebugLocation::CreateArtificial(*this); + // This function is not affected by NoSanitizeList. This function does // not have a source location, but "src:*" would still apply. Revert any // changes to SanOpts made in StartFunction. @@ -4557,7 +4570,7 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked, bool NoMerge, /*TO_UPSTREAM(BoundsSafety) ON*/ StringRef Annotation, - StringRef TrapMessage) { + StringRef BoundsSafetyTrapMessage) { /*TO_UPSTREAM(BoundsSafety) OFF*/ llvm::BasicBlock *Cont = createBasicBlock("cont"); @@ -4568,13 +4581,23 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked, llvm::BasicBlock *&TrapBB = TrapBBs[CheckHandlerID]; - /*TO_UPSTREAM(BoundsSafety) ON*/ llvm::DILocation *TrapLocation = Builder.getCurrentDebugLocation(); + + /*TO_UPSTREAM(BoundsSafety) ON*/ if (CheckHandlerID == SanitizerHandler::BoundsSafety && getDebugInfo()) { TrapLocation = getDebugInfo()->CreateTrapFailureMessageFor( - TrapLocation, GetBoundsSafetyTrapMessagePrefix(), TrapMessage); + TrapLocation, GetBoundsSafetyTrapMessagePrefix(), BoundsSafetyTrapMessage); } /*TO_UPSTREAM(BoundsSafety) OFF*/ + else { + llvm::StringRef TrapMessage = GetUBSanTrapForHandler(CheckHandlerID); + + if (getDebugInfo() && !TrapMessage.empty() && + CGM.getCodeGenOpts().SanitizeDebugTrapReasons && TrapLocation) { + TrapLocation = getDebugInfo()->CreateTrapFailureMessageFor( + TrapLocation, "Undefined Behavior Sanitizer", TrapMessage); + } + } NoMerge = NoMerge || !CGM.getCodeGenOpts().OptimizationLevel || (CurCodeDecl && CurCodeDecl->hasAttr()); diff --git a/clang/lib/CodeGen/SanitizerHandler.h b/clang/lib/CodeGen/SanitizerHandler.h index bb42e3947cf14..a66e7ab354eb2 100644 --- a/clang/lib/CodeGen/SanitizerHandler.h +++ b/clang/lib/CodeGen/SanitizerHandler.h @@ -14,35 +14,69 @@ #define LLVM_CLANG_LIB_CODEGEN_SANITIZER_HANDLER_H #define LIST_SANITIZER_CHECKS \ - SANITIZER_CHECK(AddOverflow, add_overflow, 0) \ - SANITIZER_CHECK(BuiltinUnreachable, builtin_unreachable, 0) \ - SANITIZER_CHECK(CFICheckFail, cfi_check_fail, 0) \ - SANITIZER_CHECK(DivremOverflow, divrem_overflow, 0) \ - SANITIZER_CHECK(DynamicTypeCacheMiss, dynamic_type_cache_miss, 0) \ - SANITIZER_CHECK(FloatCastOverflow, float_cast_overflow, 0) \ - SANITIZER_CHECK(FunctionTypeMismatch, function_type_mismatch, 0) \ - SANITIZER_CHECK(ImplicitConversion, implicit_conversion, 0) \ - SANITIZER_CHECK(InvalidBuiltin, invalid_builtin, 0) \ - SANITIZER_CHECK(InvalidObjCCast, invalid_objc_cast, 0) \ - SANITIZER_CHECK(LoadInvalidValue, load_invalid_value, 0) \ - SANITIZER_CHECK(MissingReturn, missing_return, 0) \ - SANITIZER_CHECK(MulOverflow, mul_overflow, 0) \ - SANITIZER_CHECK(NegateOverflow, negate_overflow, 0) \ - SANITIZER_CHECK(NullabilityArg, nullability_arg, 0) \ - SANITIZER_CHECK(NullabilityReturn, nullability_return, 1) \ - SANITIZER_CHECK(NonnullArg, nonnull_arg, 0) \ - SANITIZER_CHECK(NonnullReturn, nonnull_return, 1) \ - SANITIZER_CHECK(OutOfBounds, out_of_bounds, 0) \ - SANITIZER_CHECK(PointerOverflow, pointer_overflow, 0) \ - SANITIZER_CHECK(ShiftOutOfBounds, shift_out_of_bounds, 0) \ - SANITIZER_CHECK(SubOverflow, sub_overflow, 0) \ - SANITIZER_CHECK(TypeMismatch, type_mismatch, 1) \ - SANITIZER_CHECK(AlignmentAssumption, alignment_assumption, 0) \ - SANITIZER_CHECK(VLABoundNotPositive, vla_bound_not_positive, 0) \ - SANITIZER_CHECK(BoundsSafety, bounds_safety, 0) + SANITIZER_CHECK(AddOverflow, add_overflow, 0, "Integer addition overflowed") \ + SANITIZER_CHECK(BuiltinUnreachable, builtin_unreachable, 0, \ + "_builtin_unreachable(), execution reached an unreachable " \ + "program point") \ + SANITIZER_CHECK(CFICheckFail, cfi_check_fail, 0, \ + "Control flow integrity check failed") \ + SANITIZER_CHECK(DivremOverflow, divrem_overflow, 0, \ + "Integer divide or remainder overflowed") \ + SANITIZER_CHECK(DynamicTypeCacheMiss, dynamic_type_cache_miss, 0, \ + "Dynamic type cache miss, member call made on an object " \ + "whose dynamic type differs from the expected type") \ + SANITIZER_CHECK(FloatCastOverflow, float_cast_overflow, 0, \ + "Floating-point to integer conversion overflowed") \ + SANITIZER_CHECK(FunctionTypeMismatch, function_type_mismatch, 0, \ + "Function called with mismatched signature") \ + SANITIZER_CHECK(ImplicitConversion, implicit_conversion, 0, \ + "Implicit integer conversion overflowed or lost data") \ + SANITIZER_CHECK(InvalidBuiltin, invalid_builtin, 0, \ + "Invalid use of builtin function") \ + SANITIZER_CHECK(InvalidObjCCast, invalid_objc_cast, 0, \ + "Invalid Objective-C cast") \ + SANITIZER_CHECK(LoadInvalidValue, load_invalid_value, 0, \ + "Loaded an invalid or uninitialized value for the type") \ + SANITIZER_CHECK(MissingReturn, missing_return, 0, \ + "Execution reached the end of a value-returning function " \ + "without returning a value") \ + SANITIZER_CHECK(MulOverflow, mul_overflow, 0, \ + "Integer multiplication overflowed") \ + SANITIZER_CHECK(NegateOverflow, negate_overflow, 0, \ + "Integer negation overflowed") \ + SANITIZER_CHECK( \ + NullabilityArg, nullability_arg, 0, \ + "Passing null as an argument which is annotated with _Nonnull") \ + SANITIZER_CHECK(NullabilityReturn, nullability_return, 1, \ + "Returning null from a function with a return type " \ + "annotated with _Nonnull") \ + SANITIZER_CHECK(NonnullArg, nonnull_arg, 0, \ + "Passing null pointer as an argument which is declared to " \ + "never be null") \ + SANITIZER_CHECK(NonnullReturn, nonnull_return, 1, \ + "Returning null pointer from a function which is declared " \ + "to never return null") \ + SANITIZER_CHECK(OutOfBounds, out_of_bounds, 0, "Array index out of bounds") \ + SANITIZER_CHECK(PointerOverflow, pointer_overflow, 0, \ + "Pointer arithmetic overflowed bounds") \ + SANITIZER_CHECK(ShiftOutOfBounds, shift_out_of_bounds, 0, \ + "Shift exponent is too large for the type") \ + SANITIZER_CHECK(SubOverflow, sub_overflow, 0, \ + "Integer subtraction overflowed") \ + SANITIZER_CHECK(TypeMismatch, type_mismatch, 1, \ + "Type mismatch in operation") \ + SANITIZER_CHECK(AlignmentAssumption, alignment_assumption, 0, \ + "Alignment assumption violated") \ + SANITIZER_CHECK( \ + VLABoundNotPositive, vla_bound_not_positive, 0, \ + "Variable length array bound evaluates to non-positive value") \ + SANITIZER_CHECK(BoundsSafety, bounds_safety, 0, \ + "") // BoundsSafety Msg is empty because it is not considered + // part of UBSan; therefore, no trap reason is emitted for + // this case. enum SanitizerHandler { -#define SANITIZER_CHECK(Enum, Name, Version) Enum, +#define SANITIZER_CHECK(Enum, Name, Version, Msg) Enum, LIST_SANITIZER_CHECKS #undef SANITIZER_CHECK }; diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp index fe76a0f9df887..0fbbd49eb9275 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -1385,6 +1385,12 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args, CmdArgs.push_back(Args.MakeArgString("-fsanitize-annotate-debug-info=" + toString(AnnotateDebugInfo))); + if (const Arg *A = + Args.getLastArg(options::OPT_fsanitize_debug_trap_reasons, + options::OPT_fno_sanitize_debug_trap_reasons)) { + CmdArgs.push_back(Args.MakeArgString(A->getAsString(Args))); + } + addSpecialCaseListOpt(Args, CmdArgs, "-fsanitize-ignorelist=", UserIgnorelistFiles); addSpecialCaseListOpt(Args, CmdArgs, diff --git a/clang/test/CodeGen/bounds-checking-debuginfo.c b/clang/test/CodeGen/bounds-checking-debuginfo.c index 74c06665dfe02..bd7aedd7ac2c1 100644 --- a/clang/test/CodeGen/bounds-checking-debuginfo.c +++ b/clang/test/CodeGen/bounds-checking-debuginfo.c @@ -25,13 +25,13 @@ void d(double*); // 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 [[DBG23]], !nosanitize [[META10]] -// CHECK-TRAP-NEXT: unreachable, !dbg [[DBG23]], !nosanitize [[META10]] +// 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 [[DBG28:![0-9]+]] +// 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]+]] { @@ -93,7 +93,9 @@ double f1(int b, int i) { // 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: [[DBG28]] = !DILocation(line: 66, column: 3, scope: [[DBG4]]) +// 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: "", directory: {{.*}}) diff --git a/clang/test/CodeGen/cfi-icall-generalize-debuginfo.c b/clang/test/CodeGen/cfi-icall-generalize-debuginfo.c index 304b60539c3d1..0ffc2b9e415d4 100644 --- a/clang/test/CodeGen/cfi-icall-generalize-debuginfo.c +++ b/clang/test/CodeGen/cfi-icall-generalize-debuginfo.c @@ -30,11 +30,11 @@ int** f(const char *a, const char **b) { // UNGENERALIZED-NEXT: [[TMP0:%.*]] = tail call i1 @llvm.type.test(ptr [[FP]], metadata !"_ZTSFPPiPKcPS2_E"), !dbg [[DBG34:![0-9]+]], !nosanitize [[META38:![0-9]+]] // UNGENERALIZED-NEXT: br i1 [[TMP0]], label %[[CONT:.*]], label %[[TRAP:.*]], !dbg [[DBG34]], !prof [[PROF39:![0-9]+]], !nosanitize [[META38]] // UNGENERALIZED: [[TRAP]]: -// UNGENERALIZED-NEXT: tail call void @llvm.ubsantrap(i8 2) #[[ATTR4:[0-9]+]], !dbg [[DBG34]], !nosanitize [[META38]] -// UNGENERALIZED-NEXT: unreachable, !dbg [[DBG34]], !nosanitize [[META38]] +// UNGENERALIZED-NEXT: tail call void @llvm.ubsantrap(i8 2) #[[ATTR4:[0-9]+]], !dbg [[DBGTRAP:![0-9]+]], !nosanitize [[META38]] +// UNGENERALIZED-NEXT: unreachable, !dbg [[DBGTRAP]], !nosanitize [[META38]] // UNGENERALIZED: [[CONT]]: // UNGENERALIZED-NEXT: [[CALL:%.*]] = tail call ptr [[FP]](ptr noundef null, ptr noundef null) #[[ATTR5:[0-9]+]], !dbg [[DBG37:![0-9]+]] -// UNGENERALIZED-NEXT: ret void, !dbg [[DBG40:![0-9]+]] +// UNGENERALIZED-NEXT: ret void, !dbg [[DBG42:![0-9]+]] // // GENERALIZED-LABEL: define dso_local void @g( // GENERALIZED-SAME: ptr noundef [[FP:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] !dbg [[DBG25:![0-9]+]] !type [[META31:![0-9]+]] !type [[META32:![0-9]+]] { @@ -43,11 +43,11 @@ int** f(const char *a, const char **b) { // GENERALIZED-NEXT: [[TMP0:%.*]] = tail call i1 @llvm.type.test(ptr [[FP]], metadata !"_ZTSFPvPKvS_E.generalized"), !dbg [[DBG34:![0-9]+]], !nosanitize [[META38:![0-9]+]] // GENERALIZED-NEXT: br i1 [[TMP0]], label %[[CONT:.*]], label %[[TRAP:.*]], !dbg [[DBG34]], !prof [[PROF39:![0-9]+]], !nosanitize [[META38]] // GENERALIZED: [[TRAP]]: -// GENERALIZED-NEXT: tail call void @llvm.ubsantrap(i8 2) #[[ATTR4:[0-9]+]], !dbg [[DBG34]], !nosanitize [[META38]] -// GENERALIZED-NEXT: unreachable, !dbg [[DBG34]], !nosanitize [[META38]] +// GENERALIZED-NEXT: tail call void @llvm.ubsantrap(i8 2) #[[ATTR4:[0-9]+]], !dbg [[DBGTRAP:![0-9]+]], !nosanitize [[META38]] +// GENERALIZED-NEXT: unreachable, !dbg [[DBGTRAP]], !nosanitize [[META38]] // GENERALIZED: [[CONT]]: // GENERALIZED-NEXT: [[CALL:%.*]] = tail call ptr [[FP]](ptr noundef null, ptr noundef null) #[[ATTR5:[0-9]+]], !dbg [[DBG37:![0-9]+]] -// GENERALIZED-NEXT: ret void, !dbg [[DBG40:![0-9]+]] +// GENERALIZED-NEXT: ret void, !dbg [[DBG42:![0-9]+]] // void g(int** (*fp)(const char *, const char **)) { fp(0, 0); @@ -90,7 +90,9 @@ void g(int** (*fp)(const char *, const char **)) { // UNGENERALIZED: [[DBG37]] = !DILocation(line: 53, column: 3, scope: [[DBG25]]) // UNGENERALIZED: [[META38]] = !{} // UNGENERALIZED: [[PROF39]] = !{!"branch_weights", i32 1048575, i32 1} -// UNGENERALIZED: [[DBG40]] = !DILocation(line: 54, column: 1, scope: [[DBG25]]) +// UNGENERALIZED: [[DBGTRAP]] = !DILocation(line: 0, scope: [[TRAPMSG:![0-9]+]], inlinedAt: [[DBG34]]) +// UNGENERALIZED: [[TRAPMSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Control flow integrity check failed", scope: [[META11]], file: [[META11]], type: [[META36]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META0]]) +// UNGENERALIZED: [[DBG42]] = !DILocation(line: 54, column: 1, scope: [[DBG25]]) //. // GENERALIZED: [[META0:![0-9]+]] = distinct !DICompileUnit(language: DW_LANG_C11, file: [[META1:![0-9]+]], isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: [[META2:![0-9]+]], splitDebugInlining: false, nameTableKind: None) // GENERALIZED: [[META1]] = !DIFile(filename: "{{.*}}", directory: {{.*}}) @@ -128,5 +130,7 @@ void g(int** (*fp)(const char *, const char **)) { // GENERALIZED: [[DBG37]] = !DILocation(line: 53, column: 3, scope: [[DBG25]]) // GENERALIZED: [[META38]] = !{} // GENERALIZED: [[PROF39]] = !{!"branch_weights", i32 1048575, i32 1} -// GENERALIZED: [[DBG40]] = !DILocation(line: 54, column: 1, scope: [[DBG25]]) +// GENERALIZED: [[DBGTRAP]] = !DILocation(line: 0, scope: [[TRAPMSG:![0-9]+]], inlinedAt: [[DBG34]]) +// GENERALIZED: [[TRAPMSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Control flow integrity check failed", scope: [[META11]], file: [[META11]], type: [[META36]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META0]]) +// GENERALIZED: [[DBG42]] = !DILocation(line: 54, column: 1, scope: [[DBG25]]) //. diff --git a/clang/test/CodeGen/cfi-icall-normalize2-debuginfo.c b/clang/test/CodeGen/cfi-icall-normalize2-debuginfo.c index a2f6ee0c6805c..258c3bfbc9f55 100644 --- a/clang/test/CodeGen/cfi-icall-normalize2-debuginfo.c +++ b/clang/test/CodeGen/cfi-icall-normalize2-debuginfo.c @@ -15,50 +15,50 @@ // CHECK-NEXT: [[TMP0:%.*]] = tail call i1 @llvm.type.test(ptr [[FN]], metadata !"_ZTSFvu3i32E.normalized"), !dbg [[DBG21:![0-9]+]], !nosanitize [[META25:![0-9]+]] // CHECK-NEXT: br i1 [[TMP0]], label %[[CONT:.*]], label %[[TRAP:.*]], !dbg [[DBG21]], !prof [[PROF26:![0-9]+]], !nosanitize [[META25]] // CHECK: [[TRAP]]: -// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 2) #[[ATTR3:[0-9]+]], !dbg [[DBG21]], !nosanitize [[META25]] -// CHECK-NEXT: unreachable, !dbg [[DBG21]], !nosanitize [[META25]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 2) #[[ATTR3:[0-9]+]], !dbg [[DBGTRAP:![0-9]+]], !nosanitize [[META25]] +// CHECK-NEXT: unreachable, !dbg [[DBGTRAP]], !nosanitize [[META25]] // CHECK: [[CONT]]: // CHECK-NEXT: tail call void [[FN]](i32 noundef [[ARG]]) #[[ATTR4:[0-9]+]], !dbg [[DBG24:![0-9]+]] -// CHECK-NEXT: ret void, !dbg [[DBG27:![0-9]+]] +// CHECK-NEXT: ret void, !dbg [[DBG29:![0-9]+]] // void foo(void (*fn)(int), int arg) { fn(arg); } // CHECK-LABEL: define dso_local void @bar( -// CHECK-SAME: ptr noundef [[FN:%.*]], i32 noundef [[ARG1:%.*]], i32 noundef [[ARG2:%.*]]) local_unnamed_addr #[[ATTR0]] !dbg [[DBG28:![0-9]+]] !type [[META38:![0-9]+]] !type [[META39:![0-9]+]] { +// CHECK-SAME: ptr noundef [[FN:%.*]], i32 noundef [[ARG1:%.*]], i32 noundef [[ARG2:%.*]]) local_unnamed_addr #[[ATTR0]] !dbg [[DBG30:![0-9]+]] !type [[META40:![0-9]+]] !type [[META41:![0-9]+]] { // CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: #dbg_value(ptr [[FN]], [[META35:![0-9]+]], !DIExpression(), [[META40:![0-9]+]]) -// CHECK-NEXT: #dbg_value(i32 [[ARG1]], [[META36:![0-9]+]], !DIExpression(), [[META40]]) -// CHECK-NEXT: #dbg_value(i32 [[ARG2]], [[META37:![0-9]+]], !DIExpression(), [[META40]]) -// CHECK-NEXT: [[TMP0:%.*]] = tail call i1 @llvm.type.test(ptr [[FN]], metadata !"_ZTSFvu3i32S_E.normalized"), !dbg [[DBG41:![0-9]+]], !nosanitize [[META25]] -// CHECK-NEXT: br i1 [[TMP0]], label %[[CONT:.*]], label %[[TRAP:.*]], !dbg [[DBG41]], !prof [[PROF26]], !nosanitize [[META25]] +// CHECK-NEXT: #dbg_value(ptr [[FN]], [[META37:![0-9]+]], !DIExpression(), [[META42:![0-9]+]]) +// CHECK-NEXT: #dbg_value(i32 [[ARG1]], [[META38:![0-9]+]], !DIExpression(), [[META42]]) +// CHECK-NEXT: #dbg_value(i32 [[ARG2]], [[META39:![0-9]+]], !DIExpression(), [[META42]]) +// CHECK-NEXT: [[TMP0:%.*]] = tail call i1 @llvm.type.test(ptr [[FN]], metadata !"_ZTSFvu3i32S_E.normalized"), !dbg [[DBG43:![0-9]+]], !nosanitize [[META25]] +// CHECK-NEXT: br i1 [[TMP0]], label %[[CONT:.*]], label %[[TRAP:.*]], !dbg [[DBG43]], !prof [[PROF26]], !nosanitize [[META25]] // CHECK: [[TRAP]]: -// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 2) #[[ATTR3]], !dbg [[DBG41]], !nosanitize [[META25]] -// CHECK-NEXT: unreachable, !dbg [[DBG41]], !nosanitize [[META25]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 2) #[[ATTR3]], !dbg [[DBG45:![0-9]+]], !nosanitize [[META25]] +// CHECK-NEXT: unreachable, !dbg [[DBG45]], !nosanitize [[META25]] // CHECK: [[CONT]]: -// CHECK-NEXT: tail call void [[FN]](i32 noundef [[ARG1]], i32 noundef [[ARG2]]) #[[ATTR4]], !dbg [[DBG42:![0-9]+]] -// CHECK-NEXT: ret void, !dbg [[DBG43:![0-9]+]] +// CHECK-NEXT: tail call void [[FN]](i32 noundef [[ARG1]], i32 noundef [[ARG2]]) #[[ATTR4]], !dbg [[DBG44:![0-9]+]] +// CHECK-NEXT: ret void, !dbg [[DBG46:![0-9]+]] // void bar(void (*fn)(int, int), int arg1, int arg2) { fn(arg1, arg2); } // CHECK-LABEL: define dso_local void @baz( -// CHECK-SAME: ptr noundef [[FN:%.*]], i32 noundef [[ARG1:%.*]], i32 noundef [[ARG2:%.*]], i32 noundef [[ARG3:%.*]]) local_unnamed_addr #[[ATTR0]] !dbg [[DBG44:![0-9]+]] !type [[META55:![0-9]+]] !type [[META56:![0-9]+]] { +// CHECK-SAME: ptr noundef [[FN:%.*]], i32 noundef [[ARG1:%.*]], i32 noundef [[ARG2:%.*]], i32 noundef [[ARG3:%.*]]) local_unnamed_addr #[[ATTR0]] !dbg [[DBG47:![0-9]+]] !type [[META58:![0-9]+]] !type [[META59:![0-9]+]] { // CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: #dbg_value(ptr [[FN]], [[META51:![0-9]+]], !DIExpression(), [[META57:![0-9]+]]) -// CHECK-NEXT: #dbg_value(i32 [[ARG1]], [[META52:![0-9]+]], !DIExpression(), [[META57]]) -// CHECK-NEXT: #dbg_value(i32 [[ARG2]], [[META53:![0-9]+]], !DIExpression(), [[META57]]) -// CHECK-NEXT: #dbg_value(i32 [[ARG3]], [[META54:![0-9]+]], !DIExpression(), [[META57]]) -// CHECK-NEXT: [[TMP0:%.*]] = tail call i1 @llvm.type.test(ptr [[FN]], metadata !"_ZTSFvu3i32S_S_E.normalized"), !dbg [[DBG58:![0-9]+]], !nosanitize [[META25]] -// CHECK-NEXT: br i1 [[TMP0]], label %[[CONT:.*]], label %[[TRAP:.*]], !dbg [[DBG58]], !prof [[PROF26]], !nosanitize [[META25]] +// CHECK-NEXT: #dbg_value(ptr [[FN]], [[META54:![0-9]+]], !DIExpression(), [[META60:![0-9]+]]) +// CHECK-NEXT: #dbg_value(i32 [[ARG1]], [[META55:![0-9]+]], !DIExpression(), [[META60]]) +// CHECK-NEXT: #dbg_value(i32 [[ARG2]], [[META56:![0-9]+]], !DIExpression(), [[META60]]) +// CHECK-NEXT: #dbg_value(i32 [[ARG3]], [[META57:![0-9]+]], !DIExpression(), [[META60]]) +// CHECK-NEXT: [[TMP0:%.*]] = tail call i1 @llvm.type.test(ptr [[FN]], metadata !"_ZTSFvu3i32S_S_E.normalized"), !dbg [[DBG61:![0-9]+]], !nosanitize [[META25]] +// CHECK-NEXT: br i1 [[TMP0]], label %[[CONT:.*]], label %[[TRAP:.*]], !dbg [[DBG61]], !prof [[PROF26]], !nosanitize [[META25]] // CHECK: [[TRAP]]: -// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 2) #[[ATTR3]], !dbg [[DBG58]], !nosanitize [[META25]] -// CHECK-NEXT: unreachable, !dbg [[DBG58]], !nosanitize [[META25]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 2) #[[ATTR3]], !dbg [[DBG63:![0-9]+]], !nosanitize [[META25]] +// CHECK-NEXT: unreachable, !dbg [[DBG63]], !nosanitize [[META25]] // CHECK: [[CONT]]: -// CHECK-NEXT: tail call void [[FN]](i32 noundef [[ARG1]], i32 noundef [[ARG2]], i32 noundef [[ARG3]]) #[[ATTR4]], !dbg [[DBG59:![0-9]+]] -// CHECK-NEXT: ret void, !dbg [[DBG60:![0-9]+]] +// CHECK-NEXT: tail call void [[FN]](i32 noundef [[ARG1]], i32 noundef [[ARG2]], i32 noundef [[ARG3]]) #[[ATTR4]], !dbg [[DBG62:![0-9]+]] +// CHECK-NEXT: ret void, !dbg [[DBG64:![0-9]+]] // void baz(void (*fn)(int, int, int), int arg1, int arg2, int arg3) { fn(arg1, arg2, arg3); @@ -87,38 +87,42 @@ void baz(void (*fn)(int, int, int), int arg1, int arg2, int arg3) { // CHECK: [[DBG24]] = !DILocation(line: 25, column: 5, scope: [[DBG7]]) // CHECK: [[META25]] = !{} // CHECK: [[PROF26]] = !{!"branch_weights", i32 1048575, i32 1} -// CHECK: [[DBG27]] = !DILocation(line: 26, column: 1, scope: [[DBG7]]) -// CHECK: [[DBG28]] = distinct !DISubprogram(name: "bar", scope: [[META8]], file: [[META8]], line: 43, type: [[META29:![0-9]+]], scopeLine: 43, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: [[META0]], retainedNodes: [[META34:![0-9]+]]) -// CHECK: [[META29]] = !DISubroutineType(types: [[META30:![0-9]+]]) -// CHECK: [[META30]] = !{null, [[META31:![0-9]+]], [[META14]], [[META14]]} -// CHECK: [[META31]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: [[META32:![0-9]+]], size: 64) -// CHECK: [[META32]] = !DISubroutineType(types: [[META33:![0-9]+]]) -// CHECK: [[META33]] = !{null, [[META14]], [[META14]]} -// CHECK: [[META34]] = !{[[META35]], [[META36]], [[META37]]} -// CHECK: [[META35]] = !DILocalVariable(name: "fn", arg: 1, scope: [[DBG28]], file: [[META8]], line: 43, type: [[META31]]) -// CHECK: [[META36]] = !DILocalVariable(name: "arg1", arg: 2, scope: [[DBG28]], file: [[META8]], line: 43, type: [[META14]]) -// CHECK: [[META37]] = !DILocalVariable(name: "arg2", arg: 3, scope: [[DBG28]], file: [[META8]], line: 43, type: [[META14]]) -// CHECK: [[META38]] = !{i64 0, !"_ZTSFvPFvu3i32S_ES_S_E.normalized"} -// CHECK: [[META39]] = !{i64 0, !"_ZTSFvPvu3i32S0_E.normalized.generalized"} -// CHECK: [[META40]] = !DILocation(line: 0, scope: [[DBG28]]) -// CHECK: [[DBG41]] = !DILocation(line: 0, scope: [[META22]], inlinedAt: [[DBG42]]) -// CHECK: [[DBG42]] = !DILocation(line: 44, column: 5, scope: [[DBG28]]) -// CHECK: [[DBG43]] = !DILocation(line: 45, column: 1, scope: [[DBG28]]) -// CHECK: [[DBG44]] = distinct !DISubprogram(name: "baz", scope: [[META8]], file: [[META8]], line: 63, type: [[META45:![0-9]+]], scopeLine: 63, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: [[META0]], retainedNodes: [[META50:![0-9]+]]) -// CHECK: [[META45]] = !DISubroutineType(types: [[META46:![0-9]+]]) -// CHECK: [[META46]] = !{null, [[META47:![0-9]+]], [[META14]], [[META14]], [[META14]]} -// CHECK: [[META47]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: [[META48:![0-9]+]], size: 64) +// CHECK: [[DBGTRAP]] = !DILocation(line: 0, scope: [[TRAPMSG:![0-9]+]], inlinedAt: [[DBG21]]) +// CHECK: [[TRAPMSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Control flow integrity check failed", scope: [[META8]], file: [[META8]], type: [[META23]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META0]]) +// CHECK: [[DBG29]] = !DILocation(line: 26, column: 1, scope: [[DBG7]]) +// CHECK: [[DBG30]] = distinct !DISubprogram(name: "bar", scope: [[META8]], file: [[META8]], line: 43, type: [[META31:![0-9]+]], scopeLine: 43, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: [[META0]], retainedNodes: [[META36:![0-9]+]]) +// CHECK: [[META31]] = !DISubroutineType(types: [[META32:![0-9]+]]) +// CHECK: [[META32]] = !{null, [[META33:![0-9]+]], [[META14]], [[META14]]} +// CHECK: [[META33]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: [[META34:![0-9]+]], size: 64) +// CHECK: [[META34]] = !DISubroutineType(types: [[META35:![0-9]+]]) +// CHECK: [[META35]] = !{null, [[META14]], [[META14]]} +// CHECK: [[META36]] = !{[[META37]], [[META38]], [[META39]]} +// CHECK: [[META37]] = !DILocalVariable(name: "fn", arg: 1, scope: [[DBG30]], file: [[META8]], line: 43, type: [[META33]]) +// CHECK: [[META38]] = !DILocalVariable(name: "arg1", arg: 2, scope: [[DBG30]], file: [[META8]], line: 43, type: [[META14]]) +// CHECK: [[META39]] = !DILocalVariable(name: "arg2", arg: 3, scope: [[DBG30]], file: [[META8]], line: 43, type: [[META14]]) +// CHECK: [[META40]] = !{i64 0, !"_ZTSFvPFvu3i32S_ES_S_E.normalized"} +// CHECK: [[META41]] = !{i64 0, !"_ZTSFvPvu3i32S0_E.normalized.generalized"} +// CHECK: [[META42]] = !DILocation(line: 0, scope: [[DBG30]]) +// CHECK: [[DBG43]] = !DILocation(line: 0, scope: [[META22]], inlinedAt: [[DBG44]]) +// CHECK: [[DBG44]] = !DILocation(line: 44, column: 5, scope: [[DBG30]]) +// CHECK: [[DBG45]] = !DILocation(line: 0, scope: [[TRAPMSG]], inlinedAt: [[DBG43]]) +// CHECK: [[DBG46]] = !DILocation(line: 45, column: 1, scope: [[DBG30]]) +// CHECK: [[DBG47]] = distinct !DISubprogram(name: "baz", scope: [[META8]], file: [[META8]], line: 63, type: [[META48:![0-9]+]], scopeLine: 63, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: [[META0]], retainedNodes: [[META53:![0-9]+]]) // CHECK: [[META48]] = !DISubroutineType(types: [[META49:![0-9]+]]) -// CHECK: [[META49]] = !{null, [[META14]], [[META14]], [[META14]]} -// CHECK: [[META50]] = !{[[META51]], [[META52]], [[META53]], [[META54]]} -// CHECK: [[META51]] = !DILocalVariable(name: "fn", arg: 1, scope: [[DBG44]], file: [[META8]], line: 63, type: [[META47]]) -// CHECK: [[META52]] = !DILocalVariable(name: "arg1", arg: 2, scope: [[DBG44]], file: [[META8]], line: 63, type: [[META14]]) -// CHECK: [[META53]] = !DILocalVariable(name: "arg2", arg: 3, scope: [[DBG44]], file: [[META8]], line: 63, type: [[META14]]) -// CHECK: [[META54]] = !DILocalVariable(name: "arg3", arg: 4, scope: [[DBG44]], file: [[META8]], line: 63, type: [[META14]]) -// CHECK: [[META55]] = !{i64 0, !"_ZTSFvPFvu3i32S_S_ES_S_S_E.normalized"} -// CHECK: [[META56]] = !{i64 0, !"_ZTSFvPvu3i32S0_S0_E.normalized.generalized"} -// CHECK: [[META57]] = !DILocation(line: 0, scope: [[DBG44]]) -// CHECK: [[DBG58]] = !DILocation(line: 0, scope: [[META22]], inlinedAt: [[DBG59]]) -// CHECK: [[DBG59]] = !DILocation(line: 64, column: 5, scope: [[DBG44]]) -// CHECK: [[DBG60]] = !DILocation(line: 65, column: 1, scope: [[DBG44]]) +// CHECK: [[META49]] = !{null, [[META50:![0-9]+]], [[META14]], [[META14]], [[META14]]} +// CHECK: [[META50]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: [[META51:![0-9]+]], size: 64) +// CHECK: [[META51]] = !DISubroutineType(types: [[META52:![0-9]+]]) +// CHECK: [[META52]] = !{null, [[META14]], [[META14]], [[META14]]} +// CHECK: [[META53]] = !{[[META54]], [[META55]], [[META56]], [[META57]]} +// CHECK: [[META54]] = !DILocalVariable(name: "fn", arg: 1, scope: [[DBG47]], file: [[META8]], line: 63, type: [[META50]]) +// CHECK: [[META55]] = !DILocalVariable(name: "arg1", arg: 2, scope: [[DBG47]], file: [[META8]], line: 63, type: [[META14]]) +// CHECK: [[META56]] = !DILocalVariable(name: "arg2", arg: 3, scope: [[DBG47]], file: [[META8]], line: 63, type: [[META14]]) +// CHECK: [[META57]] = !DILocalVariable(name: "arg3", arg: 4, scope: [[DBG47]], file: [[META8]], line: 63, type: [[META14]]) +// CHECK: [[META58]] = !{i64 0, !"_ZTSFvPFvu3i32S_S_ES_S_S_E.normalized"} +// CHECK: [[META59]] = !{i64 0, !"_ZTSFvPvu3i32S0_S0_E.normalized.generalized"} +// CHECK: [[META60]] = !DILocation(line: 0, scope: [[DBG47]]) +// CHECK: [[DBG61]] = !DILocation(line: 0, scope: [[META22]], inlinedAt: [[DBG62]]) +// CHECK: [[DBG62]] = !DILocation(line: 64, column: 5, scope: [[DBG47]]) +// CHECK: [[DBG63]] = !DILocation(line: 0, scope: [[TRAPMSG]], inlinedAt: [[DBG61]]) +// CHECK: [[DBG64]] = !DILocation(line: 65, column: 1, scope: [[DBG47]]) //. diff --git a/clang/test/CodeGen/ubsan-trap-debugloc.c b/clang/test/CodeGen/ubsan-trap-debugloc.c index 87cbfadec7d30..2f5258a6f4ce2 100644 --- a/clang/test/CodeGen/ubsan-trap-debugloc.c +++ b/clang/test/CodeGen/ubsan-trap-debugloc.c @@ -20,5 +20,8 @@ void bar(volatile int a) __attribute__((optnone)) { // CHECK: [[LOC]] = !DILocation(line: 0 // With optimisations disabled the traps are not merged and retain accurate debug locations -// CHECK: [[LOC2]] = !DILocation(line: 15, column: 9 -// CHECK: [[LOC3]] = !DILocation(line: 16, column: 9 + // CHECK-DAG: [[SRC2:![0-9]+]] = !DILocation(line: 15, column: 9, + // CHECK-DAG: [[SRC3:![0-9]+]] = !DILocation(line: 16, column: 9, + // CHECK-DAG: [[LOC2]] = !DILocation(line: 0, scope: [[SCOPE2:![0-9]+]], inlinedAt: [[SRC2]]) + // CHECK-DAG: [[LOC3]] = !DILocation(line: 0, scope: [[SCOPE3:![0-9]+]], inlinedAt: [[SRC3]]) + diff --git a/clang/test/CodeGen/ubsan-trap-reason-add-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-add-overflow.c new file mode 100644 index 0000000000000..225778d68833d --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-add-overflow.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow -emit-llvm %s -o - | FileCheck %s + +int add_overflow(int a, int b) { return a + b; } + +// CHECK-LABEL: @add_overflow +// CHECK: call void @llvm.ubsantrap(i8 0) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer addition overflowed" diff --git a/clang/test/CodeGen/ubsan-trap-reason-alignment-assumption.c b/clang/test/CodeGen/ubsan-trap-reason-alignment-assumption.c new file mode 100644 index 0000000000000..3247ceb4d4a74 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-alignment-assumption.c @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=alignment -fsanitize-trap=alignment -emit-llvm %s -o - | FileCheck %s + +#include +int32_t *get_int(void) __attribute__((assume_aligned(16))); + +void retrieve_int(void) { + int *i = get_int(); + *i = 7; +} + +// CHECK-LABEL: @retrieve_int +// CHECK: call void @llvm.ubsantrap(i8 23) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Alignment assumption violated" diff --git a/clang/test/CodeGen/ubsan-trap-reason-builtin-unreachable.c b/clang/test/CodeGen/ubsan-trap-reason-builtin-unreachable.c new file mode 100644 index 0000000000000..97bd6905bc327 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-builtin-unreachable.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=unreachable -fsanitize-trap=unreachable -emit-llvm %s -o - | FileCheck %s + +int call_builtin_unreachable(void) { __builtin_unreachable(); } + +// CHECK-LABEL: @call_builtin_unreachable +// CHECK: call void @llvm.ubsantrap(i8 1) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$_builtin_unreachable(), execution reached an unreachable program point" diff --git a/clang/test/CodeGen/ubsan-trap-reason-cfi-check-fail.c b/clang/test/CodeGen/ubsan-trap-reason-cfi-check-fail.c new file mode 100644 index 0000000000000..9304f51046c0d --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-cfi-check-fail.c @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=cfi-icall -fsanitize-trap=cfi-icall -emit-llvm %s -o - | FileCheck %s + +typedef int (*fp_t)(int); + +int good(int x) { return x + 1; } + +int bad(void) { return 0; } + +int cfi_trigger(int a) { + fp_t p = good; + int r1 = p(a); + + p = (fp_t)(void *)bad; + int r2 = p(a); + + return r1 + r2; +} + +// CHECK-LABEL: @good +// CHECK-LABEL: @bad +// CHECK-LABEL: @cfi_trigger +// CHECK: call void @llvm.ubsantrap(i8 2) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Control flow integrity check failed" diff --git a/clang/test/CodeGen/ubsan-trap-reason-crash.cpp b/clang/test/CodeGen/ubsan-trap-reason-crash.cpp new file mode 100644 index 0000000000000..6add9bf2b6b34 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-crash.cpp @@ -0,0 +1,20 @@ +// FIXME: We should emit a trap message for this case too. +// But sometimes Clang will emit a ubsan trap into the prologue of a function, +// at which point the debug-info locations haven't been set up yet and +// can't hook up our artificial inline frame. [Issue #150707] + +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=null -fsanitize-trap=null -emit-llvm %s -o - | FileCheck %s + +struct Foo { + void target() {} +} f; + +void caller() { + f.target(); +} + + +// CHECK-LABEL: @_Z6callerv +// CHECK: call void @llvm.ubsantrap(i8 22){{.*}}!nosanitize +// CHECK-NOT: __clang_trap_msg diff --git a/clang/test/CodeGen/ubsan-trap-reason-div-rem-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-div-rem-overflow.c new file mode 100644 index 0000000000000..d0b21dd173894 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-div-rem-overflow.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow -emit-llvm %s -o - | FileCheck %s + +int div_rem_overflow(int a, int b) { return a / b; } + +// CHECK-LABEL: @div_rem_overflow +// CHECK: call void @llvm.ubsantrap(i8 3) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer divide or remainder overflowed" diff --git a/clang/test/CodeGen/ubsan-trap-reason-dynamic-type-cache-miss.cpp b/clang/test/CodeGen/ubsan-trap-reason-dynamic-type-cache-miss.cpp new file mode 100644 index 0000000000000..f89fbdcf1002f --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-dynamic-type-cache-miss.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=vptr -fsanitize-trap=vptr -emit-llvm %s -o - | FileCheck %s + +struct A { + virtual void foo(); +}; +struct B { + virtual void bar(); +}; + +void A::foo() {} +void B::bar() {} + +int dynamic_type_cache_miss() { + B b; + A &a = reinterpret_cast(b); + a.foo(); + return 0; +} + +// CHECK-LABEL: @_ZN1A3fooEv +// CHECK-LABEL: @_ZN1B3barEv +// CHECK-LABEL: @_Z23dynamic_type_cache_missv +// CHECK: call void @llvm.ubsantrap(i8 4) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Dynamic type cache miss, member call made on an object whose dynamic type differs from the expected type" diff --git a/clang/test/CodeGen/ubsan-trap-reason-flag.c b/clang/test/CodeGen/ubsan-trap-reason-flag.c new file mode 100644 index 0000000000000..5cc16d154bf68 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-flag.c @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow -emit-llvm %s -o - \ +// RUN: | FileCheck %s --check-prefix=ANNOTATE + +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow \ +// RUN: -fsanitize-debug-trap-reasons -emit-llvm %s -o - | FileCheck %s --check-prefix=ANNOTATE + +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow \ +// RUN: -fno-sanitize-debug-trap-reasons -emit-llvm %s -o - | FileCheck %s --check-prefix=NO-ANNOTATE + +int add_overflow(int a, int b) { return a + b; } + +// ANNOTATE-LABEL: @add_overflow +// ANNOTATE: call void @llvm.ubsantrap(i8 0) {{.*}}!dbg [[LOC:![0-9]+]] +// ANNOTATE: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// ANNOTATE: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer addition overflowed" + +// NO-ANNOTATE-LABEL: @add_overflow +// NO-ANNOTATE: call void @llvm.ubsantrap(i8 0) {{.*}}!dbg [[LOC:![0-9]+]] +// NO-ANNOTATE-NOT: __clang_trap_msg diff --git a/clang/test/CodeGen/ubsan-trap-reason-float-cast-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-float-cast-overflow.c new file mode 100644 index 0000000000000..079a191e05d4b --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-float-cast-overflow.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=float-cast-overflow -fsanitize-trap=float-cast-overflow -emit-llvm %s -o - | FileCheck %s + +int float_cast_overflow(float x) { return (int)x; } + +// CHECK-LABEL: @float_cast_overflow +// CHECK: call void @llvm.ubsantrap(i8 5) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Floating-point to integer conversion overflowed" diff --git a/clang/test/CodeGen/ubsan-trap-reason-function-type-mismatch.c b/clang/test/CodeGen/ubsan-trap-reason-function-type-mismatch.c new file mode 100644 index 0000000000000..1727f9c092a4c --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-function-type-mismatch.c @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=function -fsanitize-trap=function -emit-llvm %s -o - | FileCheck %s + +void target(void) {} + +int function_type_mismatch(void) { + int (*fp_int)(int); + + fp_int = (int (*)(int))(void *)target; + + return fp_int(42); +} + +// CHECK-LABEL: @target +// CHECK-LABEL: @function_type_mismatch +// CHECK: call void @llvm.ubsantrap(i8 6) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Function called with mismatched signature" diff --git a/clang/test/CodeGen/ubsan-trap-reason-implicit-conversion.c b/clang/test/CodeGen/ubsan-trap-reason-implicit-conversion.c new file mode 100644 index 0000000000000..43c091d51a5c2 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-implicit-conversion.c @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=implicit-unsigned-integer-truncation -fsanitize-trap=implicit-unsigned-integer-truncation -emit-llvm %s -o - | FileCheck %s + +unsigned long long big; + +unsigned implicit_conversion(void) { return big; } + +// CHECK-LABEL: @implicit_conversion +// CHECK: call void @llvm.ubsantrap(i8 7) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Implicit integer conversion overflowed or lost data" diff --git a/clang/test/CodeGen/ubsan-trap-reason-invalid-builtin.c b/clang/test/CodeGen/ubsan-trap-reason-invalid-builtin.c new file mode 100644 index 0000000000000..56cf6744277df --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-invalid-builtin.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=builtin -fsanitize-trap=builtin -emit-llvm %s -o - | FileCheck %s + +unsigned invalid_builtin(unsigned x) { return __builtin_clz(x); } + +// CHECK-LABEL: @invalid_builtin +// CHECK: call void @llvm.ubsantrap(i8 8) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Invalid use of builtin function" diff --git a/clang/test/CodeGen/ubsan-trap-reason-invalid-objc-cast.m b/clang/test/CodeGen/ubsan-trap-reason-invalid-objc-cast.m new file mode 100644 index 0000000000000..ed2d5ffe1600c --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-invalid-objc-cast.m @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=objc-cast -fsanitize-trap=objc-cast -emit-llvm %s -o - | FileCheck %s + +@interface NSFastEnumerationState +@end + +#define NSUInteger unsigned int + +@interface NSArray ++(NSArray*) arrayWithObjects: (id) first, ...; +- (NSUInteger) countByEnumeratingWithState:(NSFastEnumerationState *) state + objects:(id[]) buffer + count:(NSUInteger) len; +-(unsigned) count; +@end +@interface NSString +-(const char*) cString; +@end + +void receive_NSString(NSString*); + +void t0(void) { + NSArray *array = [NSArray arrayWithObjects: @"0", @"1", (void*)0]; + for (NSString *i in array) { + receive_NSString(i); + } +} + +// CHECK-LABEL: @t0 +// CHECK: call void @llvm.ubsantrap(i8 9) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Invalid Objective-C cast" diff --git a/clang/test/CodeGen/ubsan-trap-reason-load-invalid-value.c b/clang/test/CodeGen/ubsan-trap-reason-load-invalid-value.c new file mode 100644 index 0000000000000..4aad8325c5119 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-load-invalid-value.c @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=bool -fsanitize-trap=bool -emit-llvm %s -o - | FileCheck %s + +#include + +unsigned char bad_byte; + +bool load_invalid_value(void) { return *((bool *)&bad_byte); } + +// CHECK-LABEL: @load_invalid_value +// CHECK: call void @llvm.ubsantrap(i8 10) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Loaded an invalid or uninitialized value for the type" diff --git a/clang/test/CodeGen/ubsan-trap-reason-missing-return.cpp b/clang/test/CodeGen/ubsan-trap-reason-missing-return.cpp new file mode 100644 index 0000000000000..2818d9d202720 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-missing-return.cpp @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=return -fsanitize-trap=return -emit-llvm %s -o - | FileCheck %s + +int missing_return(int x) { + if (x > 0) + return x; +} + +// CHECK-LABEL: @_Z14missing_return +// CHECK: call void @llvm.ubsantrap(i8 11) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Execution reached the end of a value-returning function without returning a value" diff --git a/clang/test/CodeGen/ubsan-trap-reason-mul-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-mul-overflow.c new file mode 100644 index 0000000000000..cf9a0b4e7439c --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-mul-overflow.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow -emit-llvm %s -o - | FileCheck %s + +int mul_overflow(int a, int b) { return a * b; } + +// CHECK-LABEL: @mul_overflow +// CHECK: call void @llvm.ubsantrap(i8 12) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer multiplication overflowed" diff --git a/clang/test/CodeGen/ubsan-trap-reason-negate-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-negate-overflow.c new file mode 100644 index 0000000000000..55346794b2928 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-negate-overflow.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow -emit-llvm %s -o - | FileCheck %s + +int negate_overflow() { + int x; + return -x; +} + +// CHECK-LABEL: @negate_overflow +// CHECK: call void @llvm.ubsantrap(i8 13) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer negation overflowed" diff --git a/clang/test/CodeGen/ubsan-trap-reason-nonnull-arg.c b/clang/test/CodeGen/ubsan-trap-reason-nonnull-arg.c new file mode 100644 index 0000000000000..1f0f450d86180 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-nonnull-arg.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=nonnull-attribute -fsanitize-trap=nonnull-attribute -emit-llvm %s -o - | FileCheck %s + +__attribute__((nonnull)) void nonnull_arg(int *p) { (void)p; } + +void trigger_nonnull_arg() { nonnull_arg(0); } + +// CHECK-LABEL: @nonnull_arg +// CHECK-LABEL: @trigger_nonnull_arg +// CHECK: call void @llvm.ubsantrap(i8 16) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Passing null pointer as an argument which is declared to never be null" diff --git a/clang/test/CodeGen/ubsan-trap-reason-nonnull-return.c b/clang/test/CodeGen/ubsan-trap-reason-nonnull-return.c new file mode 100644 index 0000000000000..1197b4dbafea5 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-nonnull-return.c @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=returns-nonnull-attribute -fsanitize-trap=returns-nonnull-attribute -emit-llvm %s -o - | FileCheck %s + +__attribute__((returns_nonnull)) int *must_return_nonnull(int bad) { + if (bad) + return 0; + static int x = 1; + return &x; +} + +// CHECK-LABEL: @must_return_nonnull +// CHECK: call void @llvm.ubsantrap(i8 17) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Returning null pointer from a function which is declared to never return null" diff --git a/clang/test/CodeGen/ubsan-trap-reason-nullability-arg.c b/clang/test/CodeGen/ubsan-trap-reason-nullability-arg.c new file mode 100644 index 0000000000000..2bc71dec67c93 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-nullability-arg.c @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=nullability-arg -fsanitize-trap=nullability-arg -emit-llvm %s -o - | FileCheck %s + +#include + +int nullability_arg(int *_Nonnull p) { return *p; } + +int trigger_nullability_arg(void) { return nullability_arg(NULL); } + +// CHECK-LABEL: @nullability_arg +// CHECK-LABEL: @trigger_nullability_arg +// CHECK: call void @llvm.ubsantrap(i8 14) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Passing null as an argument which is annotated with _Nonnull" diff --git a/clang/test/CodeGen/ubsan-trap-reason-nullability-return.c b/clang/test/CodeGen/ubsan-trap-reason-nullability-return.c new file mode 100644 index 0000000000000..3d64c5a71a9e7 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-nullability-return.c @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=nullability-return -fsanitize-trap=nullability-return -emit-llvm %s -o - | FileCheck %s + +#include +#include + +int *_Nonnull nullability_return(bool fail) { + if (fail) + return NULL; + + static int x = 0; + return &x; +} + +// CHECK-LABEL: @nullability_return +// CHECK: call void @llvm.ubsantrap(i8 15) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Returning null from a function with a return type annotated with _Nonnull" diff --git a/clang/test/CodeGen/ubsan-trap-reason-out-of-bounds.c b/clang/test/CodeGen/ubsan-trap-reason-out-of-bounds.c new file mode 100644 index 0000000000000..979886da6d25b --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-out-of-bounds.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=array-bounds -fsanitize-trap=array-bounds -emit-llvm %s -o - | FileCheck %s + +int out_of_bounds() { + int a[1] = {0}; + return a[1]; +} + +// CHECK-LABEL: @out_of_bounds +// CHECK: call void @llvm.ubsantrap(i8 18) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Array index out of bounds" diff --git a/clang/test/CodeGen/ubsan-trap-reason-pointer-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-pointer-overflow.c new file mode 100644 index 0000000000000..41cb4873a423c --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-pointer-overflow.c @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - | FileCheck %s + +#include +#include + +int *pointer_overflow(void) { + int buf[4]; + volatile size_t n = (SIZE_MAX / sizeof(int)) - 1; + return buf + n; +} + +// CHECK-LABEL: @pointer_overflow +// CHECK: call void @llvm.ubsantrap(i8 19) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Pointer arithmetic overflowed bounds" diff --git a/clang/test/CodeGen/ubsan-trap-reason-shift-out-of-bounds.c b/clang/test/CodeGen/ubsan-trap-reason-shift-out-of-bounds.c new file mode 100644 index 0000000000000..1a7465d93aef5 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-shift-out-of-bounds.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=shift-base -fsanitize-trap=shift-base -emit-llvm %s -o - | FileCheck %s + +int shift_out_of_bounds(void) { + int sh = 32; + return 1 << sh; +} + +// CHECK-LABEL: @shift_out_of_bounds +// CHECK: call void @llvm.ubsantrap(i8 20) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Shift exponent is too large for the type" diff --git a/clang/test/CodeGen/ubsan-trap-reason-sub-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-sub-overflow.c new file mode 100644 index 0000000000000..62aa7fc953dad --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-sub-overflow.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow -emit-llvm %s -o - | FileCheck %s + +int sub_overflow(int a, int b) { return a - b; } + +// CHECK-LABEL: @sub_overflow +// CHECK: call void @llvm.ubsantrap(i8 21) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer subtraction overflowed" diff --git a/clang/test/CodeGen/ubsan-trap-reason-type-mismatch.c b/clang/test/CodeGen/ubsan-trap-reason-type-mismatch.c new file mode 100644 index 0000000000000..802ec91b53a0d --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-type-mismatch.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=alignment -fsanitize-trap=alignment -emit-llvm %s -o - | FileCheck %s + +int type_mismatch(int *p) { return *p; } + +// CHECK-LABEL: @type_mismatch +// CHECK: call void @llvm.ubsantrap(i8 22) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Type mismatch in operation" diff --git a/clang/test/CodeGen/ubsan-trap-reason-vla-bound-not-positive.c b/clang/test/CodeGen/ubsan-trap-reason-vla-bound-not-positive.c new file mode 100644 index 0000000000000..ad9c408b5e14d --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-vla-bound-not-positive.c @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=vla-bound -fsanitize-trap=vla-bound -emit-llvm %s -o - | FileCheck %s + +int n = 0; + +int vla_bound_not_positive(void) { + int a[n]; + return sizeof a; +} + +// CHECK-LABEL: @vla_bound_not_positive +// CHECK: call void @llvm.ubsantrap(i8 24) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Variable length array bound evaluates to non-positive value" From af4e55cf2c7d71a23a3094b25d7a6c939db0670e Mon Sep 17 00:00:00 2001 From: Anthony Tran Date: Wed, 30 Jul 2025 17:13:49 -0700 Subject: [PATCH 2/6] [lldb][test][NFC] Add LLDB test for UBSan trap frame recognizer (#151231) In #145967 Clang was taught to emit trap reasons on UBSan traps in debug info using the same method as `__builtin_verbose_trap`. This patch adds a test case to make sure that the existing "Verbose Trap StackFrame Recognizer" recognizes the trap reason and sets the stop reason and stack frame appropriately. Part of a GSoC 2025 Project. (cherry picked from commit a8d0ae3412bdbbf3248192c31f94f6649a217b3a) --- .../Recognizer/Inputs/ubsan_add_overflow.c | 3 +++ .../Shell/Recognizer/ubsan_add_overflow.test | 20 +++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 lldb/test/Shell/Recognizer/Inputs/ubsan_add_overflow.c create mode 100644 lldb/test/Shell/Recognizer/ubsan_add_overflow.test diff --git a/lldb/test/Shell/Recognizer/Inputs/ubsan_add_overflow.c b/lldb/test/Shell/Recognizer/Inputs/ubsan_add_overflow.c new file mode 100644 index 0000000000000..9f12c321bf409 --- /dev/null +++ b/lldb/test/Shell/Recognizer/Inputs/ubsan_add_overflow.c @@ -0,0 +1,3 @@ +#include + +int main() { return INT_MAX + 1; } diff --git a/lldb/test/Shell/Recognizer/ubsan_add_overflow.test b/lldb/test/Shell/Recognizer/ubsan_add_overflow.test new file mode 100644 index 0000000000000..9dac039b27111 --- /dev/null +++ b/lldb/test/Shell/Recognizer/ubsan_add_overflow.test @@ -0,0 +1,20 @@ +# RUN: %clang_host -g -O0 %S/Inputs/ubsan_add_overflow.c -o %t.out \ +# RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow + +# RUN: %lldb -b -s %s %t.out | FileCheck %s + +run +# CHECK: thread #{{.*}} stop reason = Undefined Behavior Sanitizer: Integer addition overflowed +# CHECK-NEXT: frame #1: {{.*}}`main at ubsan_add_overflow.c + +bt +# CHECK: frame #0: {{.*}}`__clang_trap_msg$Undefined Behavior Sanitizer$Integer addition overflowed{{.*}} +# CHECK: frame #1: {{.*}}`main at ubsan_add_overflow.c + +frame info +# CHECK: frame #{{.*}}`main at ubsan_add_overflow.c + +frame recognizer info 0 +# CHECK: frame 0 is recognized by Verbose Trap StackFrame Recognizer + +quit \ No newline at end of file From 1f42eeec8a337e454d86803f28322520dd57d50e Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Thu, 31 Jul 2025 08:58:00 +0100 Subject: [PATCH 3/6] [lldb][test] Skip Recognizer/ubsan_add_overflow.test on Windows Same reason we skip the other verbose_trap tests. Fails on Windows CI (cherry picked from commit 21bf2fa77fbf19d0a02ced63e4caa37a8c65fb07) --- lldb/test/Shell/Recognizer/ubsan_add_overflow.test | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lldb/test/Shell/Recognizer/ubsan_add_overflow.test b/lldb/test/Shell/Recognizer/ubsan_add_overflow.test index 9dac039b27111..a5e95cf5a898f 100644 --- a/lldb/test/Shell/Recognizer/ubsan_add_overflow.test +++ b/lldb/test/Shell/Recognizer/ubsan_add_overflow.test @@ -1,3 +1,5 @@ +# UNSUPPORTED: system-windows + # RUN: %clang_host -g -O0 %S/Inputs/ubsan_add_overflow.c -o %t.out \ # RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow @@ -17,4 +19,4 @@ frame info frame recognizer info 0 # CHECK: frame 0 is recognized by Verbose Trap StackFrame Recognizer -quit \ No newline at end of file +quit From 5f8b450cd53b2eca4f9a02484c94705d5b291670 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Thu, 21 Aug 2025 22:06:49 +0100 Subject: [PATCH 4/6] [NFC][Diagnostics] Reformat `DiagnosticIDs.h` and `AllDiagnostics.h` (#154628) When trying to add a new diagnostic category (e.g. #154618) I discovered `clang-format` really wanted to reformat these files. My initial attempt was just to suppress the reformatting with `// clang-format (on|off)` directives but reviewers preferred just reformatting the files so these two files have been completely reformatted. `clang-format` has been disabled for the enum that declares the `DIAG_START_*` constants because its much less readable after formatting. (cherry picked from commit 09612007f1d1f431e4418acbe3373b14b21fb913) Conflicts: clang/include/clang/Basic/DiagnosticIDs.h --- clang/include/clang/Basic/AllDiagnostics.h | 10 +- clang/include/clang/Basic/DiagnosticIDs.h | 151 +++++++++++---------- 2 files changed, 82 insertions(+), 79 deletions(-) diff --git a/clang/include/clang/Basic/AllDiagnostics.h b/clang/include/clang/Basic/AllDiagnostics.h index b71748483b065..dc26f3105731f 100644 --- a/clang/include/clang/Basic/AllDiagnostics.h +++ b/clang/include/clang/Basic/AllDiagnostics.h @@ -24,20 +24,20 @@ #include "clang/Basic/DiagnosticInstallAPI.h" #include "clang/Basic/DiagnosticLex.h" #include "clang/Basic/DiagnosticParse.h" +#include "clang/Basic/DiagnosticRefactoring.h" #include "clang/Basic/DiagnosticSema.h" #include "clang/Basic/DiagnosticSerialization.h" -#include "clang/Basic/DiagnosticRefactoring.h" namespace clang { -template -class StringSizerHelper { +template class StringSizerHelper { static_assert(SizeOfStr <= FieldType(~0U), "Field too small!"); + public: enum { Size = SizeOfStr }; }; } // end namespace clang -#define STR_SIZE(str, fieldTy) clang::StringSizerHelper::Size +#define STR_SIZE(str, fieldTy) \ + clang::StringSizerHelper::Size #endif diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h index 69a90f761a21c..fe50eea182bac 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -23,78 +23,80 @@ #include namespace clang { - class DiagnosticsEngine; - class DiagnosticBuilder; - class LangOptions; - class SourceLocation; - - // Import the diagnostic enums themselves. - namespace diag { - enum class Group; - - // Size of each of the diagnostic categories. - enum { - DIAG_SIZE_COMMON = 300, - DIAG_SIZE_DRIVER = 400, - DIAG_SIZE_FRONTEND = 200, - DIAG_SIZE_SERIALIZATION = 120, - DIAG_SIZE_LEX = 500, - DIAG_SIZE_PARSE = 800, - DIAG_SIZE_AST = 300, - DIAG_SIZE_COMMENT = 100, - DIAG_SIZE_CROSSTU = 100, - DIAG_SIZE_SEMA = 5000, - DIAG_SIZE_ANALYSIS = 100, - DIAG_SIZE_REFACTORING = 1000, - DIAG_SIZE_INSTALLAPI = 100, - DIAG_SIZE_CAS = 100, - }; - // Start position for diagnostics. - enum { - DIAG_START_COMMON = 0, - DIAG_START_DRIVER = DIAG_START_COMMON + static_cast(DIAG_SIZE_COMMON), - DIAG_START_FRONTEND = DIAG_START_DRIVER + static_cast(DIAG_SIZE_DRIVER), - DIAG_START_SERIALIZATION = DIAG_START_FRONTEND + static_cast(DIAG_SIZE_FRONTEND), - DIAG_START_LEX = DIAG_START_SERIALIZATION + static_cast(DIAG_SIZE_SERIALIZATION), - DIAG_START_PARSE = DIAG_START_LEX + static_cast(DIAG_SIZE_LEX), - DIAG_START_AST = DIAG_START_PARSE + static_cast(DIAG_SIZE_PARSE), - DIAG_START_COMMENT = DIAG_START_AST + static_cast(DIAG_SIZE_AST), - DIAG_START_CROSSTU = DIAG_START_COMMENT + static_cast(DIAG_SIZE_COMMENT), - DIAG_START_SEMA = DIAG_START_CROSSTU + static_cast(DIAG_SIZE_CROSSTU), - DIAG_START_ANALYSIS = DIAG_START_SEMA + static_cast(DIAG_SIZE_SEMA), - DIAG_START_REFACTORING = DIAG_START_ANALYSIS + static_cast(DIAG_SIZE_ANALYSIS), - DIAG_START_INSTALLAPI = DIAG_START_REFACTORING + static_cast(DIAG_SIZE_REFACTORING), - DIAG_START_CAS = DIAG_START_INSTALLAPI + static_cast(DIAG_START_INSTALLAPI), - DIAG_UPPER_LIMIT = DIAG_START_CAS + static_cast(DIAG_SIZE_CAS) - }; - - class CustomDiagInfo; - - /// All of the diagnostics that can be emitted by the frontend. - typedef unsigned kind; - - /// Enum values that allow the client to map NOTEs, WARNINGs, and EXTENSIONs - /// to either Ignore (nothing), Remark (emit a remark), Warning - /// (emit a warning) or Error (emit as an error). It allows clients to - /// map ERRORs to Error or Fatal (stop emitting diagnostics after this one). - enum class Severity : uint8_t { - // NOTE: 0 means "uncomputed". - Ignored = 1, ///< Do not present this diagnostic, ignore it. - Remark = 2, ///< Present this diagnostic as a remark. - Warning = 3, ///< Present this diagnostic as a warning. - Error = 4, ///< Present this diagnostic as an error. - Fatal = 5 ///< Present this diagnostic as a fatal error. - }; - - /// Flavors of diagnostics we can emit. Used to filter for a particular - /// kind of diagnostic (for instance, for -W/-R flags). - enum class Flavor { - WarningOrError, ///< A diagnostic that indicates a problem or potential - ///< problem. Can be made fatal by -Werror. - Remark ///< A diagnostic that indicates normal progress through - ///< compilation. - }; - } // end namespace diag +class DiagnosticsEngine; +class DiagnosticBuilder; +class LangOptions; +class SourceLocation; + +// Import the diagnostic enums themselves. +namespace diag { +enum class Group; + +// Size of each of the diagnostic categories. +enum { + DIAG_SIZE_COMMON = 300, + DIAG_SIZE_DRIVER = 400, + DIAG_SIZE_FRONTEND = 200, + DIAG_SIZE_SERIALIZATION = 120, + DIAG_SIZE_LEX = 500, + DIAG_SIZE_PARSE = 800, + DIAG_SIZE_AST = 300, + DIAG_SIZE_COMMENT = 100, + DIAG_SIZE_CROSSTU = 100, + DIAG_SIZE_SEMA = 5000, + DIAG_SIZE_ANALYSIS = 100, + DIAG_SIZE_REFACTORING = 1000, + DIAG_SIZE_INSTALLAPI = 100, + DIAG_SIZE_CAS = 100, +}; +// Start position for diagnostics. +// clang-format off +enum { + DIAG_START_COMMON = 0, + DIAG_START_DRIVER = DIAG_START_COMMON + static_cast(DIAG_SIZE_COMMON), + DIAG_START_FRONTEND = DIAG_START_DRIVER + static_cast(DIAG_SIZE_DRIVER), + DIAG_START_SERIALIZATION = DIAG_START_FRONTEND + static_cast(DIAG_SIZE_FRONTEND), + DIAG_START_LEX = DIAG_START_SERIALIZATION + static_cast(DIAG_SIZE_SERIALIZATION), + DIAG_START_PARSE = DIAG_START_LEX + static_cast(DIAG_SIZE_LEX), + DIAG_START_AST = DIAG_START_PARSE + static_cast(DIAG_SIZE_PARSE), + DIAG_START_COMMENT = DIAG_START_AST + static_cast(DIAG_SIZE_AST), + DIAG_START_CROSSTU = DIAG_START_COMMENT + static_cast(DIAG_SIZE_COMMENT), + DIAG_START_SEMA = DIAG_START_CROSSTU + static_cast(DIAG_SIZE_CROSSTU), + DIAG_START_ANALYSIS = DIAG_START_SEMA + static_cast(DIAG_SIZE_SEMA), + DIAG_START_REFACTORING = DIAG_START_ANALYSIS + static_cast(DIAG_SIZE_ANALYSIS), + DIAG_START_INSTALLAPI = DIAG_START_REFACTORING + static_cast(DIAG_SIZE_REFACTORING), + DIAG_START_CAS = DIAG_START_INSTALLAPI + static_cast(DIAG_SIZE_INSTALLAPI), + DIAG_UPPER_LIMIT = DIAG_START_CAS + static_cast(DIAG_SIZE_CAS) +}; +// clang-format on + +class CustomDiagInfo; + +/// All of the diagnostics that can be emitted by the frontend. +typedef unsigned kind; + +/// Enum values that allow the client to map NOTEs, WARNINGs, and EXTENSIONs +/// to either Ignore (nothing), Remark (emit a remark), Warning +/// (emit a warning) or Error (emit as an error). It allows clients to +/// map ERRORs to Error or Fatal (stop emitting diagnostics after this one). +enum class Severity : uint8_t { + // NOTE: 0 means "uncomputed". + Ignored = 1, ///< Do not present this diagnostic, ignore it. + Remark = 2, ///< Present this diagnostic as a remark. + Warning = 3, ///< Present this diagnostic as a warning. + Error = 4, ///< Present this diagnostic as an error. + Fatal = 5 ///< Present this diagnostic as a fatal error. +}; + +/// Flavors of diagnostics we can emit. Used to filter for a particular +/// kind of diagnostic (for instance, for -W/-R flags). +enum class Flavor { + WarningOrError, ///< A diagnostic that indicates a problem or potential + ///< problem. Can be made fatal by -Werror. + Remark ///< A diagnostic that indicates normal progress through + ///< compilation. +}; +} // end namespace diag } // end namespace clang // This has to be included *after* the DIAG_START_ enums above are defined. @@ -175,7 +177,8 @@ class DiagnosticMapping { /// Used for handling and querying diagnostic IDs. /// -/// Can be used and shared by multiple Diagnostics for multiple translation units. +/// Can be used and shared by multiple Diagnostics for multiple translation +/// units. class DiagnosticIDs : public RefCountedBase { public: /// The level of the diagnostic, after it has been through mapping. @@ -496,6 +499,6 @@ class DiagnosticIDs : public RefCountedBase { friend class DiagnosticsEngine; }; -} // end namespace clang +} // end namespace clang #endif From 4a7139b418c2991ab5e4c2a7f864cb8280743f8b Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Wed, 27 Aug 2025 14:49:11 -0700 Subject: [PATCH 5/6] [UBSan][BoundsSafety] Implement support for more expressive "trap reasons" (#154618) In 29992cfd628ed5b968ccb73b17ed0521382ba317 (#145967) support was added for "trap reasons" on traps emitted in UBSan in trapping mode (e.g. `-fsanitize-trap=undefined`). This improved the debugging experience by attaching the reason for trapping as a string on the debug info on trap instructions. Consumers such as LLDB can display this trap reason string when the trap is reached. A limitation of that patch is that the trap reason string is hard-coded for each `SanitizerKind` even though the compiler actually has much more information about the trap available at compile time that could be shown to the user. This patch is an incremental step in fixing that. It consists of two main steps. **1. Introduce infrastructure for building trap reason strings** To make it convenient to construct trap reason strings this patch re-uses Clang's powerful diagnostic infrastructure to provide a convenient API for constructing trap reason strings. This is achieved by: * Introducing a new `Trap` diagnostic kind to represent trap diagnostics in TableGen files. * Adding a new `Trap` diagnostic component. While this part probably isn't technically necessary it seemed like I should follow the existing convention used by the diagnostic system. * Adding `DiagnosticTrapKinds.td` to describe the different trap reasons. * Add the `TrapReasonBuilder` and `TrapReason` classes to provide an interface for constructing trap reason strings and the trap category. Note this API while similar to `DiagnosticBuilder` has different semantics which are described in the code comments. In particular the behavior when the destructor is called is very different. * Adding `CodeGenModule::BuildTrapReason()` as a convenient constructor for the `TrapReasonBuilder`. This use of the diagnostic system is a little unusual in that the emitted trap diagnostics aren't actually consumed by normal diagnostic consumers (e.g. the console). Instead the `TrapReasonBuilder` is just used to format a string, so in effect the builder is somewhat analagous to "printf". However, re-using the diagnostics system in this way brings a several benefits: * The powerful diagnostic templating languge (e.g. `%select`) can be used. * Formatting Clang data types (e.g. `Type`, `Expr`, etc.) just work out-of-the-box. * Describing trap reasons in tablegen files opens the door for translation to different languages in the future. * The `TrapReasonBuilder` API is very similar to `DiagnosticBuilder` which makes it easy to use by anyone already familiar with Clang's diagnostic system. While UBSan is the first consumer of this new infrastructure the intent is to use this to overhaul how trap reasons are implemented in the `-fbounds-safety` implementation (currently exists downstream). **2. Apply the new infrastructure to UBSan checks for arithmetic overflow** To demonstrate using `TrapReasonBuilder` this patch applies it to UBSan traps for arithmetic overflow. The intention is that we would iteratively switch to using the `TrapReasonBuilder` for all UBSan traps where it makes sense in future patches. Previously for code like ``` int test(int a, int b) { return a + b; } ``` The trap reason string looked like ``` Undefined Behavior Sanitizer: Integer addition overflowed ``` now the trap message looks like: ``` Undefined Behavior Sanitizer: signed integer addition overflow in 'a + b' ``` This string is much more specific because * It explains if signed or unsigned overflow occurred * It actually shows the expression that overflowed One possible downside of this approach is it may blow up Debug info size because now there can be many more distinct trap reason strings. To allow users to avoid this a new driver/cc1 flag `-fsanitize-debug-trap-reasons=` has been added which can either be `none` (disable trap reasons entirely), `basic` (use the per `SanitizerKind` hard coded strings), and `detailed` (use the new expressive trap reasons implemented in this patch). The default is `detailed` to give the best out-of-the-box debugging experience. The existing `-fsanitize-debug-trap-reasons` and `-fno-sanitize-debug-trap-reasons` have been kept for compatibility and are aliases of the new flag with `detailed` and `none` arguments passed respectively. rdar://158612755 Conflicts: clang/include/clang/Basic/AllDiagnosticKinds.inc clang/include/clang/Basic/DiagnosticIDs.h clang/lib/Basic/DiagnosticIDs.cpp clang/lib/CodeGen/CGExpr.cpp clang/lib/CodeGen/CodeGenFunction.h clang/docs/ReleaseNotes.rst clang/include/clang/Basic/DiagnosticIDs.h rdar://159302620 (cherry picked from commit 6d385c34d3490223786673cbeb7d5f04c6d05061) (cherry picked from commit f1ee0473209e31b0d05f589e9091ffbbcc60be31) --- clang/docs/ReleaseNotes.rst | 33 ++++-- .../clang/Basic/AllDiagnosticKinds.inc | 2 + clang/include/clang/Basic/AllDiagnostics.h | 1 + clang/include/clang/Basic/CMakeLists.txt | 1 + clang/include/clang/Basic/CodeGenOptions.def | 2 +- clang/include/clang/Basic/CodeGenOptions.h | 10 ++ clang/include/clang/Basic/Diagnostic.h | 6 +- clang/include/clang/Basic/Diagnostic.td | 5 +- clang/include/clang/Basic/DiagnosticIDs.h | 11 +- clang/include/clang/Basic/DiagnosticTrap.h | 14 +++ .../clang/Basic/DiagnosticTrapKinds.td | 30 +++++ clang/include/clang/Driver/Options.td | 31 +++-- clang/lib/Basic/Diagnostic.cpp | 2 + clang/lib/Basic/DiagnosticIDs.cpp | 4 + clang/lib/CodeGen/CGExpr.cpp | 49 +++++--- clang/lib/CodeGen/CGExprScalar.cpp | 39 +++++- clang/lib/CodeGen/CMakeLists.txt | 1 + clang/lib/CodeGen/CodeGenFunction.h | 5 +- clang/lib/CodeGen/CodeGenModule.h | 6 + clang/lib/CodeGen/TrapReasonBuilder.cpp | 50 ++++++++ clang/lib/CodeGen/TrapReasonBuilder.h | 112 ++++++++++++++++++ clang/lib/Driver/SanitizerArgs.cpp | 6 +- .../CodeGen/ubsan-trap-reason-add-overflow.c | 31 ++++- clang/test/CodeGen/ubsan-trap-reason-flag.c | 29 ++++- .../CodeGen/ubsan-trap-reason-mul-overflow.c | 29 ++++- .../CodeGen/ubsan-trap-reason-sub-overflow.c | 29 ++++- .../Driver/fsanitize-debug-trap-reasons.c | 57 +++++++++ .../Frontend/fsanitize-debug-trap-reasons.c | 6 + clang/tools/diagtool/ListWarnings.cpp | 3 + .../Shell/Recognizer/ubsan_add_overflow.test | 4 +- 30 files changed, 536 insertions(+), 72 deletions(-) create mode 100644 clang/include/clang/Basic/DiagnosticTrap.h create mode 100644 clang/include/clang/Basic/DiagnosticTrapKinds.td create mode 100644 clang/lib/CodeGen/TrapReasonBuilder.cpp create mode 100644 clang/lib/CodeGen/TrapReasonBuilder.h create mode 100644 clang/test/Driver/fsanitize-debug-trap-reasons.c create mode 100644 clang/test/Frontend/fsanitize-debug-trap-reasons.c diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 0286c46f19624..b3609073eaa0f 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -366,18 +366,33 @@ Non-comprehensive list of changes in this release correct method to check for these features is to test for the ``__PTRAUTH__`` macro. -- Trapping UBSan (e.g. ``-fsanitize-trap=undefined``) now emits a string describing the reason for - trapping into the generated debug info. This feature allows debuggers (e.g. LLDB) to display - the reason for trapping if the trap is reached. The string is currently encoded in the debug - info as an artificial frame that claims to be inlined at the trap location. The function used - for the artificial frame is an artificial function whose name encodes the reason for trapping. - The encoding used is currently the same as ``__builtin_verbose_trap`` but might change in the future. - This feature is enabled by default but can be disabled by compiling with - ``-fno-sanitize-annotate-debug-info-traps``. +- Trapping UBSan (e.g. ``-fsanitize=undefined -fsanitize-trap=undefined``) now + emits a string describing the reason for trapping into the generated debug + info. This feature allows debuggers (e.g. LLDB) to display the reason for + trapping if the trap is reached. The string is currently encoded in the debug + info as an artificial frame that claims to be inlined at the trap location. + The function used for the artificial frame is an artificial function whose + name encodes the reason for trapping. The encoding used is currently the same + as ``__builtin_verbose_trap`` but might change in the future. This feature is + enabled by default but can be disabled by compiling with + ``-fno-sanitize-debug-trap-reasons``. The feature has a ``basic`` and + ``detailed`` mode (the default). The ``basic`` mode emits a hard-coded string + per trap kind (e.g. ``Integer addition overflowed``) and the ``detailed`` mode + emits a more descriptive string describing each individual trap (e.g. ``signed + integer addition overflow in 'a + b'``). The ``detailed`` mode produces larger + debug info than ``basic`` but is more helpful for debugging. The + ``-fsanitize-debug-trap-reasons=`` flag can be used to switch between the + different modes or disable the feature entirely. Note due to trap merging in + optimized builds (i.e. in each function all traps of the same kind get merged + into the same trap instruction) the trap reasons might be removed. To prevent + this build without optimizations (i.e. use `-O0` or use the `optnone` function + attribute) or use the `fno-sanitize-merge=` flag in optimized builds. New Compiler Flags ------------------ -- New option ``-fno-sanitize-annotate-debug-info-traps`` added to disable emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``). +- New option ``-fno-sanitize-debug-trap-reasons`` added to disable emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``). +- New option ``-fsanitize-debug-trap-reasons=`` added to control emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``). + - New option ``-Wundef-true`` added and enabled by default to warn when `true` is used in the C preprocessor without being defined before C23. diff --git a/clang/include/clang/Basic/AllDiagnosticKinds.inc b/clang/include/clang/Basic/AllDiagnosticKinds.inc index 83498a857eae4..a8c4f911c151b 100644 --- a/clang/include/clang/Basic/AllDiagnosticKinds.inc +++ b/clang/include/clang/Basic/AllDiagnosticKinds.inc @@ -30,5 +30,7 @@ #include "clang/Basic/DiagnosticAnalysisKinds.inc" #include "clang/Basic/DiagnosticRefactoringKinds.inc" #include "clang/Basic/DiagnosticInstallAPIKinds.inc" +#include "clang/Basic/DiagnosticTrapKinds.inc" #include "clang/Basic/DiagnosticCASKinds.inc" + // clang-format on diff --git a/clang/include/clang/Basic/AllDiagnostics.h b/clang/include/clang/Basic/AllDiagnostics.h index dc26f3105731f..9d56344ec5cb0 100644 --- a/clang/include/clang/Basic/AllDiagnostics.h +++ b/clang/include/clang/Basic/AllDiagnostics.h @@ -27,6 +27,7 @@ #include "clang/Basic/DiagnosticRefactoring.h" #include "clang/Basic/DiagnosticSema.h" #include "clang/Basic/DiagnosticSerialization.h" +#include "clang/Basic/DiagnosticTrap.h" namespace clang { template class StringSizerHelper { diff --git a/clang/include/clang/Basic/CMakeLists.txt b/clang/include/clang/Basic/CMakeLists.txt index 91e1c4bc27459..8cd5de10996f4 100644 --- a/clang/include/clang/Basic/CMakeLists.txt +++ b/clang/include/clang/Basic/CMakeLists.txt @@ -34,6 +34,7 @@ clang_diag_gen(Parse) clang_diag_gen(Refactoring) clang_diag_gen(Sema) clang_diag_gen(Serialization) +clang_diag_gen(Trap) clang_tablegen(DiagnosticGroups.inc -gen-clang-diag-groups SOURCE Diagnostic.td TARGET ClangDiagnosticGroups) diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index 47caec13824d2..853b60b26e041 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -306,7 +306,7 @@ CODEGENOPT(SanitizeBinaryMetadataAtomics, 1, 0, Benign) ///< Emit PCs for atomic CODEGENOPT(SanitizeBinaryMetadataUAR, 1, 0, Benign) ///< Emit PCs for start of functions ///< that are subject for use-after-return checking. CODEGENOPT(SanitizeStats , 1, 0, Benign) ///< Collect statistics for sanitizers. -CODEGENOPT(SanitizeDebugTrapReasons, 1, 1 , Benign) ///< Enable UBSan trapping messages +ENUM_CODEGENOPT(SanitizeDebugTrapReasons, SanitizeDebugTrapReasonKind, 2, SanitizeDebugTrapReasonKind::Detailed, Benign) ///< Control how "trap reasons" are emitted in debug info CODEGENOPT(SimplifyLibCalls , 1, 1, Benign) ///< Set when -fbuiltin is enabled. CODEGENOPT(SoftFloat , 1, 0, Benign) ///< -soft-float. CODEGENOPT(SpeculativeLoadHardening, 1, 0, Benign) ///< Enable speculative load hardening. diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index 30583c88d0543..1dd8bd30073cf 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -178,6 +178,16 @@ class CodeGenOptions : public CodeGenOptionsBase { /// The callback for mc result. std::optional MCCallBack; + enum SanitizeDebugTrapReasonKind { + None, ///< Trap Messages are omitted. This offers the smallest debug info + ///< size but at the cost of making traps hard to debug. + Basic, ///< Trap Message is fixed per SanitizerKind. Produces smaller debug + ///< info than `Detailed` but is not as helpful for debugging. + Detailed, ///< Trap Message includes more context (e.g. the expression being + ///< overflowed). This is more helpful for debugging but produces + ///< larger debug info than `Basic`. + }; + /// The code model to use (-mcmodel). std::string CodeModel; diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h index 25e0c44d78d85..33de6ca1ce838 100644 --- a/clang/include/clang/Basic/Diagnostic.h +++ b/clang/include/clang/Basic/Diagnostic.h @@ -23,6 +23,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FunctionExtras.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Support/Compiler.h" @@ -1269,10 +1270,13 @@ class DiagnosticBuilder : public StreamingDiagnostic { DiagnosticBuilder() = default; +protected: DiagnosticBuilder(DiagnosticsEngine *DiagObj, SourceLocation DiagLoc, unsigned DiagID); -protected: + DiagnosticsEngine *getDiagnosticsEngine() const { return DiagObj; } + unsigned getDiagID() const { return DiagID; } + /// Clear out the current diagnostic. void Clear() const { DiagObj = nullptr; diff --git a/clang/include/clang/Basic/Diagnostic.td b/clang/include/clang/Basic/Diagnostic.td index 8f8862773e112..d3d76df004551 100644 --- a/clang/include/clang/Basic/Diagnostic.td +++ b/clang/include/clang/Basic/Diagnostic.td @@ -30,6 +30,7 @@ def CLASS_REMARK : DiagClass; def CLASS_WARNING : DiagClass; def CLASS_EXTENSION : DiagClass; def CLASS_ERROR : DiagClass; +def CLASS_TRAP : DiagClass; // Responses to a diagnostic in a SFINAE context. class SFINAEResponse; @@ -144,7 +145,8 @@ class Extension : Diagnostic; class ExtWarn : Diagnostic; // Notes can provide supplementary information on errors, warnings, and remarks. class Note : Diagnostic; - +// Trap messages attached to traps in debug info. +class Trap : Diagnostic; class DefaultIgnore { Severity DefaultSeverity = SEV_Ignored; } class DefaultWarn { Severity DefaultSeverity = SEV_Warning; } @@ -236,3 +238,4 @@ include "DiagnosticParseKinds.td" include "DiagnosticRefactoringKinds.td" include "DiagnosticSemaKinds.td" include "DiagnosticSerializationKinds.td" +include "DiagnosticTrapKinds.td" diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h index fe50eea182bac..a343a233cd9b8 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -47,6 +47,7 @@ enum { DIAG_SIZE_ANALYSIS = 100, DIAG_SIZE_REFACTORING = 1000, DIAG_SIZE_INSTALLAPI = 100, + DIAG_SIZE_TRAP = 100, DIAG_SIZE_CAS = 100, }; // Start position for diagnostics. @@ -65,7 +66,8 @@ enum { DIAG_START_ANALYSIS = DIAG_START_SEMA + static_cast(DIAG_SIZE_SEMA), DIAG_START_REFACTORING = DIAG_START_ANALYSIS + static_cast(DIAG_SIZE_ANALYSIS), DIAG_START_INSTALLAPI = DIAG_START_REFACTORING + static_cast(DIAG_SIZE_REFACTORING), - DIAG_START_CAS = DIAG_START_INSTALLAPI + static_cast(DIAG_SIZE_INSTALLAPI), + DIAG_START_TRAP = DIAG_START_INSTALLAPI + static_cast(DIAG_SIZE_INSTALLAPI), + DIAG_START_CAS = DIAG_START_TRAP + static_cast(DIAG_SIZE_TRAP), DIAG_UPPER_LIMIT = DIAG_START_CAS + static_cast(DIAG_SIZE_CAS) }; // clang-format on @@ -191,7 +193,8 @@ class DiagnosticIDs : public RefCountedBase { CLASS_REMARK = 0x02, CLASS_WARNING = 0x03, CLASS_EXTENSION = 0x04, - CLASS_ERROR = 0x05 + CLASS_ERROR = 0x05, + CLASS_TRAP = 0x06 }; static bool IsCustomDiag(diag::kind Diag) { @@ -361,6 +364,10 @@ class DiagnosticIDs : public RefCountedBase { /// bool isExtensionDiag(unsigned DiagID, bool &EnabledByDefault) const; + bool isTrapDiag(unsigned DiagID) const { + return getDiagClass(DiagID) == CLASS_TRAP; + } + /// Given a group ID, returns the flag that toggles the group. /// For example, for Group::DeprecatedDeclarations, returns /// "deprecated-declarations". diff --git a/clang/include/clang/Basic/DiagnosticTrap.h b/clang/include/clang/Basic/DiagnosticTrap.h new file mode 100644 index 0000000000000..da8bd257037e9 --- /dev/null +++ b/clang/include/clang/Basic/DiagnosticTrap.h @@ -0,0 +1,14 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_BASIC_DIAGNOSTICTRAP_H +#define LLVM_CLANG_BASIC_DIAGNOSTICTRAP_H + +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticTrapInterface.inc" + +#endif diff --git a/clang/include/clang/Basic/DiagnosticTrapKinds.td b/clang/include/clang/Basic/DiagnosticTrapKinds.td new file mode 100644 index 0000000000000..c17a88d4fb4fb --- /dev/null +++ b/clang/include/clang/Basic/DiagnosticTrapKinds.td @@ -0,0 +1,30 @@ +//==--- DiagnosticTrapKinds.td ------------------------ -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Trap Diagnostics +// +// These are diagnostics that are emitted into `TrapReason` objects using the +// `TrapReasonBuilder` class. These `TrapReason` objects are then encoded into +// debug info during codegen, rather than to the traditional diagnostic +// consumers like the terminal. Their primary purpose is to make debugging traps +// (e.g. `-fsanitize-trap=undefined`) easier by attaching a trap category and +// message to the trap instruction that tools like a debugger can show. +// +//===----------------------------------------------------------------------===// +let Component = "Trap" in { +let CategoryName = "Undefined Behavior Sanitizer" in { + +def trap_ubsan_arith_overflow : Trap< + "%select{unsigned|signed}0 integer " + "%enum_select{" + "%Add{addition}|" + "%Sub{subtraction}|" + "%Mul{multiplication}" + "}1 overflow in %2">; + +} +} diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 4e5605b822023..2c950102626f2 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2717,16 +2717,27 @@ def fsanitize_undefined_trap_on_error def fno_sanitize_undefined_trap_on_error : Flag<["-"], "fno-sanitize-undefined-trap-on-error">, Group, Alias, AliasArgs<["undefined"]>; -defm sanitize_debug_trap_reasons - : BoolFOption< - "sanitize-debug-trap-reasons", - CodeGenOpts<"SanitizeDebugTrapReasons">, DefaultTrue, - PosFlag, - NegFlag>; - +def fsanitize_debug_trap_reasons_EQ + : Joined<["-"], "fsanitize-debug-trap-reasons=">, Group, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Set how trap reasons are emitted. " + "`none` - Not emitted. This gives the smallest debug info; " + "`basic` - Emit a fixed trap message per check type. This increases the " + "debug info size but not as much as `detailed`; " + "`detailed` - Emit a more detailed trap message. This increases the " + "debug info size the most. Default is `detailed`.">, + Values<"none,basic,detailed">, + NormalizedValuesScope<"CodeGenOptions::SanitizeDebugTrapReasonKind">, + NormalizedValues<["None", "Basic", "Detailed"]>, + MarshallingInfoEnum, "Detailed">; +def fsanitize_debug_trap_reasons + : Flag<["-"], "fsanitize-debug-trap-reasons">, Group, + Alias, AliasArgs<["detailed"]>, + HelpText<"Alias for -fsanitize-debug-trap-reasons=detailed">; +def fno_sanitize_debug_trap_reasons + : Flag<["-"], "fno-sanitize-debug-trap-reasons">, Group, + Alias, AliasArgs<["none"]>, + HelpText<"Alias for -fsanitize-debug-trap-reasons=none">; defm sanitize_minimal_runtime : BoolOption<"f", "sanitize-minimal-runtime", CodeGenOpts<"SanitizeMinimalRuntime">, DefaultFalse, PosFlag, diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp index 0806fa4ca8177..17d39ba8d0ce4 100644 --- a/clang/lib/Basic/Diagnostic.cpp +++ b/clang/lib/Basic/Diagnostic.cpp @@ -672,6 +672,8 @@ void DiagnosticsEngine::Report(const StoredDiagnostic &storedDiag) { void DiagnosticsEngine::Report(Level DiagLevel, const Diagnostic &Info) { assert(DiagLevel != Ignored && "Cannot emit ignored diagnostics!"); + assert(!getDiagnosticIDs()->isTrapDiag(Info.getID()) && + "Trap diagnostics should not be consumed by the DiagnosticsEngine"); Client->HandleDiagnostic(DiagLevel, Info); if (Client->IncludeInDiagnosticCounts()) { if (DiagLevel == Warning) diff --git a/clang/lib/Basic/DiagnosticIDs.cpp b/clang/lib/Basic/DiagnosticIDs.cpp index bde0bdd8f46bc..a929f998d050b 100644 --- a/clang/lib/Basic/DiagnosticIDs.cpp +++ b/clang/lib/Basic/DiagnosticIDs.cpp @@ -69,6 +69,7 @@ enum DiagnosticClass { CLASS_WARNING = DiagnosticIDs::CLASS_WARNING, CLASS_EXTENSION = DiagnosticIDs::CLASS_EXTENSION, CLASS_ERROR = DiagnosticIDs::CLASS_ERROR, + CLASS_TRAP = DiagnosticIDs::CLASS_TRAP, }; struct StaticDiagInfoRec { @@ -139,6 +140,7 @@ VALIDATE_DIAG_SIZE(SEMA) VALIDATE_DIAG_SIZE(ANALYSIS) VALIDATE_DIAG_SIZE(REFACTORING) VALIDATE_DIAG_SIZE(INSTALLAPI) +VALIDATE_DIAG_SIZE(TRAP) VALIDATE_DIAG_SIZE(CAS) #undef VALIDATE_DIAG_SIZE #undef STRINGIFY_NAME @@ -172,6 +174,7 @@ const StaticDiagInfoRec StaticDiagInfo[] = { #include "clang/Basic/DiagnosticAnalysisKinds.inc" #include "clang/Basic/DiagnosticRefactoringKinds.inc" #include "clang/Basic/DiagnosticInstallAPIKinds.inc" +#include "clang/Basic/DiagnosticTrapKinds.inc" #include "clang/Basic/DiagnosticCASKinds.inc" // clang-format on #undef DIAG @@ -216,6 +219,7 @@ CATEGORY(SEMA, CROSSTU) CATEGORY(ANALYSIS, SEMA) CATEGORY(REFACTORING, ANALYSIS) CATEGORY(INSTALLAPI, REFACTORING) +CATEGORY(TRAP, INSTALLAPI) CATEGORY(CAS, INSTALLAPI) #undef CATEGORY diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 10f43f697e2b0..bd6b9a6c7e1be 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -4233,7 +4233,7 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF, void CodeGenFunction::EmitCheck( ArrayRef> Checked, SanitizerHandler CheckHandler, ArrayRef StaticArgs, - ArrayRef DynamicArgs) { + ArrayRef DynamicArgs, const TrapReason *TR) { assert(IsSanitizerScope); assert(Checked.size() > 0); assert(CheckHandler >= 0 && @@ -4272,7 +4272,7 @@ void CodeGenFunction::EmitCheck( } if (TrapCond) - EmitTrapCheck(TrapCond, CheckHandler, NoMerge); + EmitTrapCheck(TrapCond, CheckHandler, NoMerge, TR); if (!FatalCond && !RecoverableCond) return; @@ -4567,11 +4567,9 @@ void CodeGenFunction::EmitUnreachable(SourceLocation Loc) { void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked, SanitizerHandler CheckHandlerID, - bool NoMerge, - /*TO_UPSTREAM(BoundsSafety) ON*/ + bool NoMerge, const TrapReason *TR, StringRef Annotation, StringRef BoundsSafetyTrapMessage) { - /*TO_UPSTREAM(BoundsSafety) OFF*/ llvm::BasicBlock *Cont = createBasicBlock("cont"); // If we're optimizing, collapse all calls to trap down to just one per @@ -4582,22 +4580,34 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked, llvm::BasicBlock *&TrapBB = TrapBBs[CheckHandlerID]; llvm::DILocation *TrapLocation = Builder.getCurrentDebugLocation(); - - /*TO_UPSTREAM(BoundsSafety) ON*/ - if (CheckHandlerID == SanitizerHandler::BoundsSafety && getDebugInfo()) { - TrapLocation = getDebugInfo()->CreateTrapFailureMessageFor( - TrapLocation, GetBoundsSafetyTrapMessagePrefix(), BoundsSafetyTrapMessage); + llvm::StringRef TrapMessage; + llvm::StringRef TrapCategory; + auto DebugTrapReasonKind = CGM.getCodeGenOpts().getSanitizeDebugTrapReasons(); + if (TR && !TR->isEmpty() && + DebugTrapReasonKind == + CodeGenOptions::SanitizeDebugTrapReasonKind::Detailed) { + TrapMessage = TR->getMessage(); + TrapCategory = TR->getCategory(); + } else { + /* TO_UPSTREAM(BoundsSafety) ON*/ + // FIXME: Move to using `TrapReason` (rdar://158623471). + if (CheckHandlerID == SanitizerHandler::BoundsSafety) { + TrapMessage = BoundsSafetyTrapMessage; + TrapCategory = GetBoundsSafetyTrapMessagePrefix(); + } else { + TrapMessage = GetUBSanTrapForHandler(CheckHandlerID); + TrapCategory = "Undefined Behavior Sanitizer"; + } } - /*TO_UPSTREAM(BoundsSafety) OFF*/ - else { - llvm::StringRef TrapMessage = GetUBSanTrapForHandler(CheckHandlerID); - if (getDebugInfo() && !TrapMessage.empty() && - CGM.getCodeGenOpts().SanitizeDebugTrapReasons && TrapLocation) { - TrapLocation = getDebugInfo()->CreateTrapFailureMessageFor( - TrapLocation, "Undefined Behavior Sanitizer", TrapMessage); - } + if (getDebugInfo() && !(TrapMessage.empty() && TrapCategory.empty()) && + DebugTrapReasonKind != + CodeGenOptions::SanitizeDebugTrapReasonKind::None && + TrapLocation) { + TrapLocation = getDebugInfo()->CreateTrapFailureMessageFor( + TrapLocation, TrapCategory, TrapMessage); } + /* TO_UPSTREAM(BoundsSafety) OFF*/ NoMerge = NoMerge || !CGM.getCodeGenOpts().OptimizationLevel || (CurCodeDecl && CurCodeDecl->hasAttr()); @@ -4696,7 +4706,8 @@ void CodeGenFunction::EmitBoundsSafetyTrapCheck(llvm::Value *Checked, // We still need to pass `OptRemark` because not all emitted instructions // can be covered by BoundsSafetyOptRemarkScope. This is because EmitTrapCheck // caches basic blocks that contain instructions that need annotating. - EmitTrapCheck(Checked, SanitizerHandler::BoundsSafety, false, + EmitTrapCheck(Checked, SanitizerHandler::BoundsSafety, /*NoMerge=*/false, + /*TR=*/nullptr, GetBoundsSafetyOptRemarkString(OptRemark), GetBoundsSafetyTrapMessageSuffix(kind, TrapCtx)); } diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 23fa80ea8cbb2..bd5a07c1518d2 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "BoundsSafetyTraps.h" #include "CGCXXABI.h" #include "CGCleanup.h" #include "CGDebugInfo.h" @@ -20,8 +21,8 @@ #include "CodeGenFunction.h" #include "CodeGenModule.h" #include "ConstantEmitter.h" -#include "BoundsSafetyTraps.h" #include "TargetInfo.h" +#include "TrapReasonBuilder.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclObjC.h" @@ -30,6 +31,7 @@ #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtVisitor.h" #include "clang/Basic/CodeGenOptions.h" +#include "clang/Basic/DiagnosticTrap.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/APFixedPoint.h" #include "llvm/IR/Argument.h" @@ -1946,6 +1948,7 @@ void ScalarExprEmitter::EmitBinOpCheck( SanitizerHandler Check; SmallVector StaticData; SmallVector DynamicData; + TrapReason TR; BinaryOperatorKind Opcode = Info.Opcode; if (BinaryOperator::isCompoundAssignmentOp(Opcode)) @@ -1972,19 +1975,43 @@ void ScalarExprEmitter::EmitBinOpCheck( StaticData.push_back(CGF.EmitCheckTypeDescriptor(Info.Ty)); } else { // Arithmetic overflow (+, -, *). + int ArithOverflowKind = 0; switch (Opcode) { - case BO_Add: Check = SanitizerHandler::AddOverflow; break; - case BO_Sub: Check = SanitizerHandler::SubOverflow; break; - case BO_Mul: Check = SanitizerHandler::MulOverflow; break; - default: llvm_unreachable("unexpected opcode for bin op check"); + case BO_Add: { + Check = SanitizerHandler::AddOverflow; + ArithOverflowKind = diag::UBSanArithKind::Add; + break; + } + case BO_Sub: { + Check = SanitizerHandler::SubOverflow; + ArithOverflowKind = diag::UBSanArithKind::Sub; + break; + } + case BO_Mul: { + Check = SanitizerHandler::MulOverflow; + ArithOverflowKind = diag::UBSanArithKind::Mul; + break; + } + default: + llvm_unreachable("unexpected opcode for bin op check"); } StaticData.push_back(CGF.EmitCheckTypeDescriptor(Info.Ty)); + if (CGF.CGM.getCodeGenOpts().SanitizeTrap.has( + SanitizerKind::UnsignedIntegerOverflow) || + CGF.CGM.getCodeGenOpts().SanitizeTrap.has( + SanitizerKind::SignedIntegerOverflow)) { + // Only pay the cost for constructing the trap diagnostic if they are + // going to be used. + CGF.CGM.BuildTrapReason(diag::trap_ubsan_arith_overflow, TR) + << Info.Ty->isSignedIntegerOrEnumerationType() << ArithOverflowKind + << Info.E; + } } DynamicData.push_back(Info.LHS); DynamicData.push_back(Info.RHS); } - CGF.EmitCheck(Checks, Check, StaticData, DynamicData); + CGF.EmitCheck(Checks, Check, StaticData, DynamicData, &TR); } //===----------------------------------------------------------------------===// diff --git a/clang/lib/CodeGen/CMakeLists.txt b/clang/lib/CodeGen/CMakeLists.txt index 6394777793469..873cae903a331 100644 --- a/clang/lib/CodeGen/CMakeLists.txt +++ b/clang/lib/CodeGen/CMakeLists.txt @@ -157,6 +157,7 @@ add_clang_library(clangCodeGen Targets/WebAssembly.cpp Targets/X86.cpp Targets/XCore.cpp + TrapReasonBuilder.cpp VarBypassDetector.cpp DEPENDS diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 1343ed082ab3c..d0d1aeb3eb520 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -5396,7 +5396,8 @@ class CodeGenFunction : public CodeGenTypeCache { EmitCheck(ArrayRef> Checked, SanitizerHandler Check, ArrayRef StaticArgs, - ArrayRef DynamicArgs); + ArrayRef DynamicArgs, + const TrapReason *TR = nullptr); /// Emit a slow path cross-DSO CFI check which calls __cfi_slowpath /// if Cond if false. @@ -5412,7 +5413,7 @@ class CodeGenFunction : public CodeGenTypeCache { /// Create a basic block that will call the trap intrinsic, and emit a /// conditional branch to it, for the -ftrapv checks. void EmitTrapCheck(llvm::Value *Checked, SanitizerHandler CheckHandlerID, - bool NoMerge = false, + bool NoMerge = false, const TrapReason *TR = nullptr, StringRef Annotation = "", StringRef TrapMessage = ""); /* TO_UPSTREAM(BoundsSafety) ON*/ diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index d9d8a1d465ffd..12cda67b7b881 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -17,6 +17,7 @@ #include "CodeGenTypeCache.h" #include "CodeGenTypes.h" #include "SanitizerMetadata.h" +#include "TrapReasonBuilder.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclOpenMP.h" @@ -1841,6 +1842,11 @@ class CodeGenModule : public CodeGenTypeCache { return PAlign; } + /// Helper function to construct a TrapReasonBuilder + TrapReasonBuilder BuildTrapReason(unsigned DiagID, TrapReason &TR) { + return TrapReasonBuilder(&getDiags(), DiagID, TR); + } + private: bool shouldDropDLLAttribute(const Decl *D, const llvm::GlobalValue *GV) const; diff --git a/clang/lib/CodeGen/TrapReasonBuilder.cpp b/clang/lib/CodeGen/TrapReasonBuilder.cpp new file mode 100644 index 0000000000000..5881229bf747d --- /dev/null +++ b/clang/lib/CodeGen/TrapReasonBuilder.cpp @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements TrapReasonBuilder and related classes. +/// +//===----------------------------------------------------------------------===// +#include "TrapReasonBuilder.h" + +namespace clang { +namespace CodeGen { + +TrapReasonBuilder::TrapReasonBuilder(DiagnosticsEngine *DiagObj, + unsigned DiagID, TrapReason &TR) + : DiagnosticBuilder(DiagObj, SourceLocation(), DiagID), TR(TR) { + assert(DiagObj->getDiagnosticIDs()->isTrapDiag(DiagID)); +} + +TrapReasonBuilder::~TrapReasonBuilder() { + // Store the trap message and category into the TrapReason object. + getMessage(TR.Message); + TR.Category = getCategory(); + + // Make sure that when `DiagnosticBuilder::~DiagnosticBuilder()` + // calls `Emit()` that it does nothing. + Clear(); +} + +void TrapReasonBuilder::getMessage(SmallVectorImpl &Storage) { + // Render the Diagnostic + Diagnostic Info(getDiagnosticsEngine(), *this); + Info.FormatDiagnostic(Storage); +} + +StringRef TrapReasonBuilder::getCategory() { + auto CategoryID = + getDiagnosticsEngine()->getDiagnosticIDs()->getCategoryNumberForDiag( + getDiagID()); + if (CategoryID == 0) + return ""; + return getDiagnosticsEngine()->getDiagnosticIDs()->getCategoryNameFromID( + CategoryID); +} +} // namespace CodeGen +} // namespace clang diff --git a/clang/lib/CodeGen/TrapReasonBuilder.h b/clang/lib/CodeGen/TrapReasonBuilder.h new file mode 100644 index 0000000000000..b16cae482153a --- /dev/null +++ b/clang/lib/CodeGen/TrapReasonBuilder.h @@ -0,0 +1,112 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the declaration of TrapReasonBuilder and related classes. +/// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_CODEGEN_TRAP_REASON_BUILDER_H +#define LLVM_CLANG_CODEGEN_TRAP_REASON_BUILDER_H +#include "clang/Basic/Diagnostic.h" + +namespace clang { +namespace CodeGen { + +/// Helper class for \class TrapReasonBuilder. \class TrapReason stores the +/// "trap reason" built by \class TrapReasonBuilder. This consists of +/// a trap message and trap category. +/// +/// It is intended that this object be allocated on the stack. +class TrapReason { +public: + TrapReason() = default; + /// \return The trap message. Note the lifetime of the underlying storage for + /// the returned StringRef lives in this class which means the returned + /// StringRef should not be used after this class is destroyed. + StringRef getMessage() const { return Message; } + + /// \return the trap category (e.g. "Undefined Behavior Sanitizer") + StringRef getCategory() const { return Category; } + + bool isEmpty() const { + // Note both Message and Category are checked because it is legitimate for + // the Message to be empty but for the Category to be non-empty when the + // trap category is known but the specific reason is not available during + // codegen. + return Message.size() == 0 && Category.size() == 0; + } + +private: + llvm::SmallString<64> Message; + // The Category doesn't need its own storage because the StringRef points + // to a global constant string. + StringRef Category; + + // Only this class can set the private fields. + friend class TrapReasonBuilder; +}; + +/// Class to make it convenient to initialize TrapReason objects which can be +/// used to attach the "trap reason" to trap instructions. +/// +/// Although this class inherits from \class DiagnosticBuilder it has slightly +/// different semantics. +/// +/// * This class should only be used with trap diagnostics (declared in +/// `DiagnosticTrapKinds.td`). +/// * The `TrapReasonBuilder` does not emit diagnostics to the normal +/// diagnostics consumers on destruction like normal Diagnostic builders. +/// Instead on destruction it assigns to the TrapReason object passed into +/// the constructor. +/// +/// Given that this class inherits from `DiagnosticBuilder` it inherits all of +/// its abilities to format diagnostic messages and consume various types in +/// class (e.g. Type, Exprs, etc.). This makes it particularly suited to +/// printing types and expressions from the AST while codegen-ing runtime +/// checks. +/// +/// +/// Example use via the `CodeGenModule::BuildTrapReason` helper. +/// +/// \code +/// { +/// TrapReason TR; +/// CGM.BuildTrapReason(diag::trap_diagnostic, TR) << 0 << SomeExpr; +/// consume(&TR); +/// } +/// \endcode +/// +/// +class TrapReasonBuilder : public DiagnosticBuilder { +public: + TrapReasonBuilder(DiagnosticsEngine *DiagObj, unsigned DiagID, + TrapReason &TR); + ~TrapReasonBuilder(); + + // Prevent accidentally copying or assigning + TrapReasonBuilder &operator=(const TrapReasonBuilder &) = delete; + TrapReasonBuilder &operator=(const TrapReasonBuilder &&) = delete; + TrapReasonBuilder(const TrapReasonBuilder &) = delete; + TrapReasonBuilder(const TrapReasonBuilder &&) = delete; + +private: + /// \return Format the trap message into `Storage`. + void getMessage(SmallVectorImpl &Storage); + + /// \return Return the trap category. These are the `CategoryName` property + /// of `trap` diagnostics declared in `DiagnosticTrapKinds.td`. + StringRef getCategory(); + +private: + TrapReason &TR; +}; + +} // namespace CodeGen +} // namespace clang + +#endif diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp index 0fbbd49eb9275..bce6d80a5b0bc 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -1385,11 +1385,7 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args, CmdArgs.push_back(Args.MakeArgString("-fsanitize-annotate-debug-info=" + toString(AnnotateDebugInfo))); - if (const Arg *A = - Args.getLastArg(options::OPT_fsanitize_debug_trap_reasons, - options::OPT_fno_sanitize_debug_trap_reasons)) { - CmdArgs.push_back(Args.MakeArgString(A->getAsString(Args))); - } + Args.AddLastArg(CmdArgs, options::OPT_fsanitize_debug_trap_reasons_EQ); addSpecialCaseListOpt(Args, CmdArgs, "-fsanitize-ignorelist=", UserIgnorelistFiles); diff --git a/clang/test/CodeGen/ubsan-trap-reason-add-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-add-overflow.c index 225778d68833d..862d434d291bc 100644 --- a/clang/test/CodeGen/ubsan-trap-reason-add-overflow.c +++ b/clang/test/CodeGen/ubsan-trap-reason-add-overflow.c @@ -1,9 +1,32 @@ // RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ -// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow -emit-llvm %s -o - | FileCheck %s +// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow \ +// RUN: -fsanitize-trap=signed-integer-overflow,unsigned-integer-overflow \ +// RUN: -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,DETAILED %s -int add_overflow(int a, int b) { return a + b; } +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow \ +// RUN: -fsanitize-trap=signed-integer-overflow,unsigned-integer-overflow \ +// RUN: -fsanitize-debug-trap-reasons=basic \ +// RUN: -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,BASIC %s + +int sadd_overflow(int a, int b) { return a + b; } + +unsigned add_overflow(unsigned c, unsigned d) { return c + d; } + +// CHECK-LABEL: @sadd_overflow +// CHECK: call void @llvm.ubsantrap(i8 0) {{.*}}!dbg [[SLOC:![0-9]+]] // CHECK-LABEL: @add_overflow // CHECK: call void @llvm.ubsantrap(i8 0) {{.*}}!dbg [[LOC:![0-9]+]] -// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) -// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer addition overflowed" + +// DETAILED: [[SLOC]] = !DILocation(line: 0, scope: [[SMSG:![0-9]+]], {{.+}}) +// DETAILED: [[SMSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$signed integer addition overflow in 'a + b'" +// DETAILED: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// DETAILED: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$unsigned integer addition overflow in 'c + d'" + +// In "Basic" mode the trap reason is shared by both functions. +// BASIC: [[SLOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// BASIC: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer addition overflowed" +// BASIC: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) + + diff --git a/clang/test/CodeGen/ubsan-trap-reason-flag.c b/clang/test/CodeGen/ubsan-trap-reason-flag.c index 5cc16d154bf68..2968e6bd2ade4 100644 --- a/clang/test/CodeGen/ubsan-trap-reason-flag.c +++ b/clang/test/CodeGen/ubsan-trap-reason-flag.c @@ -2,20 +2,45 @@ // RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow -emit-llvm %s -o - \ // RUN: | FileCheck %s --check-prefix=ANNOTATE +//============================================================================== +// Detailed trap reasons +//============================================================================== + +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow \ +// RUN: -fsanitize-debug-trap-reasons -emit-llvm %s -o - | FileCheck %s --check-prefixes=ANNOTATE,DETAILED + +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow \ +// RUN: -fsanitize-debug-trap-reasons=detailed -emit-llvm %s -o - | FileCheck %s --check-prefixes=ANNOTATE,DETAILED + +//============================================================================== +// Basic trap reasons +//============================================================================== + // RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ // RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow \ -// RUN: -fsanitize-debug-trap-reasons -emit-llvm %s -o - | FileCheck %s --check-prefix=ANNOTATE +// RUN: -fsanitize-debug-trap-reasons=basic -emit-llvm %s -o - | FileCheck %s --check-prefixes=ANNOTATE,BASIC + +//============================================================================== +// No trap reasons +//============================================================================== // RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ // RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow \ // RUN: -fno-sanitize-debug-trap-reasons -emit-llvm %s -o - | FileCheck %s --check-prefix=NO-ANNOTATE +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow \ +// RUN: -fsanitize-debug-trap-reasons=none -emit-llvm %s -o - | FileCheck %s --check-prefix=NO-ANNOTATE + int add_overflow(int a, int b) { return a + b; } // ANNOTATE-LABEL: @add_overflow // ANNOTATE: call void @llvm.ubsantrap(i8 0) {{.*}}!dbg [[LOC:![0-9]+]] // ANNOTATE: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) -// ANNOTATE: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer addition overflowed" +// DETAILED: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$signed integer addition overflow in 'a + b'" +// BASIC: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer addition overflowed" // NO-ANNOTATE-LABEL: @add_overflow // NO-ANNOTATE: call void @llvm.ubsantrap(i8 0) {{.*}}!dbg [[LOC:![0-9]+]] diff --git a/clang/test/CodeGen/ubsan-trap-reason-mul-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-mul-overflow.c index cf9a0b4e7439c..ba3928d0c2e63 100644 --- a/clang/test/CodeGen/ubsan-trap-reason-mul-overflow.c +++ b/clang/test/CodeGen/ubsan-trap-reason-mul-overflow.c @@ -1,9 +1,30 @@ // RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ -// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow -emit-llvm %s -o - | FileCheck %s +// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow \ +// RUN: -fsanitize-trap=signed-integer-overflow,unsigned-integer-overflow \ +// RUN: -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,DETAILED %s -int mul_overflow(int a, int b) { return a * b; } +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow \ +// RUN: -fsanitize-trap=signed-integer-overflow,unsigned-integer-overflow \ +// RUN: -fsanitize-debug-trap-reasons=basic \ +// RUN: -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,BASIC %s + +int smul_overflow(int a, int b) { return a * b; } + +unsigned mul_overflow(unsigned c, unsigned d) { return c * d; } + +// CHECK-LABEL: @smul_overflow +// CHECK: call void @llvm.ubsantrap(i8 12) {{.*}}!dbg [[SLOC:![0-9]+]] // CHECK-LABEL: @mul_overflow // CHECK: call void @llvm.ubsantrap(i8 12) {{.*}}!dbg [[LOC:![0-9]+]] -// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) -// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer multiplication overflowed" + +// DETAILED: [[SLOC]] = !DILocation(line: 0, scope: [[SMSG:![0-9]+]], {{.+}}) +// DETAILED: [[SMSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$signed integer multiplication overflow in 'a * b'" +// DETAILED: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// DETAILED: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$unsigned integer multiplication overflow in 'c * d'" + +// In "Basic" mode the trap reason is shared by both functions. +// BASIC: [[SLOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// BASIC: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer multiplication overflowed" +// BASIC: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) diff --git a/clang/test/CodeGen/ubsan-trap-reason-sub-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-sub-overflow.c index 62aa7fc953dad..596d777fa4360 100644 --- a/clang/test/CodeGen/ubsan-trap-reason-sub-overflow.c +++ b/clang/test/CodeGen/ubsan-trap-reason-sub-overflow.c @@ -1,9 +1,30 @@ // RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ -// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow -emit-llvm %s -o - | FileCheck %s +// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow \ +// RUN: -fsanitize-trap=signed-integer-overflow,unsigned-integer-overflow \ +// RUN: -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,DETAILED %s -int sub_overflow(int a, int b) { return a - b; } +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=signed-integer-overflow,unsigned-integer-overflow \ +// RUN: -fsanitize-trap=signed-integer-overflow,unsigned-integer-overflow \ +// RUN: -fsanitize-debug-trap-reasons=basic \ +// RUN: -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,BASIC %s + +int ssub_overflow(int a, int b) { return a - b; } + +unsigned sub_overflow(unsigned c, unsigned d) { return c - d; } + +// CHECK-LABEL: @ssub_overflow +// CHECK: call void @llvm.ubsantrap(i8 21) {{.*}}!dbg [[SLOC:![0-9]+]] // CHECK-LABEL: @sub_overflow // CHECK: call void @llvm.ubsantrap(i8 21) {{.*}}!dbg [[LOC:![0-9]+]] -// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) -// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer subtraction overflowed" + +// DETAILED: [[SLOC]] = !DILocation(line: 0, scope: [[SMSG:![0-9]+]], {{.+}}) +// DETAILED: [[SMSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$signed integer subtraction overflow in 'a - b'" +// DETAILED: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// DETAILED: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$unsigned integer subtraction overflow in 'c - d'" + +// In "Basic" mode the trap reason is shared by both functions. +// BASIC: [[SLOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// BASIC: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer subtraction overflowed" +// BASIC: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) diff --git a/clang/test/Driver/fsanitize-debug-trap-reasons.c b/clang/test/Driver/fsanitize-debug-trap-reasons.c new file mode 100644 index 0000000000000..5a0ccde015939 --- /dev/null +++ b/clang/test/Driver/fsanitize-debug-trap-reasons.c @@ -0,0 +1,57 @@ +// ============================================================================= +// No Trap Reasons +// ============================================================================= + +// RUN: %clang -fsanitize=undefined -fsanitize-trap=undefined \ +// RUN: -fsanitize-debug-trap-reasons=none %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=NONE %s + +// RUN: %clang -fsanitize=undefined -fsanitize-trap=undefined \ +// RUN: -fno-sanitize-debug-trap-reasons %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=NONE %s + +// NONE: -fsanitize-debug-trap-reasons=none + +// ============================================================================= +// Basic Trap Reasons +// ============================================================================= + +// RUN: %clang -fsanitize=undefined -fsanitize-trap=undefined \ +// RUN: -fsanitize-debug-trap-reasons=basic %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=BASIC %s +// BASIC: -fsanitize-debug-trap-reasons=basic + +// ============================================================================= +// Detailed Trap Reasons +// ============================================================================= + +// RUN: %clang -fsanitize=undefined -fsanitize-trap=undefined \ +// RUN: -fsanitize-debug-trap-reasons=detailed %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=DETAILED %s + +// RUN: %clang -fsanitize=undefined -fsanitize-trap=undefined \ +// RUN: -fsanitize-debug-trap-reasons %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=DETAILED %s + +// DETAILED: -fsanitize-debug-trap-reasons=detailed + +// ============================================================================= +// Other cases +// ============================================================================= + +// By default the driver doesn't pass along any value and the default value is +// whatever is the default in CodeGenOptions. +// RUN: %clang %s -### 2>&1 | FileCheck --check-prefix=DEFAULT %s +// DEFAULT-NOT: -fsanitize-debug-trap-reasons + +// Warning when not using UBSan +// RUN: %clang -fsanitize-debug-trap-reasons=none %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=WARN %s +// WARN: warning: argument unused during compilation: '-fsanitize-debug-trap-reasons=none' + +// Bad flag arguments are just passed along to the Frontend which handles rejecting +// invalid values. See `clang/test/Frontend/fsanitize-debug-trap-reasons.c` +// RUN: %clang -fsanitize=undefined -fsanitize-trap=undefined \ +// RUN: -fsanitize-debug-trap-reasons=bad_value %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=BAD_VALUE %s +// BAD_VALUE: -fsanitize-debug-trap-reasons=bad_value diff --git a/clang/test/Frontend/fsanitize-debug-trap-reasons.c b/clang/test/Frontend/fsanitize-debug-trap-reasons.c new file mode 100644 index 0000000000000..82b33eaf1cb27 --- /dev/null +++ b/clang/test/Frontend/fsanitize-debug-trap-reasons.c @@ -0,0 +1,6 @@ +// RUN: not %clang_cc1 -triple arm64-apple-macosx14.0.0 \ +// RUN: -fsanitize=signed-integer-overflow -fsanitize=signed-integer-overflow \ +// RUN: -fsanitize-debug-trap-reasons=bad_value 2>&1 | FileCheck %s + +// CHECK: error: invalid value 'bad_value' in '-fsanitize-debug-trap-reasons=bad_value' +int test(void) { return 0;} diff --git a/clang/tools/diagtool/ListWarnings.cpp b/clang/tools/diagtool/ListWarnings.cpp index 9f9647126dd8a..ce24f11bd1411 100644 --- a/clang/tools/diagtool/ListWarnings.cpp +++ b/clang/tools/diagtool/ListWarnings.cpp @@ -56,6 +56,9 @@ int ListWarnings::run(unsigned int argc, char **argv, llvm::raw_ostream &out) { if (DiagnosticIDs{}.isNote(diagID)) continue; + if (DiagnosticIDs{}.isTrapDiag(diagID)) + continue; + if (!DiagnosticIDs{}.isWarningOrExtension(diagID)) continue; diff --git a/lldb/test/Shell/Recognizer/ubsan_add_overflow.test b/lldb/test/Shell/Recognizer/ubsan_add_overflow.test index a5e95cf5a898f..872b5a7a4d585 100644 --- a/lldb/test/Shell/Recognizer/ubsan_add_overflow.test +++ b/lldb/test/Shell/Recognizer/ubsan_add_overflow.test @@ -6,11 +6,11 @@ # RUN: %lldb -b -s %s %t.out | FileCheck %s run -# CHECK: thread #{{.*}} stop reason = Undefined Behavior Sanitizer: Integer addition overflowed +# CHECK: thread #{{.*}} stop reason = Undefined Behavior Sanitizer: signed integer addition overflow in '2147483647 + 1' # CHECK-NEXT: frame #1: {{.*}}`main at ubsan_add_overflow.c bt -# CHECK: frame #0: {{.*}}`__clang_trap_msg$Undefined Behavior Sanitizer$Integer addition overflowed{{.*}} +# CHECK: frame #0: {{.*}}`__clang_trap_msg$Undefined Behavior Sanitizer$signed integer addition overflow in '2147483647 + 1'{{.*}} # CHECK: frame #1: {{.*}}`main at ubsan_add_overflow.c frame info From 504f60d2885514af880b342fcc6a72e75ae7e79b Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Wed, 27 Aug 2025 21:49:23 -0700 Subject: [PATCH 6/6] [cas] Fix CAS diagnostic ID mapping Accidentally broken by recent merge resolution. (cherry picked from commit 9cf7934f2b64e47e53a454d92d565059695fdd0d) --- clang/lib/Basic/DiagnosticIDs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Basic/DiagnosticIDs.cpp b/clang/lib/Basic/DiagnosticIDs.cpp index a929f998d050b..bb66e402e047e 100644 --- a/clang/lib/Basic/DiagnosticIDs.cpp +++ b/clang/lib/Basic/DiagnosticIDs.cpp @@ -220,7 +220,7 @@ CATEGORY(ANALYSIS, SEMA) CATEGORY(REFACTORING, ANALYSIS) CATEGORY(INSTALLAPI, REFACTORING) CATEGORY(TRAP, INSTALLAPI) -CATEGORY(CAS, INSTALLAPI) +CATEGORY(CAS, TRAP) #undef CATEGORY // Avoid out of bounds reads.