Author: Dan Liew Date: 2025-08-27T13:07:15-07:00 New Revision: f1ee0473209e31b0d05f589e9091ffbbcc60be31
URL: https://github.com/llvm/llvm-project/commit/f1ee0473209e31b0d05f589e9091ffbbcc60be31 DIFF: https://github.com/llvm/llvm-project/commit/f1ee0473209e31b0d05f589e9091ffbbcc60be31.diff LOG: [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 Added: clang/include/clang/Basic/DiagnosticTrap.h clang/include/clang/Basic/DiagnosticTrapKinds.td clang/lib/CodeGen/TrapReasonBuilder.cpp clang/lib/CodeGen/TrapReasonBuilder.h clang/test/Driver/fsanitize-debug-trap-reasons.c clang/test/Frontend/fsanitize-debug-trap-reasons.c Modified: clang/docs/ReleaseNotes.rst clang/include/clang/Basic/AllDiagnosticKinds.inc clang/include/clang/Basic/AllDiagnostics.h clang/include/clang/Basic/CMakeLists.txt clang/include/clang/Basic/CodeGenOptions.def clang/include/clang/Basic/CodeGenOptions.h clang/include/clang/Basic/Diagnostic.h clang/include/clang/Basic/Diagnostic.td clang/include/clang/Basic/DiagnosticIDs.h clang/include/clang/Driver/Options.td clang/lib/Basic/Diagnostic.cpp clang/lib/Basic/DiagnosticIDs.cpp clang/lib/CodeGen/CGExpr.cpp clang/lib/CodeGen/CGExprScalar.cpp clang/lib/CodeGen/CMakeLists.txt clang/lib/CodeGen/CodeGenFunction.h clang/lib/CodeGen/CodeGenModule.h clang/lib/Driver/SanitizerArgs.cpp clang/test/DebugInfo/Generic/ubsan-trap-reason-add-overflow.c clang/test/DebugInfo/Generic/ubsan-trap-reason-flag.c clang/test/DebugInfo/Generic/ubsan-trap-reason-mul-overflow.c clang/test/DebugInfo/Generic/ubsan-trap-reason-sub-overflow.c clang/tools/diagtool/ListWarnings.cpp lldb/test/Shell/Recognizer/ubsan_add_overflow.test Removed: ################################################################################ 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 + diff erent 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 <size_t SizeOfStr, typename FieldType> 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<string str> : Diagnostic<str, CLASS_EXTENSION, SEV_Ignored>; class ExtWarn<string str> : Diagnostic<str, CLASS_EXTENSION, SEV_Warning>; // Notes can provide supplementary information on errors, warnings, and remarks. class Note<string str> : Diagnostic<str, CLASS_NOTE, SEV_Fatal/*ignored*/>; - +// Trap messages attached to traps in debug info. +class Trap<string str> : Diagnostic<str, CLASS_TRAP, SEV_Fatal/*ignored*/>; 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<int>(DIAG_SIZE_SEMA), DIAG_START_REFACTORING = DIAG_START_ANALYSIS + static_cast<int>(DIAG_SIZE_ANALYSIS), DIAG_START_INSTALLAPI = DIAG_START_REFACTORING + static_cast<int>(DIAG_SIZE_REFACTORING), - DIAG_UPPER_LIMIT = DIAG_START_INSTALLAPI + static_cast<int>(DIAG_SIZE_INSTALLAPI) + DIAG_START_TRAP = DIAG_START_INSTALLAPI + static_cast<int>(DIAG_SIZE_INSTALLAPI), + DIAG_UPPER_LIMIT = DIAG_START_TRAP + static_cast<int>(DIAG_SIZE_TRAP) }; // clang-format on @@ -189,7 +191,8 @@ class DiagnosticIDs : public RefCountedBase<DiagnosticIDs> { 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<DiagnosticIDs> { /// 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<UBSanArithKind>{" + "%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<f_clang_Group>, Alias<fno_sanitize_trap_EQ>, AliasArgs<["undefined"]>; -defm sanitize_debug_trap_reasons - : BoolFOption< - "sanitize-debug-trap-reasons", - CodeGenOpts<"SanitizeDebugTrapReasons">, DefaultTrue, - PosFlag<SetTrue, [], [ClangOption, CC1Option], - "Annotate trap blocks in debug info with UBSan trap reasons">, - NegFlag<SetFalse, [], [ClangOption, CC1Option], - "Do not annotate trap blocks in debug info with UBSan trap " - "reasons">>; - +def fsanitize_debug_trap_reasons_EQ + : Joined<["-"], "fsanitize-debug-trap-reasons=">, Group<f_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<CodeGenOpts<"SanitizeDebugTrapReasons">, "Detailed">; +def fsanitize_debug_trap_reasons + : Flag<["-"], "fsanitize-debug-trap-reasons">, Group<f_clang_Group>, + Alias<fsanitize_debug_trap_reasons_EQ>, AliasArgs<["detailed"]>, + HelpText<"Alias for -fsanitize-debug-trap-reasons=detailed">; +def fno_sanitize_debug_trap_reasons + : Flag<["-"], "fno-sanitize-debug-trap-reasons">, Group<f_clang_Group>, + Alias<fsanitize_debug_trap_reasons_EQ>, AliasArgs<["none"]>, + HelpText<"Alias for -fsanitize-debug-trap-reasons=none">; defm sanitize_minimal_runtime : BoolOption<"f", "sanitize-minimal-runtime", CodeGenOpts<"SanitizeMinimalRuntime">, DefaultFalse, PosFlag<SetTrue>, 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<std::pair<llvm::Value *, SanitizerKind::SanitizerOrdinal>> Checked, SanitizerHandler CheckHandler, ArrayRef<llvm::Constant *> StaticArgs, - ArrayRef<llvm::Value *> DynamicArgs) { + ArrayRef<llvm::Value *> 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<llvm::Constant *, 4> StaticData; SmallVector<llvm::Value *, 2> 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<std::pair<llvm::Value *, SanitizerKind::SanitizerOrdinal>> Checked, SanitizerHandler Check, ArrayRef<llvm::Constant *> StaticArgs, - ArrayRef<llvm::Value *> DynamicArgs); + ArrayRef<llvm::Value *> 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<char> &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 +/// diff erent 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<char> &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 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits