diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 3ad9e566a5d93..84e499e5d0ab9 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -141,14 +141,27 @@ Non-comprehensive list of changes in this release - Added ``__builtin_elementwise_minnumnum`` and ``__builtin_elementwise_maxnumnum``. -- 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. - ``__builtin_elementwise_max`` and ``__builtin_elementwise_min`` functions for integer types can now be used in constant expressions. @@ -185,7 +198,9 @@ Non-comprehensive list of changes in this release 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``). + Lanai Support ^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/AllDiagnosticKinds.inc b/clang/include/clang/Basic/AllDiagnosticKinds.inc index a946b4a640ac6..2d08bb0525970 100644 --- a/clang/include/clang/Basic/AllDiagnosticKinds.inc +++ b/clang/include/clang/Basic/AllDiagnosticKinds.inc @@ -30,4 +30,5 @@ #include "clang/Basic/DiagnosticAnalysisKinds.inc" #include "clang/Basic/DiagnosticRefactoringKinds.inc" #include "clang/Basic/DiagnosticInstallAPIKinds.inc" +#include "clang/Basic/DiagnosticTrapKinds.inc" // clang-format on diff --git a/clang/include/clang/Basic/AllDiagnostics.h b/clang/include/clang/Basic/AllDiagnostics.h index 3b782732c1507..78e5428ddbfff 100644 --- a/clang/include/clang/Basic/AllDiagnostics.h +++ b/clang/include/clang/Basic/AllDiagnostics.h @@ -26,6 +26,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 0cf661a57dfa8..81736006a21a0 100644 --- a/clang/include/clang/Basic/CMakeLists.txt +++ b/clang/include/clang/Basic/CMakeLists.txt @@ -33,6 +33,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 b96ec75068004..fda0da99b60c0 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -307,7 +307,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 cdeedd5b4eac6..5d5cf250b56b9 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -198,6 +198,16 @@ class CodeGenOptions : public CodeGenOptionsBase { Forced, }; + 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 cee5bed665d0a..af26a04d94889 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" @@ -1259,10 +1260,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 65b19f3feea4f..53b1db265ccd0 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; } @@ -235,3 +237,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 17fecd346f03e..06446cf580389 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, }; // Start position for diagnostics. // clang-format off @@ -64,7 +65,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_UPPER_LIMIT = DIAG_START_INSTALLAPI + static_cast(DIAG_SIZE_INSTALLAPI) + DIAG_START_TRAP = DIAG_START_INSTALLAPI + static_cast(DIAG_SIZE_INSTALLAPI), + DIAG_UPPER_LIMIT = DIAG_START_TRAP + static_cast(DIAG_SIZE_TRAP) }; // clang-format on @@ -189,7 +191,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) { @@ -363,6 +366,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 82e8212bee12d..b1ae3cf6525b8 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2612,16 +2612,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 e33e843db6a44..dc3778bbf339c 100644 --- a/clang/lib/Basic/Diagnostic.cpp +++ b/clang/lib/Basic/Diagnostic.cpp @@ -664,6 +664,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 73f24a82d4c75..a1d9d0f34d20d 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) #undef VALIDATE_DIAG_SIZE #undef STRINGIFY_NAME @@ -171,6 +173,7 @@ const StaticDiagInfoRec StaticDiagInfo[] = { #include "clang/Basic/DiagnosticAnalysisKinds.inc" #include "clang/Basic/DiagnosticRefactoringKinds.inc" #include "clang/Basic/DiagnosticInstallAPIKinds.inc" +#include "clang/Basic/DiagnosticTrapKinds.inc" // clang-format on #undef DIAG }; @@ -214,6 +217,7 @@ CATEGORY(SEMA, CROSSTU) CATEGORY(ANALYSIS, SEMA) CATEGORY(REFACTORING, ANALYSIS) CATEGORY(INSTALLAPI, REFACTORING) +CATEGORY(TRAP, INSTALLAPI) #undef CATEGORY // Avoid out of bounds reads. diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index eeaf68dfd0521..844b445b98c1d 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -3782,7 +3782,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 && @@ -3821,7 +3821,7 @@ void CodeGenFunction::EmitCheck( } if (TrapCond) - EmitTrapCheck(TrapCond, CheckHandler, NoMerge); + EmitTrapCheck(TrapCond, CheckHandler, NoMerge, TR); if (!FatalCond && !RecoverableCond) return; @@ -4133,7 +4133,7 @@ void CodeGenFunction::EmitUnreachable(SourceLocation Loc) { void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked, SanitizerHandler CheckHandlerID, - bool NoMerge) { + bool NoMerge, const TrapReason *TR) { llvm::BasicBlock *Cont = createBasicBlock("cont"); // If we're optimizing, collapse all calls to trap down to just one per @@ -4144,12 +4144,25 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked, llvm::BasicBlock *&TrapBB = TrapBBs[CheckHandlerID]; llvm::DILocation *TrapLocation = Builder.getCurrentDebugLocation(); - llvm::StringRef TrapMessage = GetUBSanTrapForHandler(CheckHandlerID); + 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 { + TrapMessage = GetUBSanTrapForHandler(CheckHandlerID); + TrapCategory = "Undefined Behavior Sanitizer"; + } if (getDebugInfo() && !TrapMessage.empty() && - CGM.getCodeGenOpts().SanitizeDebugTrapReasons && TrapLocation) { + DebugTrapReasonKind != + CodeGenOptions::SanitizeDebugTrapReasonKind::None && + TrapLocation) { TrapLocation = getDebugInfo()->CreateTrapFailureMessageFor( - TrapLocation, "Undefined Behavior Sanitizer", TrapMessage); + TrapLocation, TrapCategory, TrapMessage); } NoMerge = NoMerge || !CGM.getCodeGenOpts().OptimizationLevel || diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 2338cbd74f002..2eff3a387593c 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -21,6 +21,7 @@ #include "CodeGenModule.h" #include "ConstantEmitter.h" #include "TargetInfo.h" +#include "TrapReasonBuilder.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclObjC.h" @@ -29,6 +30,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" @@ -1813,6 +1815,7 @@ void ScalarExprEmitter::EmitBinOpCheck( SanitizerHandler Check; SmallVector StaticData; SmallVector DynamicData; + TrapReason TR; BinaryOperatorKind Opcode = Info.Opcode; if (BinaryOperator::isCompoundAssignmentOp(Opcode)) @@ -1839,19 +1842,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 0f2a352886e7f..ad9ef91c781a8 100644 --- a/clang/lib/CodeGen/CMakeLists.txt +++ b/clang/lib/CodeGen/CMakeLists.txt @@ -154,6 +154,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 a562a6a1ea6e1..c02ac18ec0198 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -5278,7 +5278,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. @@ -5294,7 +5295,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); /// Emit a call to trap or debugtrap and attach function attribute /// "trap-func-name" if specified. diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 705d9a3cb9de3..b4b3a17662045 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" @@ -1824,6 +1825,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 54f0e63b98070..7ce1afe6f2e6a 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -1384,11 +1384,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/DebugInfo/Generic/ubsan-trap-reason-add-overflow.c b/clang/test/DebugInfo/Generic/ubsan-trap-reason-add-overflow.c index 225778d68833d..862d434d291bc 100644 --- a/clang/test/DebugInfo/Generic/ubsan-trap-reason-add-overflow.c +++ b/clang/test/DebugInfo/Generic/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/DebugInfo/Generic/ubsan-trap-reason-flag.c b/clang/test/DebugInfo/Generic/ubsan-trap-reason-flag.c index 5cc16d154bf68..2968e6bd2ade4 100644 --- a/clang/test/DebugInfo/Generic/ubsan-trap-reason-flag.c +++ b/clang/test/DebugInfo/Generic/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/DebugInfo/Generic/ubsan-trap-reason-mul-overflow.c b/clang/test/DebugInfo/Generic/ubsan-trap-reason-mul-overflow.c index cf9a0b4e7439c..ba3928d0c2e63 100644 --- a/clang/test/DebugInfo/Generic/ubsan-trap-reason-mul-overflow.c +++ b/clang/test/DebugInfo/Generic/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/DebugInfo/Generic/ubsan-trap-reason-sub-overflow.c b/clang/test/DebugInfo/Generic/ubsan-trap-reason-sub-overflow.c index 62aa7fc953dad..596d777fa4360 100644 --- a/clang/test/DebugInfo/Generic/ubsan-trap-reason-sub-overflow.c +++ b/clang/test/DebugInfo/Generic/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