diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 9400be296e7c2..b3609073eaa0f 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -366,9 +366,33 @@ Non-comprehensive list of changes in this release correct method to check for these features is to test for the ``__PTRAUTH__`` macro. +- Trapping UBSan (e.g. ``-fsanitize=undefined -fsanitize-trap=undefined``) now + emits a string describing the reason for trapping into the generated debug + info. This feature allows debuggers (e.g. LLDB) to display the reason for + trapping if the trap is reached. The string is currently encoded in the debug + info as an artificial frame that claims to be inlined at the trap location. + The function used for the artificial frame is an artificial function whose + name encodes the reason for trapping. The encoding used is currently the same + as ``__builtin_verbose_trap`` but might change in the future. This feature is + enabled by default but can be disabled by compiling with + ``-fno-sanitize-debug-trap-reasons``. The feature has a ``basic`` and + ``detailed`` mode (the default). The ``basic`` mode emits a hard-coded string + per trap kind (e.g. ``Integer addition overflowed``) and the ``detailed`` mode + emits a more descriptive string describing each individual trap (e.g. ``signed + integer addition overflow in 'a + b'``). The ``detailed`` mode produces larger + debug info than ``basic`` but is more helpful for debugging. The + ``-fsanitize-debug-trap-reasons=`` flag can be used to switch between the + different modes or disable the feature entirely. Note due to trap merging in + optimized builds (i.e. in each function all traps of the same kind get merged + into the same trap instruction) the trap reasons might be removed. To prevent + this build without optimizations (i.e. use `-O0` or use the `optnone` function + attribute) or use the `fno-sanitize-merge=` flag in optimized builds. New Compiler Flags ------------------ +- New option ``-fno-sanitize-debug-trap-reasons`` added to disable emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``). +- New option ``-fsanitize-debug-trap-reasons=`` added to control emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``). + - New option ``-Wundef-true`` added and enabled by default to warn when `true` is used in the C preprocessor without being defined before C23. diff --git a/clang/include/clang/Basic/AllDiagnosticKinds.inc b/clang/include/clang/Basic/AllDiagnosticKinds.inc index 83498a857eae4..a8c4f911c151b 100644 --- a/clang/include/clang/Basic/AllDiagnosticKinds.inc +++ b/clang/include/clang/Basic/AllDiagnosticKinds.inc @@ -30,5 +30,7 @@ #include "clang/Basic/DiagnosticAnalysisKinds.inc" #include "clang/Basic/DiagnosticRefactoringKinds.inc" #include "clang/Basic/DiagnosticInstallAPIKinds.inc" +#include "clang/Basic/DiagnosticTrapKinds.inc" #include "clang/Basic/DiagnosticCASKinds.inc" + // clang-format on diff --git a/clang/include/clang/Basic/AllDiagnostics.h b/clang/include/clang/Basic/AllDiagnostics.h index b71748483b065..9d56344ec5cb0 100644 --- a/clang/include/clang/Basic/AllDiagnostics.h +++ b/clang/include/clang/Basic/AllDiagnostics.h @@ -24,20 +24,21 @@ #include "clang/Basic/DiagnosticInstallAPI.h" #include "clang/Basic/DiagnosticLex.h" #include "clang/Basic/DiagnosticParse.h" +#include "clang/Basic/DiagnosticRefactoring.h" #include "clang/Basic/DiagnosticSema.h" #include "clang/Basic/DiagnosticSerialization.h" -#include "clang/Basic/DiagnosticRefactoring.h" +#include "clang/Basic/DiagnosticTrap.h" namespace clang { -template -class StringSizerHelper { +template class StringSizerHelper { static_assert(SizeOfStr <= FieldType(~0U), "Field too small!"); + public: enum { Size = SizeOfStr }; }; } // end namespace clang -#define STR_SIZE(str, fieldTy) clang::StringSizerHelper::Size +#define STR_SIZE(str, fieldTy) \ + clang::StringSizerHelper::Size #endif diff --git a/clang/include/clang/Basic/CMakeLists.txt b/clang/include/clang/Basic/CMakeLists.txt index 91e1c4bc27459..8cd5de10996f4 100644 --- a/clang/include/clang/Basic/CMakeLists.txt +++ b/clang/include/clang/Basic/CMakeLists.txt @@ -34,6 +34,7 @@ clang_diag_gen(Parse) clang_diag_gen(Refactoring) clang_diag_gen(Sema) clang_diag_gen(Serialization) +clang_diag_gen(Trap) clang_tablegen(DiagnosticGroups.inc -gen-clang-diag-groups SOURCE Diagnostic.td TARGET ClangDiagnosticGroups) diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index 2c24223692ffb..853b60b26e041 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -306,6 +306,7 @@ CODEGENOPT(SanitizeBinaryMetadataAtomics, 1, 0, Benign) ///< Emit PCs for atomic CODEGENOPT(SanitizeBinaryMetadataUAR, 1, 0, Benign) ///< Emit PCs for start of functions ///< that are subject for use-after-return checking. CODEGENOPT(SanitizeStats , 1, 0, Benign) ///< Collect statistics for sanitizers. +ENUM_CODEGENOPT(SanitizeDebugTrapReasons, SanitizeDebugTrapReasonKind, 2, SanitizeDebugTrapReasonKind::Detailed, Benign) ///< Control how "trap reasons" are emitted in debug info CODEGENOPT(SimplifyLibCalls , 1, 1, Benign) ///< Set when -fbuiltin is enabled. CODEGENOPT(SoftFloat , 1, 0, Benign) ///< -soft-float. CODEGENOPT(SpeculativeLoadHardening, 1, 0, Benign) ///< Enable speculative load hardening. diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index 30583c88d0543..1dd8bd30073cf 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -178,6 +178,16 @@ class CodeGenOptions : public CodeGenOptionsBase { /// The callback for mc result. std::optional MCCallBack; + enum SanitizeDebugTrapReasonKind { + None, ///< Trap Messages are omitted. This offers the smallest debug info + ///< size but at the cost of making traps hard to debug. + Basic, ///< Trap Message is fixed per SanitizerKind. Produces smaller debug + ///< info than `Detailed` but is not as helpful for debugging. + Detailed, ///< Trap Message includes more context (e.g. the expression being + ///< overflowed). This is more helpful for debugging but produces + ///< larger debug info than `Basic`. + }; + /// The code model to use (-mcmodel). std::string CodeModel; diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h index 25e0c44d78d85..33de6ca1ce838 100644 --- a/clang/include/clang/Basic/Diagnostic.h +++ b/clang/include/clang/Basic/Diagnostic.h @@ -23,6 +23,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FunctionExtras.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Support/Compiler.h" @@ -1269,10 +1270,13 @@ class DiagnosticBuilder : public StreamingDiagnostic { DiagnosticBuilder() = default; +protected: DiagnosticBuilder(DiagnosticsEngine *DiagObj, SourceLocation DiagLoc, unsigned DiagID); -protected: + DiagnosticsEngine *getDiagnosticsEngine() const { return DiagObj; } + unsigned getDiagID() const { return DiagID; } + /// Clear out the current diagnostic. void Clear() const { DiagObj = nullptr; diff --git a/clang/include/clang/Basic/Diagnostic.td b/clang/include/clang/Basic/Diagnostic.td index 8f8862773e112..d3d76df004551 100644 --- a/clang/include/clang/Basic/Diagnostic.td +++ b/clang/include/clang/Basic/Diagnostic.td @@ -30,6 +30,7 @@ def CLASS_REMARK : DiagClass; def CLASS_WARNING : DiagClass; def CLASS_EXTENSION : DiagClass; def CLASS_ERROR : DiagClass; +def CLASS_TRAP : DiagClass; // Responses to a diagnostic in a SFINAE context. class SFINAEResponse; @@ -144,7 +145,8 @@ class Extension : Diagnostic; class ExtWarn : Diagnostic; // Notes can provide supplementary information on errors, warnings, and remarks. class Note : Diagnostic; - +// Trap messages attached to traps in debug info. +class Trap : Diagnostic; class DefaultIgnore { Severity DefaultSeverity = SEV_Ignored; } class DefaultWarn { Severity DefaultSeverity = SEV_Warning; } @@ -236,3 +238,4 @@ include "DiagnosticParseKinds.td" include "DiagnosticRefactoringKinds.td" include "DiagnosticSemaKinds.td" include "DiagnosticSerializationKinds.td" +include "DiagnosticTrapKinds.td" diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h index 69a90f761a21c..a343a233cd9b8 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -23,78 +23,82 @@ #include namespace clang { - class DiagnosticsEngine; - class DiagnosticBuilder; - class LangOptions; - class SourceLocation; - - // Import the diagnostic enums themselves. - namespace diag { - enum class Group; - - // Size of each of the diagnostic categories. - enum { - DIAG_SIZE_COMMON = 300, - DIAG_SIZE_DRIVER = 400, - DIAG_SIZE_FRONTEND = 200, - DIAG_SIZE_SERIALIZATION = 120, - DIAG_SIZE_LEX = 500, - DIAG_SIZE_PARSE = 800, - DIAG_SIZE_AST = 300, - DIAG_SIZE_COMMENT = 100, - DIAG_SIZE_CROSSTU = 100, - DIAG_SIZE_SEMA = 5000, - DIAG_SIZE_ANALYSIS = 100, - DIAG_SIZE_REFACTORING = 1000, - DIAG_SIZE_INSTALLAPI = 100, - DIAG_SIZE_CAS = 100, - }; - // Start position for diagnostics. - enum { - DIAG_START_COMMON = 0, - DIAG_START_DRIVER = DIAG_START_COMMON + static_cast(DIAG_SIZE_COMMON), - DIAG_START_FRONTEND = DIAG_START_DRIVER + static_cast(DIAG_SIZE_DRIVER), - DIAG_START_SERIALIZATION = DIAG_START_FRONTEND + static_cast(DIAG_SIZE_FRONTEND), - DIAG_START_LEX = DIAG_START_SERIALIZATION + static_cast(DIAG_SIZE_SERIALIZATION), - DIAG_START_PARSE = DIAG_START_LEX + static_cast(DIAG_SIZE_LEX), - DIAG_START_AST = DIAG_START_PARSE + static_cast(DIAG_SIZE_PARSE), - DIAG_START_COMMENT = DIAG_START_AST + static_cast(DIAG_SIZE_AST), - DIAG_START_CROSSTU = DIAG_START_COMMENT + static_cast(DIAG_SIZE_COMMENT), - DIAG_START_SEMA = DIAG_START_CROSSTU + static_cast(DIAG_SIZE_CROSSTU), - DIAG_START_ANALYSIS = DIAG_START_SEMA + static_cast(DIAG_SIZE_SEMA), - DIAG_START_REFACTORING = DIAG_START_ANALYSIS + static_cast(DIAG_SIZE_ANALYSIS), - DIAG_START_INSTALLAPI = DIAG_START_REFACTORING + static_cast(DIAG_SIZE_REFACTORING), - DIAG_START_CAS = DIAG_START_INSTALLAPI + static_cast(DIAG_START_INSTALLAPI), - DIAG_UPPER_LIMIT = DIAG_START_CAS + static_cast(DIAG_SIZE_CAS) - }; - - class CustomDiagInfo; - - /// All of the diagnostics that can be emitted by the frontend. - typedef unsigned kind; - - /// Enum values that allow the client to map NOTEs, WARNINGs, and EXTENSIONs - /// to either Ignore (nothing), Remark (emit a remark), Warning - /// (emit a warning) or Error (emit as an error). It allows clients to - /// map ERRORs to Error or Fatal (stop emitting diagnostics after this one). - enum class Severity : uint8_t { - // NOTE: 0 means "uncomputed". - Ignored = 1, ///< Do not present this diagnostic, ignore it. - Remark = 2, ///< Present this diagnostic as a remark. - Warning = 3, ///< Present this diagnostic as a warning. - Error = 4, ///< Present this diagnostic as an error. - Fatal = 5 ///< Present this diagnostic as a fatal error. - }; - - /// Flavors of diagnostics we can emit. Used to filter for a particular - /// kind of diagnostic (for instance, for -W/-R flags). - enum class Flavor { - WarningOrError, ///< A diagnostic that indicates a problem or potential - ///< problem. Can be made fatal by -Werror. - Remark ///< A diagnostic that indicates normal progress through - ///< compilation. - }; - } // end namespace diag +class DiagnosticsEngine; +class DiagnosticBuilder; +class LangOptions; +class SourceLocation; + +// Import the diagnostic enums themselves. +namespace diag { +enum class Group; + +// Size of each of the diagnostic categories. +enum { + DIAG_SIZE_COMMON = 300, + DIAG_SIZE_DRIVER = 400, + DIAG_SIZE_FRONTEND = 200, + DIAG_SIZE_SERIALIZATION = 120, + DIAG_SIZE_LEX = 500, + DIAG_SIZE_PARSE = 800, + DIAG_SIZE_AST = 300, + DIAG_SIZE_COMMENT = 100, + DIAG_SIZE_CROSSTU = 100, + DIAG_SIZE_SEMA = 5000, + DIAG_SIZE_ANALYSIS = 100, + DIAG_SIZE_REFACTORING = 1000, + DIAG_SIZE_INSTALLAPI = 100, + DIAG_SIZE_TRAP = 100, + DIAG_SIZE_CAS = 100, +}; +// Start position for diagnostics. +// clang-format off +enum { + DIAG_START_COMMON = 0, + DIAG_START_DRIVER = DIAG_START_COMMON + static_cast(DIAG_SIZE_COMMON), + DIAG_START_FRONTEND = DIAG_START_DRIVER + static_cast(DIAG_SIZE_DRIVER), + DIAG_START_SERIALIZATION = DIAG_START_FRONTEND + static_cast(DIAG_SIZE_FRONTEND), + DIAG_START_LEX = DIAG_START_SERIALIZATION + static_cast(DIAG_SIZE_SERIALIZATION), + DIAG_START_PARSE = DIAG_START_LEX + static_cast(DIAG_SIZE_LEX), + DIAG_START_AST = DIAG_START_PARSE + static_cast(DIAG_SIZE_PARSE), + DIAG_START_COMMENT = DIAG_START_AST + static_cast(DIAG_SIZE_AST), + DIAG_START_CROSSTU = DIAG_START_COMMENT + static_cast(DIAG_SIZE_COMMENT), + DIAG_START_SEMA = DIAG_START_CROSSTU + static_cast(DIAG_SIZE_CROSSTU), + DIAG_START_ANALYSIS = DIAG_START_SEMA + static_cast(DIAG_SIZE_SEMA), + DIAG_START_REFACTORING = DIAG_START_ANALYSIS + static_cast(DIAG_SIZE_ANALYSIS), + DIAG_START_INSTALLAPI = DIAG_START_REFACTORING + static_cast(DIAG_SIZE_REFACTORING), + DIAG_START_TRAP = DIAG_START_INSTALLAPI + static_cast(DIAG_SIZE_INSTALLAPI), + DIAG_START_CAS = DIAG_START_TRAP + static_cast(DIAG_SIZE_TRAP), + DIAG_UPPER_LIMIT = DIAG_START_CAS + static_cast(DIAG_SIZE_CAS) +}; +// clang-format on + +class CustomDiagInfo; + +/// All of the diagnostics that can be emitted by the frontend. +typedef unsigned kind; + +/// Enum values that allow the client to map NOTEs, WARNINGs, and EXTENSIONs +/// to either Ignore (nothing), Remark (emit a remark), Warning +/// (emit a warning) or Error (emit as an error). It allows clients to +/// map ERRORs to Error or Fatal (stop emitting diagnostics after this one). +enum class Severity : uint8_t { + // NOTE: 0 means "uncomputed". + Ignored = 1, ///< Do not present this diagnostic, ignore it. + Remark = 2, ///< Present this diagnostic as a remark. + Warning = 3, ///< Present this diagnostic as a warning. + Error = 4, ///< Present this diagnostic as an error. + Fatal = 5 ///< Present this diagnostic as a fatal error. +}; + +/// Flavors of diagnostics we can emit. Used to filter for a particular +/// kind of diagnostic (for instance, for -W/-R flags). +enum class Flavor { + WarningOrError, ///< A diagnostic that indicates a problem or potential + ///< problem. Can be made fatal by -Werror. + Remark ///< A diagnostic that indicates normal progress through + ///< compilation. +}; +} // end namespace diag } // end namespace clang // This has to be included *after* the DIAG_START_ enums above are defined. @@ -175,7 +179,8 @@ class DiagnosticMapping { /// Used for handling and querying diagnostic IDs. /// -/// Can be used and shared by multiple Diagnostics for multiple translation units. +/// Can be used and shared by multiple Diagnostics for multiple translation +/// units. class DiagnosticIDs : public RefCountedBase { public: /// The level of the diagnostic, after it has been through mapping. @@ -188,7 +193,8 @@ class DiagnosticIDs : public RefCountedBase { CLASS_REMARK = 0x02, CLASS_WARNING = 0x03, CLASS_EXTENSION = 0x04, - CLASS_ERROR = 0x05 + CLASS_ERROR = 0x05, + CLASS_TRAP = 0x06 }; static bool IsCustomDiag(diag::kind Diag) { @@ -358,6 +364,10 @@ class DiagnosticIDs : public RefCountedBase { /// bool isExtensionDiag(unsigned DiagID, bool &EnabledByDefault) const; + bool isTrapDiag(unsigned DiagID) const { + return getDiagClass(DiagID) == CLASS_TRAP; + } + /// Given a group ID, returns the flag that toggles the group. /// For example, for Group::DeprecatedDeclarations, returns /// "deprecated-declarations". @@ -496,6 +506,6 @@ class DiagnosticIDs : public RefCountedBase { friend class DiagnosticsEngine; }; -} // end namespace clang +} // end namespace clang #endif 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 015ad31d7db18..2c950102626f2 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2717,6 +2717,27 @@ def fsanitize_undefined_trap_on_error def fno_sanitize_undefined_trap_on_error : Flag<["-"], "fno-sanitize-undefined-trap-on-error">, Group, Alias, AliasArgs<["undefined"]>; +def fsanitize_debug_trap_reasons_EQ + : Joined<["-"], "fsanitize-debug-trap-reasons=">, Group, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Set how trap reasons are emitted. " + "`none` - Not emitted. This gives the smallest debug info; " + "`basic` - Emit a fixed trap message per check type. This increases the " + "debug info size but not as much as `detailed`; " + "`detailed` - Emit a more detailed trap message. This increases the " + "debug info size the most. Default is `detailed`.">, + Values<"none,basic,detailed">, + NormalizedValuesScope<"CodeGenOptions::SanitizeDebugTrapReasonKind">, + NormalizedValues<["None", "Basic", "Detailed"]>, + MarshallingInfoEnum, "Detailed">; +def fsanitize_debug_trap_reasons + : Flag<["-"], "fsanitize-debug-trap-reasons">, Group, + Alias, AliasArgs<["detailed"]>, + HelpText<"Alias for -fsanitize-debug-trap-reasons=detailed">; +def fno_sanitize_debug_trap_reasons + : Flag<["-"], "fno-sanitize-debug-trap-reasons">, Group, + Alias, AliasArgs<["none"]>, + HelpText<"Alias for -fsanitize-debug-trap-reasons=none">; defm sanitize_minimal_runtime : BoolOption<"f", "sanitize-minimal-runtime", CodeGenOpts<"SanitizeMinimalRuntime">, DefaultFalse, PosFlag, diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp index 0806fa4ca8177..17d39ba8d0ce4 100644 --- a/clang/lib/Basic/Diagnostic.cpp +++ b/clang/lib/Basic/Diagnostic.cpp @@ -672,6 +672,8 @@ void DiagnosticsEngine::Report(const StoredDiagnostic &storedDiag) { void DiagnosticsEngine::Report(Level DiagLevel, const Diagnostic &Info) { assert(DiagLevel != Ignored && "Cannot emit ignored diagnostics!"); + assert(!getDiagnosticIDs()->isTrapDiag(Info.getID()) && + "Trap diagnostics should not be consumed by the DiagnosticsEngine"); Client->HandleDiagnostic(DiagLevel, Info); if (Client->IncludeInDiagnosticCounts()) { if (DiagLevel == Warning) diff --git a/clang/lib/Basic/DiagnosticIDs.cpp b/clang/lib/Basic/DiagnosticIDs.cpp index bde0bdd8f46bc..bb66e402e047e 100644 --- a/clang/lib/Basic/DiagnosticIDs.cpp +++ b/clang/lib/Basic/DiagnosticIDs.cpp @@ -69,6 +69,7 @@ enum DiagnosticClass { CLASS_WARNING = DiagnosticIDs::CLASS_WARNING, CLASS_EXTENSION = DiagnosticIDs::CLASS_EXTENSION, CLASS_ERROR = DiagnosticIDs::CLASS_ERROR, + CLASS_TRAP = DiagnosticIDs::CLASS_TRAP, }; struct StaticDiagInfoRec { @@ -139,6 +140,7 @@ VALIDATE_DIAG_SIZE(SEMA) VALIDATE_DIAG_SIZE(ANALYSIS) VALIDATE_DIAG_SIZE(REFACTORING) VALIDATE_DIAG_SIZE(INSTALLAPI) +VALIDATE_DIAG_SIZE(TRAP) VALIDATE_DIAG_SIZE(CAS) #undef VALIDATE_DIAG_SIZE #undef STRINGIFY_NAME @@ -172,6 +174,7 @@ const StaticDiagInfoRec StaticDiagInfo[] = { #include "clang/Basic/DiagnosticAnalysisKinds.inc" #include "clang/Basic/DiagnosticRefactoringKinds.inc" #include "clang/Basic/DiagnosticInstallAPIKinds.inc" +#include "clang/Basic/DiagnosticTrapKinds.inc" #include "clang/Basic/DiagnosticCASKinds.inc" // clang-format on #undef DIAG @@ -216,7 +219,8 @@ CATEGORY(SEMA, CROSSTU) CATEGORY(ANALYSIS, SEMA) CATEGORY(REFACTORING, ANALYSIS) CATEGORY(INSTALLAPI, REFACTORING) -CATEGORY(CAS, INSTALLAPI) +CATEGORY(TRAP, INSTALLAPI) +CATEGORY(CAS, TRAP) #undef CATEGORY // Avoid out of bounds reads. diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 2501879efe833..3d38dca987258 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -6535,7 +6535,7 @@ CodeGenFunction::LexicalScope::~LexicalScope() { static std::string SanitizerHandlerToCheckLabel(SanitizerHandler Handler) { std::string Label; switch (Handler) { -#define SANITIZER_CHECK(Enum, Name, Version) \ +#define SANITIZER_CHECK(Enum, Name, Version, Msg) \ case Enum: \ Label = "__ubsan_check_" #Name; \ break; diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 2f75509d41f50..bd6b9a6c7e1be 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -312,6 +312,17 @@ void CodeGenFunction::EmitValueTerminatedAssignmentCheck( EmitBoundsSafetyTrapCheck(Check, BNS_TRAP_TERMINATED_BY_TERM_ASSIGN); } + +static llvm::StringRef GetUBSanTrapForHandler(SanitizerHandler ID) { + switch (ID) { +#define SANITIZER_CHECK(Enum, Name, Version, Msg) \ + case SanitizerHandler::Enum: \ + return Msg; + LIST_SANITIZER_CHECKS +#undef SANITIZER_CHECK + } +} + /// CreateTempAlloca - This creates a alloca and inserts it into the entry /// block. RawAddress @@ -4162,7 +4173,7 @@ struct SanitizerHandlerInfo { } const SanitizerHandlerInfo SanitizerHandlers[] = { -#define SANITIZER_CHECK(Enum, Name, Version) {#Name, Version}, +#define SANITIZER_CHECK(Enum, Name, Version, Msg) {#Name, Version}, LIST_SANITIZER_CHECKS #undef SANITIZER_CHECK }; @@ -4222,7 +4233,7 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF, void CodeGenFunction::EmitCheck( ArrayRef> Checked, SanitizerHandler CheckHandler, ArrayRef StaticArgs, - ArrayRef DynamicArgs) { + ArrayRef DynamicArgs, const TrapReason *TR) { assert(IsSanitizerScope); assert(Checked.size() > 0); assert(CheckHandler >= 0 && @@ -4261,7 +4272,7 @@ void CodeGenFunction::EmitCheck( } if (TrapCond) - EmitTrapCheck(TrapCond, CheckHandler, NoMerge); + EmitTrapCheck(TrapCond, CheckHandler, NoMerge, TR); if (!FatalCond && !RecoverableCond) return; @@ -4467,6 +4478,8 @@ void CodeGenFunction::EmitCfiCheckFail() { StartFunction(GlobalDecl(), CGM.getContext().VoidTy, F, FI, Args, SourceLocation()); + ApplyDebugLocation ADL = ApplyDebugLocation::CreateArtificial(*this); + // This function is not affected by NoSanitizeList. This function does // not have a source location, but "src:*" would still apply. Revert any // changes to SanOpts made in StartFunction. @@ -4554,11 +4567,9 @@ void CodeGenFunction::EmitUnreachable(SourceLocation Loc) { void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked, SanitizerHandler CheckHandlerID, - bool NoMerge, - /*TO_UPSTREAM(BoundsSafety) ON*/ + bool NoMerge, const TrapReason *TR, StringRef Annotation, - StringRef TrapMessage) { - /*TO_UPSTREAM(BoundsSafety) OFF*/ + StringRef BoundsSafetyTrapMessage) { llvm::BasicBlock *Cont = createBasicBlock("cont"); // If we're optimizing, collapse all calls to trap down to just one per @@ -4568,13 +4579,35 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked, llvm::BasicBlock *&TrapBB = TrapBBs[CheckHandlerID]; - /*TO_UPSTREAM(BoundsSafety) ON*/ llvm::DILocation *TrapLocation = Builder.getCurrentDebugLocation(); - if (CheckHandlerID == SanitizerHandler::BoundsSafety && getDebugInfo()) { + llvm::StringRef TrapMessage; + llvm::StringRef TrapCategory; + auto DebugTrapReasonKind = CGM.getCodeGenOpts().getSanitizeDebugTrapReasons(); + if (TR && !TR->isEmpty() && + DebugTrapReasonKind == + CodeGenOptions::SanitizeDebugTrapReasonKind::Detailed) { + TrapMessage = TR->getMessage(); + TrapCategory = TR->getCategory(); + } else { + /* TO_UPSTREAM(BoundsSafety) ON*/ + // FIXME: Move to using `TrapReason` (rdar://158623471). + if (CheckHandlerID == SanitizerHandler::BoundsSafety) { + TrapMessage = BoundsSafetyTrapMessage; + TrapCategory = GetBoundsSafetyTrapMessagePrefix(); + } else { + TrapMessage = GetUBSanTrapForHandler(CheckHandlerID); + TrapCategory = "Undefined Behavior Sanitizer"; + } + } + + if (getDebugInfo() && !(TrapMessage.empty() && TrapCategory.empty()) && + DebugTrapReasonKind != + CodeGenOptions::SanitizeDebugTrapReasonKind::None && + TrapLocation) { TrapLocation = getDebugInfo()->CreateTrapFailureMessageFor( - TrapLocation, GetBoundsSafetyTrapMessagePrefix(), TrapMessage); + TrapLocation, TrapCategory, TrapMessage); } - /*TO_UPSTREAM(BoundsSafety) OFF*/ + /* TO_UPSTREAM(BoundsSafety) OFF*/ NoMerge = NoMerge || !CGM.getCodeGenOpts().OptimizationLevel || (CurCodeDecl && CurCodeDecl->hasAttr()); @@ -4673,7 +4706,8 @@ void CodeGenFunction::EmitBoundsSafetyTrapCheck(llvm::Value *Checked, // We still need to pass `OptRemark` because not all emitted instructions // can be covered by BoundsSafetyOptRemarkScope. This is because EmitTrapCheck // caches basic blocks that contain instructions that need annotating. - EmitTrapCheck(Checked, SanitizerHandler::BoundsSafety, false, + EmitTrapCheck(Checked, SanitizerHandler::BoundsSafety, /*NoMerge=*/false, + /*TR=*/nullptr, GetBoundsSafetyOptRemarkString(OptRemark), GetBoundsSafetyTrapMessageSuffix(kind, TrapCtx)); } diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 23fa80ea8cbb2..bd5a07c1518d2 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "BoundsSafetyTraps.h" #include "CGCXXABI.h" #include "CGCleanup.h" #include "CGDebugInfo.h" @@ -20,8 +21,8 @@ #include "CodeGenFunction.h" #include "CodeGenModule.h" #include "ConstantEmitter.h" -#include "BoundsSafetyTraps.h" #include "TargetInfo.h" +#include "TrapReasonBuilder.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclObjC.h" @@ -30,6 +31,7 @@ #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtVisitor.h" #include "clang/Basic/CodeGenOptions.h" +#include "clang/Basic/DiagnosticTrap.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/APFixedPoint.h" #include "llvm/IR/Argument.h" @@ -1946,6 +1948,7 @@ void ScalarExprEmitter::EmitBinOpCheck( SanitizerHandler Check; SmallVector StaticData; SmallVector DynamicData; + TrapReason TR; BinaryOperatorKind Opcode = Info.Opcode; if (BinaryOperator::isCompoundAssignmentOp(Opcode)) @@ -1972,19 +1975,43 @@ void ScalarExprEmitter::EmitBinOpCheck( StaticData.push_back(CGF.EmitCheckTypeDescriptor(Info.Ty)); } else { // Arithmetic overflow (+, -, *). + int ArithOverflowKind = 0; switch (Opcode) { - case BO_Add: Check = SanitizerHandler::AddOverflow; break; - case BO_Sub: Check = SanitizerHandler::SubOverflow; break; - case BO_Mul: Check = SanitizerHandler::MulOverflow; break; - default: llvm_unreachable("unexpected opcode for bin op check"); + case BO_Add: { + Check = SanitizerHandler::AddOverflow; + ArithOverflowKind = diag::UBSanArithKind::Add; + break; + } + case BO_Sub: { + Check = SanitizerHandler::SubOverflow; + ArithOverflowKind = diag::UBSanArithKind::Sub; + break; + } + case BO_Mul: { + Check = SanitizerHandler::MulOverflow; + ArithOverflowKind = diag::UBSanArithKind::Mul; + break; + } + default: + llvm_unreachable("unexpected opcode for bin op check"); } StaticData.push_back(CGF.EmitCheckTypeDescriptor(Info.Ty)); + if (CGF.CGM.getCodeGenOpts().SanitizeTrap.has( + SanitizerKind::UnsignedIntegerOverflow) || + CGF.CGM.getCodeGenOpts().SanitizeTrap.has( + SanitizerKind::SignedIntegerOverflow)) { + // Only pay the cost for constructing the trap diagnostic if they are + // going to be used. + CGF.CGM.BuildTrapReason(diag::trap_ubsan_arith_overflow, TR) + << Info.Ty->isSignedIntegerOrEnumerationType() << ArithOverflowKind + << Info.E; + } } DynamicData.push_back(Info.LHS); DynamicData.push_back(Info.RHS); } - CGF.EmitCheck(Checks, Check, StaticData, DynamicData); + CGF.EmitCheck(Checks, Check, StaticData, DynamicData, &TR); } //===----------------------------------------------------------------------===// diff --git a/clang/lib/CodeGen/CMakeLists.txt b/clang/lib/CodeGen/CMakeLists.txt index 6394777793469..873cae903a331 100644 --- a/clang/lib/CodeGen/CMakeLists.txt +++ b/clang/lib/CodeGen/CMakeLists.txt @@ -157,6 +157,7 @@ add_clang_library(clangCodeGen Targets/WebAssembly.cpp Targets/X86.cpp Targets/XCore.cpp + TrapReasonBuilder.cpp VarBypassDetector.cpp DEPENDS diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 1343ed082ab3c..d0d1aeb3eb520 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -5396,7 +5396,8 @@ class CodeGenFunction : public CodeGenTypeCache { EmitCheck(ArrayRef> Checked, SanitizerHandler Check, ArrayRef StaticArgs, - ArrayRef DynamicArgs); + ArrayRef DynamicArgs, + const TrapReason *TR = nullptr); /// Emit a slow path cross-DSO CFI check which calls __cfi_slowpath /// if Cond if false. @@ -5412,7 +5413,7 @@ class CodeGenFunction : public CodeGenTypeCache { /// Create a basic block that will call the trap intrinsic, and emit a /// conditional branch to it, for the -ftrapv checks. void EmitTrapCheck(llvm::Value *Checked, SanitizerHandler CheckHandlerID, - bool NoMerge = false, + bool NoMerge = false, const TrapReason *TR = nullptr, StringRef Annotation = "", StringRef TrapMessage = ""); /* TO_UPSTREAM(BoundsSafety) ON*/ diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index d9d8a1d465ffd..12cda67b7b881 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -17,6 +17,7 @@ #include "CodeGenTypeCache.h" #include "CodeGenTypes.h" #include "SanitizerMetadata.h" +#include "TrapReasonBuilder.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclOpenMP.h" @@ -1841,6 +1842,11 @@ class CodeGenModule : public CodeGenTypeCache { return PAlign; } + /// Helper function to construct a TrapReasonBuilder + TrapReasonBuilder BuildTrapReason(unsigned DiagID, TrapReason &TR) { + return TrapReasonBuilder(&getDiags(), DiagID, TR); + } + private: bool shouldDropDLLAttribute(const Decl *D, const llvm::GlobalValue *GV) const; diff --git a/clang/lib/CodeGen/SanitizerHandler.h b/clang/lib/CodeGen/SanitizerHandler.h index bb42e3947cf14..a66e7ab354eb2 100644 --- a/clang/lib/CodeGen/SanitizerHandler.h +++ b/clang/lib/CodeGen/SanitizerHandler.h @@ -14,35 +14,69 @@ #define LLVM_CLANG_LIB_CODEGEN_SANITIZER_HANDLER_H #define LIST_SANITIZER_CHECKS \ - SANITIZER_CHECK(AddOverflow, add_overflow, 0) \ - SANITIZER_CHECK(BuiltinUnreachable, builtin_unreachable, 0) \ - SANITIZER_CHECK(CFICheckFail, cfi_check_fail, 0) \ - SANITIZER_CHECK(DivremOverflow, divrem_overflow, 0) \ - SANITIZER_CHECK(DynamicTypeCacheMiss, dynamic_type_cache_miss, 0) \ - SANITIZER_CHECK(FloatCastOverflow, float_cast_overflow, 0) \ - SANITIZER_CHECK(FunctionTypeMismatch, function_type_mismatch, 0) \ - SANITIZER_CHECK(ImplicitConversion, implicit_conversion, 0) \ - SANITIZER_CHECK(InvalidBuiltin, invalid_builtin, 0) \ - SANITIZER_CHECK(InvalidObjCCast, invalid_objc_cast, 0) \ - SANITIZER_CHECK(LoadInvalidValue, load_invalid_value, 0) \ - SANITIZER_CHECK(MissingReturn, missing_return, 0) \ - SANITIZER_CHECK(MulOverflow, mul_overflow, 0) \ - SANITIZER_CHECK(NegateOverflow, negate_overflow, 0) \ - SANITIZER_CHECK(NullabilityArg, nullability_arg, 0) \ - SANITIZER_CHECK(NullabilityReturn, nullability_return, 1) \ - SANITIZER_CHECK(NonnullArg, nonnull_arg, 0) \ - SANITIZER_CHECK(NonnullReturn, nonnull_return, 1) \ - SANITIZER_CHECK(OutOfBounds, out_of_bounds, 0) \ - SANITIZER_CHECK(PointerOverflow, pointer_overflow, 0) \ - SANITIZER_CHECK(ShiftOutOfBounds, shift_out_of_bounds, 0) \ - SANITIZER_CHECK(SubOverflow, sub_overflow, 0) \ - SANITIZER_CHECK(TypeMismatch, type_mismatch, 1) \ - SANITIZER_CHECK(AlignmentAssumption, alignment_assumption, 0) \ - SANITIZER_CHECK(VLABoundNotPositive, vla_bound_not_positive, 0) \ - SANITIZER_CHECK(BoundsSafety, bounds_safety, 0) + SANITIZER_CHECK(AddOverflow, add_overflow, 0, "Integer addition overflowed") \ + SANITIZER_CHECK(BuiltinUnreachable, builtin_unreachable, 0, \ + "_builtin_unreachable(), execution reached an unreachable " \ + "program point") \ + SANITIZER_CHECK(CFICheckFail, cfi_check_fail, 0, \ + "Control flow integrity check failed") \ + SANITIZER_CHECK(DivremOverflow, divrem_overflow, 0, \ + "Integer divide or remainder overflowed") \ + SANITIZER_CHECK(DynamicTypeCacheMiss, dynamic_type_cache_miss, 0, \ + "Dynamic type cache miss, member call made on an object " \ + "whose dynamic type differs from the expected type") \ + SANITIZER_CHECK(FloatCastOverflow, float_cast_overflow, 0, \ + "Floating-point to integer conversion overflowed") \ + SANITIZER_CHECK(FunctionTypeMismatch, function_type_mismatch, 0, \ + "Function called with mismatched signature") \ + SANITIZER_CHECK(ImplicitConversion, implicit_conversion, 0, \ + "Implicit integer conversion overflowed or lost data") \ + SANITIZER_CHECK(InvalidBuiltin, invalid_builtin, 0, \ + "Invalid use of builtin function") \ + SANITIZER_CHECK(InvalidObjCCast, invalid_objc_cast, 0, \ + "Invalid Objective-C cast") \ + SANITIZER_CHECK(LoadInvalidValue, load_invalid_value, 0, \ + "Loaded an invalid or uninitialized value for the type") \ + SANITIZER_CHECK(MissingReturn, missing_return, 0, \ + "Execution reached the end of a value-returning function " \ + "without returning a value") \ + SANITIZER_CHECK(MulOverflow, mul_overflow, 0, \ + "Integer multiplication overflowed") \ + SANITIZER_CHECK(NegateOverflow, negate_overflow, 0, \ + "Integer negation overflowed") \ + SANITIZER_CHECK( \ + NullabilityArg, nullability_arg, 0, \ + "Passing null as an argument which is annotated with _Nonnull") \ + SANITIZER_CHECK(NullabilityReturn, nullability_return, 1, \ + "Returning null from a function with a return type " \ + "annotated with _Nonnull") \ + SANITIZER_CHECK(NonnullArg, nonnull_arg, 0, \ + "Passing null pointer as an argument which is declared to " \ + "never be null") \ + SANITIZER_CHECK(NonnullReturn, nonnull_return, 1, \ + "Returning null pointer from a function which is declared " \ + "to never return null") \ + SANITIZER_CHECK(OutOfBounds, out_of_bounds, 0, "Array index out of bounds") \ + SANITIZER_CHECK(PointerOverflow, pointer_overflow, 0, \ + "Pointer arithmetic overflowed bounds") \ + SANITIZER_CHECK(ShiftOutOfBounds, shift_out_of_bounds, 0, \ + "Shift exponent is too large for the type") \ + SANITIZER_CHECK(SubOverflow, sub_overflow, 0, \ + "Integer subtraction overflowed") \ + SANITIZER_CHECK(TypeMismatch, type_mismatch, 1, \ + "Type mismatch in operation") \ + SANITIZER_CHECK(AlignmentAssumption, alignment_assumption, 0, \ + "Alignment assumption violated") \ + SANITIZER_CHECK( \ + VLABoundNotPositive, vla_bound_not_positive, 0, \ + "Variable length array bound evaluates to non-positive value") \ + SANITIZER_CHECK(BoundsSafety, bounds_safety, 0, \ + "") // BoundsSafety Msg is empty because it is not considered + // part of UBSan; therefore, no trap reason is emitted for + // this case. enum SanitizerHandler { -#define SANITIZER_CHECK(Enum, Name, Version) Enum, +#define SANITIZER_CHECK(Enum, Name, Version, Msg) Enum, LIST_SANITIZER_CHECKS #undef SANITIZER_CHECK }; diff --git a/clang/lib/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 fe76a0f9df887..bce6d80a5b0bc 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -1385,6 +1385,8 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args, CmdArgs.push_back(Args.MakeArgString("-fsanitize-annotate-debug-info=" + toString(AnnotateDebugInfo))); + Args.AddLastArg(CmdArgs, options::OPT_fsanitize_debug_trap_reasons_EQ); + addSpecialCaseListOpt(Args, CmdArgs, "-fsanitize-ignorelist=", UserIgnorelistFiles); addSpecialCaseListOpt(Args, CmdArgs, diff --git a/clang/test/CodeGen/bounds-checking-debuginfo.c b/clang/test/CodeGen/bounds-checking-debuginfo.c index 74c06665dfe02..bd7aedd7ac2c1 100644 --- a/clang/test/CodeGen/bounds-checking-debuginfo.c +++ b/clang/test/CodeGen/bounds-checking-debuginfo.c @@ -25,13 +25,13 @@ void d(double*); // CHECK-TRAP-NEXT: [[TMP1:%.*]] = icmp ult i64 [[TMP0]], 10, !dbg [[DBG23]], !nosanitize [[META10]] // CHECK-TRAP-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !dbg [[DBG23]], !prof [[PROF27:![0-9]+]], !nosanitize [[META10]] // CHECK-TRAP: [[TRAP]]: -// CHECK-TRAP-NEXT: call void @llvm.ubsantrap(i8 18) #[[ATTR3:[0-9]+]], !dbg [[DBG23]], !nosanitize [[META10]] -// CHECK-TRAP-NEXT: unreachable, !dbg [[DBG23]], !nosanitize [[META10]] +// CHECK-TRAP-NEXT: call void @llvm.ubsantrap(i8 18) #[[ATTR3:[0-9]+]], !dbg [[DBGTRAP:![0-9]+]], !nosanitize [[META10]] +// CHECK-TRAP-NEXT: unreachable, !dbg [[DBGTRAP]], !nosanitize [[META10]] // CHECK-TRAP: [[CONT]]: // CHECK-TRAP-NEXT: [[IDXPROM:%.*]] = sext i32 [[CALL]] to i64, !dbg [[DBG26:![0-9]+]] // CHECK-TRAP-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [10 x double], ptr [[A]], i64 0, i64 [[IDXPROM]], !dbg [[DBG26]] // CHECK-TRAP-NEXT: [[TMP2:%.*]] = load double, ptr [[ARRAYIDX]], align 8, !dbg [[DBG26]] -// CHECK-TRAP-NEXT: ret double [[TMP2]], !dbg [[DBG28:![0-9]+]] +// CHECK-TRAP-NEXT: ret double [[TMP2]], !dbg [[DBG30:![0-9]+]] // // CHECK-NOTRAP-LABEL: define dso_local double @f1( // CHECK-NOTRAP-SAME: i32 noundef [[B:%.*]], i32 noundef [[I:%.*]]) #[[ATTR0:[0-9]+]] !dbg [[DBG4:![0-9]+]] { @@ -93,7 +93,9 @@ double f1(int b, int i) { // CHECK-TRAP: [[META25]] = !DISubroutineType(types: null) // CHECK-TRAP: [[DBG26]] = !DILocation(line: 66, column: 10, scope: [[DBG4]]) // CHECK-TRAP: [[PROF27]] = !{!"branch_weights", i32 1048575, i32 1} -// CHECK-TRAP: [[DBG28]] = !DILocation(line: 66, column: 3, scope: [[DBG4]]) +// CHECK-TRAP: [[DBGTRAP]] = !DILocation(line: 0, scope: [[TRAPMSG:![0-9]+]], inlinedAt: [[DBG23]]) +// CHECK-TRAP: [[TRAPMSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Array index out of bounds", scope: [[META5]], file: [[META5]], type: [[META25]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META0]]) +// CHECK-TRAP: [[DBG30]] = !DILocation(line: 66, column: 3, scope: [[DBG4]]) //. // CHECK-NOTRAP: [[META0:![0-9]+]] = distinct !DICompileUnit(language: DW_LANG_C11, file: [[META1:![0-9]+]], isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) // CHECK-NOTRAP: [[META1]] = !DIFile(filename: "", directory: {{.*}}) diff --git a/clang/test/CodeGen/cfi-icall-generalize-debuginfo.c b/clang/test/CodeGen/cfi-icall-generalize-debuginfo.c index 304b60539c3d1..0ffc2b9e415d4 100644 --- a/clang/test/CodeGen/cfi-icall-generalize-debuginfo.c +++ b/clang/test/CodeGen/cfi-icall-generalize-debuginfo.c @@ -30,11 +30,11 @@ int** f(const char *a, const char **b) { // UNGENERALIZED-NEXT: [[TMP0:%.*]] = tail call i1 @llvm.type.test(ptr [[FP]], metadata !"_ZTSFPPiPKcPS2_E"), !dbg [[DBG34:![0-9]+]], !nosanitize [[META38:![0-9]+]] // UNGENERALIZED-NEXT: br i1 [[TMP0]], label %[[CONT:.*]], label %[[TRAP:.*]], !dbg [[DBG34]], !prof [[PROF39:![0-9]+]], !nosanitize [[META38]] // UNGENERALIZED: [[TRAP]]: -// UNGENERALIZED-NEXT: tail call void @llvm.ubsantrap(i8 2) #[[ATTR4:[0-9]+]], !dbg [[DBG34]], !nosanitize [[META38]] -// UNGENERALIZED-NEXT: unreachable, !dbg [[DBG34]], !nosanitize [[META38]] +// UNGENERALIZED-NEXT: tail call void @llvm.ubsantrap(i8 2) #[[ATTR4:[0-9]+]], !dbg [[DBGTRAP:![0-9]+]], !nosanitize [[META38]] +// UNGENERALIZED-NEXT: unreachable, !dbg [[DBGTRAP]], !nosanitize [[META38]] // UNGENERALIZED: [[CONT]]: // UNGENERALIZED-NEXT: [[CALL:%.*]] = tail call ptr [[FP]](ptr noundef null, ptr noundef null) #[[ATTR5:[0-9]+]], !dbg [[DBG37:![0-9]+]] -// UNGENERALIZED-NEXT: ret void, !dbg [[DBG40:![0-9]+]] +// UNGENERALIZED-NEXT: ret void, !dbg [[DBG42:![0-9]+]] // // GENERALIZED-LABEL: define dso_local void @g( // GENERALIZED-SAME: ptr noundef [[FP:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] !dbg [[DBG25:![0-9]+]] !type [[META31:![0-9]+]] !type [[META32:![0-9]+]] { @@ -43,11 +43,11 @@ int** f(const char *a, const char **b) { // GENERALIZED-NEXT: [[TMP0:%.*]] = tail call i1 @llvm.type.test(ptr [[FP]], metadata !"_ZTSFPvPKvS_E.generalized"), !dbg [[DBG34:![0-9]+]], !nosanitize [[META38:![0-9]+]] // GENERALIZED-NEXT: br i1 [[TMP0]], label %[[CONT:.*]], label %[[TRAP:.*]], !dbg [[DBG34]], !prof [[PROF39:![0-9]+]], !nosanitize [[META38]] // GENERALIZED: [[TRAP]]: -// GENERALIZED-NEXT: tail call void @llvm.ubsantrap(i8 2) #[[ATTR4:[0-9]+]], !dbg [[DBG34]], !nosanitize [[META38]] -// GENERALIZED-NEXT: unreachable, !dbg [[DBG34]], !nosanitize [[META38]] +// GENERALIZED-NEXT: tail call void @llvm.ubsantrap(i8 2) #[[ATTR4:[0-9]+]], !dbg [[DBGTRAP:![0-9]+]], !nosanitize [[META38]] +// GENERALIZED-NEXT: unreachable, !dbg [[DBGTRAP]], !nosanitize [[META38]] // GENERALIZED: [[CONT]]: // GENERALIZED-NEXT: [[CALL:%.*]] = tail call ptr [[FP]](ptr noundef null, ptr noundef null) #[[ATTR5:[0-9]+]], !dbg [[DBG37:![0-9]+]] -// GENERALIZED-NEXT: ret void, !dbg [[DBG40:![0-9]+]] +// GENERALIZED-NEXT: ret void, !dbg [[DBG42:![0-9]+]] // void g(int** (*fp)(const char *, const char **)) { fp(0, 0); @@ -90,7 +90,9 @@ void g(int** (*fp)(const char *, const char **)) { // UNGENERALIZED: [[DBG37]] = !DILocation(line: 53, column: 3, scope: [[DBG25]]) // UNGENERALIZED: [[META38]] = !{} // UNGENERALIZED: [[PROF39]] = !{!"branch_weights", i32 1048575, i32 1} -// UNGENERALIZED: [[DBG40]] = !DILocation(line: 54, column: 1, scope: [[DBG25]]) +// UNGENERALIZED: [[DBGTRAP]] = !DILocation(line: 0, scope: [[TRAPMSG:![0-9]+]], inlinedAt: [[DBG34]]) +// UNGENERALIZED: [[TRAPMSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Control flow integrity check failed", scope: [[META11]], file: [[META11]], type: [[META36]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META0]]) +// UNGENERALIZED: [[DBG42]] = !DILocation(line: 54, column: 1, scope: [[DBG25]]) //. // GENERALIZED: [[META0:![0-9]+]] = distinct !DICompileUnit(language: DW_LANG_C11, file: [[META1:![0-9]+]], isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: [[META2:![0-9]+]], splitDebugInlining: false, nameTableKind: None) // GENERALIZED: [[META1]] = !DIFile(filename: "{{.*}}", directory: {{.*}}) @@ -128,5 +130,7 @@ void g(int** (*fp)(const char *, const char **)) { // GENERALIZED: [[DBG37]] = !DILocation(line: 53, column: 3, scope: [[DBG25]]) // GENERALIZED: [[META38]] = !{} // GENERALIZED: [[PROF39]] = !{!"branch_weights", i32 1048575, i32 1} -// GENERALIZED: [[DBG40]] = !DILocation(line: 54, column: 1, scope: [[DBG25]]) +// GENERALIZED: [[DBGTRAP]] = !DILocation(line: 0, scope: [[TRAPMSG:![0-9]+]], inlinedAt: [[DBG34]]) +// GENERALIZED: [[TRAPMSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Control flow integrity check failed", scope: [[META11]], file: [[META11]], type: [[META36]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META0]]) +// GENERALIZED: [[DBG42]] = !DILocation(line: 54, column: 1, scope: [[DBG25]]) //. diff --git a/clang/test/CodeGen/cfi-icall-normalize2-debuginfo.c b/clang/test/CodeGen/cfi-icall-normalize2-debuginfo.c index a2f6ee0c6805c..258c3bfbc9f55 100644 --- a/clang/test/CodeGen/cfi-icall-normalize2-debuginfo.c +++ b/clang/test/CodeGen/cfi-icall-normalize2-debuginfo.c @@ -15,50 +15,50 @@ // CHECK-NEXT: [[TMP0:%.*]] = tail call i1 @llvm.type.test(ptr [[FN]], metadata !"_ZTSFvu3i32E.normalized"), !dbg [[DBG21:![0-9]+]], !nosanitize [[META25:![0-9]+]] // CHECK-NEXT: br i1 [[TMP0]], label %[[CONT:.*]], label %[[TRAP:.*]], !dbg [[DBG21]], !prof [[PROF26:![0-9]+]], !nosanitize [[META25]] // CHECK: [[TRAP]]: -// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 2) #[[ATTR3:[0-9]+]], !dbg [[DBG21]], !nosanitize [[META25]] -// CHECK-NEXT: unreachable, !dbg [[DBG21]], !nosanitize [[META25]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 2) #[[ATTR3:[0-9]+]], !dbg [[DBGTRAP:![0-9]+]], !nosanitize [[META25]] +// CHECK-NEXT: unreachable, !dbg [[DBGTRAP]], !nosanitize [[META25]] // CHECK: [[CONT]]: // CHECK-NEXT: tail call void [[FN]](i32 noundef [[ARG]]) #[[ATTR4:[0-9]+]], !dbg [[DBG24:![0-9]+]] -// CHECK-NEXT: ret void, !dbg [[DBG27:![0-9]+]] +// CHECK-NEXT: ret void, !dbg [[DBG29:![0-9]+]] // void foo(void (*fn)(int), int arg) { fn(arg); } // CHECK-LABEL: define dso_local void @bar( -// CHECK-SAME: ptr noundef [[FN:%.*]], i32 noundef [[ARG1:%.*]], i32 noundef [[ARG2:%.*]]) local_unnamed_addr #[[ATTR0]] !dbg [[DBG28:![0-9]+]] !type [[META38:![0-9]+]] !type [[META39:![0-9]+]] { +// CHECK-SAME: ptr noundef [[FN:%.*]], i32 noundef [[ARG1:%.*]], i32 noundef [[ARG2:%.*]]) local_unnamed_addr #[[ATTR0]] !dbg [[DBG30:![0-9]+]] !type [[META40:![0-9]+]] !type [[META41:![0-9]+]] { // CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: #dbg_value(ptr [[FN]], [[META35:![0-9]+]], !DIExpression(), [[META40:![0-9]+]]) -// CHECK-NEXT: #dbg_value(i32 [[ARG1]], [[META36:![0-9]+]], !DIExpression(), [[META40]]) -// CHECK-NEXT: #dbg_value(i32 [[ARG2]], [[META37:![0-9]+]], !DIExpression(), [[META40]]) -// CHECK-NEXT: [[TMP0:%.*]] = tail call i1 @llvm.type.test(ptr [[FN]], metadata !"_ZTSFvu3i32S_E.normalized"), !dbg [[DBG41:![0-9]+]], !nosanitize [[META25]] -// CHECK-NEXT: br i1 [[TMP0]], label %[[CONT:.*]], label %[[TRAP:.*]], !dbg [[DBG41]], !prof [[PROF26]], !nosanitize [[META25]] +// CHECK-NEXT: #dbg_value(ptr [[FN]], [[META37:![0-9]+]], !DIExpression(), [[META42:![0-9]+]]) +// CHECK-NEXT: #dbg_value(i32 [[ARG1]], [[META38:![0-9]+]], !DIExpression(), [[META42]]) +// CHECK-NEXT: #dbg_value(i32 [[ARG2]], [[META39:![0-9]+]], !DIExpression(), [[META42]]) +// CHECK-NEXT: [[TMP0:%.*]] = tail call i1 @llvm.type.test(ptr [[FN]], metadata !"_ZTSFvu3i32S_E.normalized"), !dbg [[DBG43:![0-9]+]], !nosanitize [[META25]] +// CHECK-NEXT: br i1 [[TMP0]], label %[[CONT:.*]], label %[[TRAP:.*]], !dbg [[DBG43]], !prof [[PROF26]], !nosanitize [[META25]] // CHECK: [[TRAP]]: -// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 2) #[[ATTR3]], !dbg [[DBG41]], !nosanitize [[META25]] -// CHECK-NEXT: unreachable, !dbg [[DBG41]], !nosanitize [[META25]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 2) #[[ATTR3]], !dbg [[DBG45:![0-9]+]], !nosanitize [[META25]] +// CHECK-NEXT: unreachable, !dbg [[DBG45]], !nosanitize [[META25]] // CHECK: [[CONT]]: -// CHECK-NEXT: tail call void [[FN]](i32 noundef [[ARG1]], i32 noundef [[ARG2]]) #[[ATTR4]], !dbg [[DBG42:![0-9]+]] -// CHECK-NEXT: ret void, !dbg [[DBG43:![0-9]+]] +// CHECK-NEXT: tail call void [[FN]](i32 noundef [[ARG1]], i32 noundef [[ARG2]]) #[[ATTR4]], !dbg [[DBG44:![0-9]+]] +// CHECK-NEXT: ret void, !dbg [[DBG46:![0-9]+]] // void bar(void (*fn)(int, int), int arg1, int arg2) { fn(arg1, arg2); } // CHECK-LABEL: define dso_local void @baz( -// CHECK-SAME: ptr noundef [[FN:%.*]], i32 noundef [[ARG1:%.*]], i32 noundef [[ARG2:%.*]], i32 noundef [[ARG3:%.*]]) local_unnamed_addr #[[ATTR0]] !dbg [[DBG44:![0-9]+]] !type [[META55:![0-9]+]] !type [[META56:![0-9]+]] { +// CHECK-SAME: ptr noundef [[FN:%.*]], i32 noundef [[ARG1:%.*]], i32 noundef [[ARG2:%.*]], i32 noundef [[ARG3:%.*]]) local_unnamed_addr #[[ATTR0]] !dbg [[DBG47:![0-9]+]] !type [[META58:![0-9]+]] !type [[META59:![0-9]+]] { // CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: #dbg_value(ptr [[FN]], [[META51:![0-9]+]], !DIExpression(), [[META57:![0-9]+]]) -// CHECK-NEXT: #dbg_value(i32 [[ARG1]], [[META52:![0-9]+]], !DIExpression(), [[META57]]) -// CHECK-NEXT: #dbg_value(i32 [[ARG2]], [[META53:![0-9]+]], !DIExpression(), [[META57]]) -// CHECK-NEXT: #dbg_value(i32 [[ARG3]], [[META54:![0-9]+]], !DIExpression(), [[META57]]) -// CHECK-NEXT: [[TMP0:%.*]] = tail call i1 @llvm.type.test(ptr [[FN]], metadata !"_ZTSFvu3i32S_S_E.normalized"), !dbg [[DBG58:![0-9]+]], !nosanitize [[META25]] -// CHECK-NEXT: br i1 [[TMP0]], label %[[CONT:.*]], label %[[TRAP:.*]], !dbg [[DBG58]], !prof [[PROF26]], !nosanitize [[META25]] +// CHECK-NEXT: #dbg_value(ptr [[FN]], [[META54:![0-9]+]], !DIExpression(), [[META60:![0-9]+]]) +// CHECK-NEXT: #dbg_value(i32 [[ARG1]], [[META55:![0-9]+]], !DIExpression(), [[META60]]) +// CHECK-NEXT: #dbg_value(i32 [[ARG2]], [[META56:![0-9]+]], !DIExpression(), [[META60]]) +// CHECK-NEXT: #dbg_value(i32 [[ARG3]], [[META57:![0-9]+]], !DIExpression(), [[META60]]) +// CHECK-NEXT: [[TMP0:%.*]] = tail call i1 @llvm.type.test(ptr [[FN]], metadata !"_ZTSFvu3i32S_S_E.normalized"), !dbg [[DBG61:![0-9]+]], !nosanitize [[META25]] +// CHECK-NEXT: br i1 [[TMP0]], label %[[CONT:.*]], label %[[TRAP:.*]], !dbg [[DBG61]], !prof [[PROF26]], !nosanitize [[META25]] // CHECK: [[TRAP]]: -// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 2) #[[ATTR3]], !dbg [[DBG58]], !nosanitize [[META25]] -// CHECK-NEXT: unreachable, !dbg [[DBG58]], !nosanitize [[META25]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 2) #[[ATTR3]], !dbg [[DBG63:![0-9]+]], !nosanitize [[META25]] +// CHECK-NEXT: unreachable, !dbg [[DBG63]], !nosanitize [[META25]] // CHECK: [[CONT]]: -// CHECK-NEXT: tail call void [[FN]](i32 noundef [[ARG1]], i32 noundef [[ARG2]], i32 noundef [[ARG3]]) #[[ATTR4]], !dbg [[DBG59:![0-9]+]] -// CHECK-NEXT: ret void, !dbg [[DBG60:![0-9]+]] +// CHECK-NEXT: tail call void [[FN]](i32 noundef [[ARG1]], i32 noundef [[ARG2]], i32 noundef [[ARG3]]) #[[ATTR4]], !dbg [[DBG62:![0-9]+]] +// CHECK-NEXT: ret void, !dbg [[DBG64:![0-9]+]] // void baz(void (*fn)(int, int, int), int arg1, int arg2, int arg3) { fn(arg1, arg2, arg3); @@ -87,38 +87,42 @@ void baz(void (*fn)(int, int, int), int arg1, int arg2, int arg3) { // CHECK: [[DBG24]] = !DILocation(line: 25, column: 5, scope: [[DBG7]]) // CHECK: [[META25]] = !{} // CHECK: [[PROF26]] = !{!"branch_weights", i32 1048575, i32 1} -// CHECK: [[DBG27]] = !DILocation(line: 26, column: 1, scope: [[DBG7]]) -// CHECK: [[DBG28]] = distinct !DISubprogram(name: "bar", scope: [[META8]], file: [[META8]], line: 43, type: [[META29:![0-9]+]], scopeLine: 43, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: [[META0]], retainedNodes: [[META34:![0-9]+]]) -// CHECK: [[META29]] = !DISubroutineType(types: [[META30:![0-9]+]]) -// CHECK: [[META30]] = !{null, [[META31:![0-9]+]], [[META14]], [[META14]]} -// CHECK: [[META31]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: [[META32:![0-9]+]], size: 64) -// CHECK: [[META32]] = !DISubroutineType(types: [[META33:![0-9]+]]) -// CHECK: [[META33]] = !{null, [[META14]], [[META14]]} -// CHECK: [[META34]] = !{[[META35]], [[META36]], [[META37]]} -// CHECK: [[META35]] = !DILocalVariable(name: "fn", arg: 1, scope: [[DBG28]], file: [[META8]], line: 43, type: [[META31]]) -// CHECK: [[META36]] = !DILocalVariable(name: "arg1", arg: 2, scope: [[DBG28]], file: [[META8]], line: 43, type: [[META14]]) -// CHECK: [[META37]] = !DILocalVariable(name: "arg2", arg: 3, scope: [[DBG28]], file: [[META8]], line: 43, type: [[META14]]) -// CHECK: [[META38]] = !{i64 0, !"_ZTSFvPFvu3i32S_ES_S_E.normalized"} -// CHECK: [[META39]] = !{i64 0, !"_ZTSFvPvu3i32S0_E.normalized.generalized"} -// CHECK: [[META40]] = !DILocation(line: 0, scope: [[DBG28]]) -// CHECK: [[DBG41]] = !DILocation(line: 0, scope: [[META22]], inlinedAt: [[DBG42]]) -// CHECK: [[DBG42]] = !DILocation(line: 44, column: 5, scope: [[DBG28]]) -// CHECK: [[DBG43]] = !DILocation(line: 45, column: 1, scope: [[DBG28]]) -// CHECK: [[DBG44]] = distinct !DISubprogram(name: "baz", scope: [[META8]], file: [[META8]], line: 63, type: [[META45:![0-9]+]], scopeLine: 63, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: [[META0]], retainedNodes: [[META50:![0-9]+]]) -// CHECK: [[META45]] = !DISubroutineType(types: [[META46:![0-9]+]]) -// CHECK: [[META46]] = !{null, [[META47:![0-9]+]], [[META14]], [[META14]], [[META14]]} -// CHECK: [[META47]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: [[META48:![0-9]+]], size: 64) +// CHECK: [[DBGTRAP]] = !DILocation(line: 0, scope: [[TRAPMSG:![0-9]+]], inlinedAt: [[DBG21]]) +// CHECK: [[TRAPMSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Control flow integrity check failed", scope: [[META8]], file: [[META8]], type: [[META23]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: [[META0]]) +// CHECK: [[DBG29]] = !DILocation(line: 26, column: 1, scope: [[DBG7]]) +// CHECK: [[DBG30]] = distinct !DISubprogram(name: "bar", scope: [[META8]], file: [[META8]], line: 43, type: [[META31:![0-9]+]], scopeLine: 43, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: [[META0]], retainedNodes: [[META36:![0-9]+]]) +// CHECK: [[META31]] = !DISubroutineType(types: [[META32:![0-9]+]]) +// CHECK: [[META32]] = !{null, [[META33:![0-9]+]], [[META14]], [[META14]]} +// CHECK: [[META33]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: [[META34:![0-9]+]], size: 64) +// CHECK: [[META34]] = !DISubroutineType(types: [[META35:![0-9]+]]) +// CHECK: [[META35]] = !{null, [[META14]], [[META14]]} +// CHECK: [[META36]] = !{[[META37]], [[META38]], [[META39]]} +// CHECK: [[META37]] = !DILocalVariable(name: "fn", arg: 1, scope: [[DBG30]], file: [[META8]], line: 43, type: [[META33]]) +// CHECK: [[META38]] = !DILocalVariable(name: "arg1", arg: 2, scope: [[DBG30]], file: [[META8]], line: 43, type: [[META14]]) +// CHECK: [[META39]] = !DILocalVariable(name: "arg2", arg: 3, scope: [[DBG30]], file: [[META8]], line: 43, type: [[META14]]) +// CHECK: [[META40]] = !{i64 0, !"_ZTSFvPFvu3i32S_ES_S_E.normalized"} +// CHECK: [[META41]] = !{i64 0, !"_ZTSFvPvu3i32S0_E.normalized.generalized"} +// CHECK: [[META42]] = !DILocation(line: 0, scope: [[DBG30]]) +// CHECK: [[DBG43]] = !DILocation(line: 0, scope: [[META22]], inlinedAt: [[DBG44]]) +// CHECK: [[DBG44]] = !DILocation(line: 44, column: 5, scope: [[DBG30]]) +// CHECK: [[DBG45]] = !DILocation(line: 0, scope: [[TRAPMSG]], inlinedAt: [[DBG43]]) +// CHECK: [[DBG46]] = !DILocation(line: 45, column: 1, scope: [[DBG30]]) +// CHECK: [[DBG47]] = distinct !DISubprogram(name: "baz", scope: [[META8]], file: [[META8]], line: 63, type: [[META48:![0-9]+]], scopeLine: 63, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: [[META0]], retainedNodes: [[META53:![0-9]+]]) // CHECK: [[META48]] = !DISubroutineType(types: [[META49:![0-9]+]]) -// CHECK: [[META49]] = !{null, [[META14]], [[META14]], [[META14]]} -// CHECK: [[META50]] = !{[[META51]], [[META52]], [[META53]], [[META54]]} -// CHECK: [[META51]] = !DILocalVariable(name: "fn", arg: 1, scope: [[DBG44]], file: [[META8]], line: 63, type: [[META47]]) -// CHECK: [[META52]] = !DILocalVariable(name: "arg1", arg: 2, scope: [[DBG44]], file: [[META8]], line: 63, type: [[META14]]) -// CHECK: [[META53]] = !DILocalVariable(name: "arg2", arg: 3, scope: [[DBG44]], file: [[META8]], line: 63, type: [[META14]]) -// CHECK: [[META54]] = !DILocalVariable(name: "arg3", arg: 4, scope: [[DBG44]], file: [[META8]], line: 63, type: [[META14]]) -// CHECK: [[META55]] = !{i64 0, !"_ZTSFvPFvu3i32S_S_ES_S_S_E.normalized"} -// CHECK: [[META56]] = !{i64 0, !"_ZTSFvPvu3i32S0_S0_E.normalized.generalized"} -// CHECK: [[META57]] = !DILocation(line: 0, scope: [[DBG44]]) -// CHECK: [[DBG58]] = !DILocation(line: 0, scope: [[META22]], inlinedAt: [[DBG59]]) -// CHECK: [[DBG59]] = !DILocation(line: 64, column: 5, scope: [[DBG44]]) -// CHECK: [[DBG60]] = !DILocation(line: 65, column: 1, scope: [[DBG44]]) +// CHECK: [[META49]] = !{null, [[META50:![0-9]+]], [[META14]], [[META14]], [[META14]]} +// CHECK: [[META50]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: [[META51:![0-9]+]], size: 64) +// CHECK: [[META51]] = !DISubroutineType(types: [[META52:![0-9]+]]) +// CHECK: [[META52]] = !{null, [[META14]], [[META14]], [[META14]]} +// CHECK: [[META53]] = !{[[META54]], [[META55]], [[META56]], [[META57]]} +// CHECK: [[META54]] = !DILocalVariable(name: "fn", arg: 1, scope: [[DBG47]], file: [[META8]], line: 63, type: [[META50]]) +// CHECK: [[META55]] = !DILocalVariable(name: "arg1", arg: 2, scope: [[DBG47]], file: [[META8]], line: 63, type: [[META14]]) +// CHECK: [[META56]] = !DILocalVariable(name: "arg2", arg: 3, scope: [[DBG47]], file: [[META8]], line: 63, type: [[META14]]) +// CHECK: [[META57]] = !DILocalVariable(name: "arg3", arg: 4, scope: [[DBG47]], file: [[META8]], line: 63, type: [[META14]]) +// CHECK: [[META58]] = !{i64 0, !"_ZTSFvPFvu3i32S_S_ES_S_S_E.normalized"} +// CHECK: [[META59]] = !{i64 0, !"_ZTSFvPvu3i32S0_S0_E.normalized.generalized"} +// CHECK: [[META60]] = !DILocation(line: 0, scope: [[DBG47]]) +// CHECK: [[DBG61]] = !DILocation(line: 0, scope: [[META22]], inlinedAt: [[DBG62]]) +// CHECK: [[DBG62]] = !DILocation(line: 64, column: 5, scope: [[DBG47]]) +// CHECK: [[DBG63]] = !DILocation(line: 0, scope: [[TRAPMSG]], inlinedAt: [[DBG61]]) +// CHECK: [[DBG64]] = !DILocation(line: 65, column: 1, scope: [[DBG47]]) //. diff --git a/clang/test/CodeGen/ubsan-trap-debugloc.c b/clang/test/CodeGen/ubsan-trap-debugloc.c index 87cbfadec7d30..2f5258a6f4ce2 100644 --- a/clang/test/CodeGen/ubsan-trap-debugloc.c +++ b/clang/test/CodeGen/ubsan-trap-debugloc.c @@ -20,5 +20,8 @@ void bar(volatile int a) __attribute__((optnone)) { // CHECK: [[LOC]] = !DILocation(line: 0 // With optimisations disabled the traps are not merged and retain accurate debug locations -// CHECK: [[LOC2]] = !DILocation(line: 15, column: 9 -// CHECK: [[LOC3]] = !DILocation(line: 16, column: 9 + // CHECK-DAG: [[SRC2:![0-9]+]] = !DILocation(line: 15, column: 9, + // CHECK-DAG: [[SRC3:![0-9]+]] = !DILocation(line: 16, column: 9, + // CHECK-DAG: [[LOC2]] = !DILocation(line: 0, scope: [[SCOPE2:![0-9]+]], inlinedAt: [[SRC2]]) + // CHECK-DAG: [[LOC3]] = !DILocation(line: 0, scope: [[SCOPE3:![0-9]+]], inlinedAt: [[SRC3]]) + diff --git a/clang/test/CodeGen/ubsan-trap-reason-add-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-add-overflow.c new file mode 100644 index 0000000000000..862d434d291bc --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-add-overflow.c @@ -0,0 +1,32 @@ +// 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: -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,DETAILED %s + +// 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]+]] + +// DETAILED: [[SLOC]] = !DILocation(line: 0, scope: [[SMSG:![0-9]+]], {{.+}}) +// DETAILED: [[SMSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$signed integer addition overflow in 'a + b'" +// DETAILED: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// DETAILED: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$unsigned integer addition overflow in 'c + d'" + +// In "Basic" mode the trap reason is shared by both functions. +// BASIC: [[SLOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// BASIC: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer addition overflowed" +// BASIC: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) + + diff --git a/clang/test/CodeGen/ubsan-trap-reason-alignment-assumption.c b/clang/test/CodeGen/ubsan-trap-reason-alignment-assumption.c new file mode 100644 index 0000000000000..3247ceb4d4a74 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-alignment-assumption.c @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=alignment -fsanitize-trap=alignment -emit-llvm %s -o - | FileCheck %s + +#include +int32_t *get_int(void) __attribute__((assume_aligned(16))); + +void retrieve_int(void) { + int *i = get_int(); + *i = 7; +} + +// CHECK-LABEL: @retrieve_int +// CHECK: call void @llvm.ubsantrap(i8 23) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Alignment assumption violated" diff --git a/clang/test/CodeGen/ubsan-trap-reason-builtin-unreachable.c b/clang/test/CodeGen/ubsan-trap-reason-builtin-unreachable.c new file mode 100644 index 0000000000000..97bd6905bc327 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-builtin-unreachable.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=unreachable -fsanitize-trap=unreachable -emit-llvm %s -o - | FileCheck %s + +int call_builtin_unreachable(void) { __builtin_unreachable(); } + +// CHECK-LABEL: @call_builtin_unreachable +// CHECK: call void @llvm.ubsantrap(i8 1) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$_builtin_unreachable(), execution reached an unreachable program point" diff --git a/clang/test/CodeGen/ubsan-trap-reason-cfi-check-fail.c b/clang/test/CodeGen/ubsan-trap-reason-cfi-check-fail.c new file mode 100644 index 0000000000000..9304f51046c0d --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-cfi-check-fail.c @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=cfi-icall -fsanitize-trap=cfi-icall -emit-llvm %s -o - | FileCheck %s + +typedef int (*fp_t)(int); + +int good(int x) { return x + 1; } + +int bad(void) { return 0; } + +int cfi_trigger(int a) { + fp_t p = good; + int r1 = p(a); + + p = (fp_t)(void *)bad; + int r2 = p(a); + + return r1 + r2; +} + +// CHECK-LABEL: @good +// CHECK-LABEL: @bad +// CHECK-LABEL: @cfi_trigger +// CHECK: call void @llvm.ubsantrap(i8 2) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Control flow integrity check failed" diff --git a/clang/test/CodeGen/ubsan-trap-reason-crash.cpp b/clang/test/CodeGen/ubsan-trap-reason-crash.cpp new file mode 100644 index 0000000000000..6add9bf2b6b34 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-crash.cpp @@ -0,0 +1,20 @@ +// FIXME: We should emit a trap message for this case too. +// But sometimes Clang will emit a ubsan trap into the prologue of a function, +// at which point the debug-info locations haven't been set up yet and +// can't hook up our artificial inline frame. [Issue #150707] + +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=null -fsanitize-trap=null -emit-llvm %s -o - | FileCheck %s + +struct Foo { + void target() {} +} f; + +void caller() { + f.target(); +} + + +// CHECK-LABEL: @_Z6callerv +// CHECK: call void @llvm.ubsantrap(i8 22){{.*}}!nosanitize +// CHECK-NOT: __clang_trap_msg diff --git a/clang/test/CodeGen/ubsan-trap-reason-div-rem-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-div-rem-overflow.c new file mode 100644 index 0000000000000..d0b21dd173894 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-div-rem-overflow.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow -emit-llvm %s -o - | FileCheck %s + +int div_rem_overflow(int a, int b) { return a / b; } + +// CHECK-LABEL: @div_rem_overflow +// CHECK: call void @llvm.ubsantrap(i8 3) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer divide or remainder overflowed" diff --git a/clang/test/CodeGen/ubsan-trap-reason-dynamic-type-cache-miss.cpp b/clang/test/CodeGen/ubsan-trap-reason-dynamic-type-cache-miss.cpp new file mode 100644 index 0000000000000..f89fbdcf1002f --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-dynamic-type-cache-miss.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=vptr -fsanitize-trap=vptr -emit-llvm %s -o - | FileCheck %s + +struct A { + virtual void foo(); +}; +struct B { + virtual void bar(); +}; + +void A::foo() {} +void B::bar() {} + +int dynamic_type_cache_miss() { + B b; + A &a = reinterpret_cast(b); + a.foo(); + return 0; +} + +// CHECK-LABEL: @_ZN1A3fooEv +// CHECK-LABEL: @_ZN1B3barEv +// CHECK-LABEL: @_Z23dynamic_type_cache_missv +// CHECK: call void @llvm.ubsantrap(i8 4) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Dynamic type cache miss, member call made on an object whose dynamic type differs from the expected type" diff --git a/clang/test/CodeGen/ubsan-trap-reason-flag.c b/clang/test/CodeGen/ubsan-trap-reason-flag.c new file mode 100644 index 0000000000000..2968e6bd2ade4 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-flag.c @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow -emit-llvm %s -o - \ +// RUN: | FileCheck %s --check-prefix=ANNOTATE + +//============================================================================== +// 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=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]+]], {{.+}}) +// 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]+]] +// NO-ANNOTATE-NOT: __clang_trap_msg diff --git a/clang/test/CodeGen/ubsan-trap-reason-float-cast-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-float-cast-overflow.c new file mode 100644 index 0000000000000..079a191e05d4b --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-float-cast-overflow.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=float-cast-overflow -fsanitize-trap=float-cast-overflow -emit-llvm %s -o - | FileCheck %s + +int float_cast_overflow(float x) { return (int)x; } + +// CHECK-LABEL: @float_cast_overflow +// CHECK: call void @llvm.ubsantrap(i8 5) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Floating-point to integer conversion overflowed" diff --git a/clang/test/CodeGen/ubsan-trap-reason-function-type-mismatch.c b/clang/test/CodeGen/ubsan-trap-reason-function-type-mismatch.c new file mode 100644 index 0000000000000..1727f9c092a4c --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-function-type-mismatch.c @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=function -fsanitize-trap=function -emit-llvm %s -o - | FileCheck %s + +void target(void) {} + +int function_type_mismatch(void) { + int (*fp_int)(int); + + fp_int = (int (*)(int))(void *)target; + + return fp_int(42); +} + +// CHECK-LABEL: @target +// CHECK-LABEL: @function_type_mismatch +// CHECK: call void @llvm.ubsantrap(i8 6) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Function called with mismatched signature" diff --git a/clang/test/CodeGen/ubsan-trap-reason-implicit-conversion.c b/clang/test/CodeGen/ubsan-trap-reason-implicit-conversion.c new file mode 100644 index 0000000000000..43c091d51a5c2 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-implicit-conversion.c @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=implicit-unsigned-integer-truncation -fsanitize-trap=implicit-unsigned-integer-truncation -emit-llvm %s -o - | FileCheck %s + +unsigned long long big; + +unsigned implicit_conversion(void) { return big; } + +// CHECK-LABEL: @implicit_conversion +// CHECK: call void @llvm.ubsantrap(i8 7) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Implicit integer conversion overflowed or lost data" diff --git a/clang/test/CodeGen/ubsan-trap-reason-invalid-builtin.c b/clang/test/CodeGen/ubsan-trap-reason-invalid-builtin.c new file mode 100644 index 0000000000000..56cf6744277df --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-invalid-builtin.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=builtin -fsanitize-trap=builtin -emit-llvm %s -o - | FileCheck %s + +unsigned invalid_builtin(unsigned x) { return __builtin_clz(x); } + +// CHECK-LABEL: @invalid_builtin +// CHECK: call void @llvm.ubsantrap(i8 8) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Invalid use of builtin function" diff --git a/clang/test/CodeGen/ubsan-trap-reason-invalid-objc-cast.m b/clang/test/CodeGen/ubsan-trap-reason-invalid-objc-cast.m new file mode 100644 index 0000000000000..ed2d5ffe1600c --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-invalid-objc-cast.m @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=objc-cast -fsanitize-trap=objc-cast -emit-llvm %s -o - | FileCheck %s + +@interface NSFastEnumerationState +@end + +#define NSUInteger unsigned int + +@interface NSArray ++(NSArray*) arrayWithObjects: (id) first, ...; +- (NSUInteger) countByEnumeratingWithState:(NSFastEnumerationState *) state + objects:(id[]) buffer + count:(NSUInteger) len; +-(unsigned) count; +@end +@interface NSString +-(const char*) cString; +@end + +void receive_NSString(NSString*); + +void t0(void) { + NSArray *array = [NSArray arrayWithObjects: @"0", @"1", (void*)0]; + for (NSString *i in array) { + receive_NSString(i); + } +} + +// CHECK-LABEL: @t0 +// CHECK: call void @llvm.ubsantrap(i8 9) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Invalid Objective-C cast" diff --git a/clang/test/CodeGen/ubsan-trap-reason-load-invalid-value.c b/clang/test/CodeGen/ubsan-trap-reason-load-invalid-value.c new file mode 100644 index 0000000000000..4aad8325c5119 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-load-invalid-value.c @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=bool -fsanitize-trap=bool -emit-llvm %s -o - | FileCheck %s + +#include + +unsigned char bad_byte; + +bool load_invalid_value(void) { return *((bool *)&bad_byte); } + +// CHECK-LABEL: @load_invalid_value +// CHECK: call void @llvm.ubsantrap(i8 10) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Loaded an invalid or uninitialized value for the type" diff --git a/clang/test/CodeGen/ubsan-trap-reason-missing-return.cpp b/clang/test/CodeGen/ubsan-trap-reason-missing-return.cpp new file mode 100644 index 0000000000000..2818d9d202720 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-missing-return.cpp @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=return -fsanitize-trap=return -emit-llvm %s -o - | FileCheck %s + +int missing_return(int x) { + if (x > 0) + return x; +} + +// CHECK-LABEL: @_Z14missing_return +// CHECK: call void @llvm.ubsantrap(i8 11) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Execution reached the end of a value-returning function without returning a value" diff --git a/clang/test/CodeGen/ubsan-trap-reason-mul-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-mul-overflow.c new file mode 100644 index 0000000000000..ba3928d0c2e63 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-mul-overflow.c @@ -0,0 +1,30 @@ +// 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: -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,DETAILED %s + +// 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]+]] + +// DETAILED: [[SLOC]] = !DILocation(line: 0, scope: [[SMSG:![0-9]+]], {{.+}}) +// DETAILED: [[SMSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$signed integer multiplication overflow in 'a * b'" +// DETAILED: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// DETAILED: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$unsigned integer multiplication overflow in 'c * d'" + +// In "Basic" mode the trap reason is shared by both functions. +// BASIC: [[SLOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// BASIC: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer multiplication overflowed" +// BASIC: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) diff --git a/clang/test/CodeGen/ubsan-trap-reason-negate-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-negate-overflow.c new file mode 100644 index 0000000000000..55346794b2928 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-negate-overflow.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow -emit-llvm %s -o - | FileCheck %s + +int negate_overflow() { + int x; + return -x; +} + +// CHECK-LABEL: @negate_overflow +// CHECK: call void @llvm.ubsantrap(i8 13) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Integer negation overflowed" diff --git a/clang/test/CodeGen/ubsan-trap-reason-nonnull-arg.c b/clang/test/CodeGen/ubsan-trap-reason-nonnull-arg.c new file mode 100644 index 0000000000000..1f0f450d86180 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-nonnull-arg.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=nonnull-attribute -fsanitize-trap=nonnull-attribute -emit-llvm %s -o - | FileCheck %s + +__attribute__((nonnull)) void nonnull_arg(int *p) { (void)p; } + +void trigger_nonnull_arg() { nonnull_arg(0); } + +// CHECK-LABEL: @nonnull_arg +// CHECK-LABEL: @trigger_nonnull_arg +// CHECK: call void @llvm.ubsantrap(i8 16) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Passing null pointer as an argument which is declared to never be null" diff --git a/clang/test/CodeGen/ubsan-trap-reason-nonnull-return.c b/clang/test/CodeGen/ubsan-trap-reason-nonnull-return.c new file mode 100644 index 0000000000000..1197b4dbafea5 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-nonnull-return.c @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=returns-nonnull-attribute -fsanitize-trap=returns-nonnull-attribute -emit-llvm %s -o - | FileCheck %s + +__attribute__((returns_nonnull)) int *must_return_nonnull(int bad) { + if (bad) + return 0; + static int x = 1; + return &x; +} + +// CHECK-LABEL: @must_return_nonnull +// CHECK: call void @llvm.ubsantrap(i8 17) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Returning null pointer from a function which is declared to never return null" diff --git a/clang/test/CodeGen/ubsan-trap-reason-nullability-arg.c b/clang/test/CodeGen/ubsan-trap-reason-nullability-arg.c new file mode 100644 index 0000000000000..2bc71dec67c93 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-nullability-arg.c @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=nullability-arg -fsanitize-trap=nullability-arg -emit-llvm %s -o - | FileCheck %s + +#include + +int nullability_arg(int *_Nonnull p) { return *p; } + +int trigger_nullability_arg(void) { return nullability_arg(NULL); } + +// CHECK-LABEL: @nullability_arg +// CHECK-LABEL: @trigger_nullability_arg +// CHECK: call void @llvm.ubsantrap(i8 14) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Passing null as an argument which is annotated with _Nonnull" diff --git a/clang/test/CodeGen/ubsan-trap-reason-nullability-return.c b/clang/test/CodeGen/ubsan-trap-reason-nullability-return.c new file mode 100644 index 0000000000000..3d64c5a71a9e7 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-nullability-return.c @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=nullability-return -fsanitize-trap=nullability-return -emit-llvm %s -o - | FileCheck %s + +#include +#include + +int *_Nonnull nullability_return(bool fail) { + if (fail) + return NULL; + + static int x = 0; + return &x; +} + +// CHECK-LABEL: @nullability_return +// CHECK: call void @llvm.ubsantrap(i8 15) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Returning null from a function with a return type annotated with _Nonnull" diff --git a/clang/test/CodeGen/ubsan-trap-reason-out-of-bounds.c b/clang/test/CodeGen/ubsan-trap-reason-out-of-bounds.c new file mode 100644 index 0000000000000..979886da6d25b --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-out-of-bounds.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=array-bounds -fsanitize-trap=array-bounds -emit-llvm %s -o - | FileCheck %s + +int out_of_bounds() { + int a[1] = {0}; + return a[1]; +} + +// CHECK-LABEL: @out_of_bounds +// CHECK: call void @llvm.ubsantrap(i8 18) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Array index out of bounds" diff --git a/clang/test/CodeGen/ubsan-trap-reason-pointer-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-pointer-overflow.c new file mode 100644 index 0000000000000..41cb4873a423c --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-pointer-overflow.c @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - | FileCheck %s + +#include +#include + +int *pointer_overflow(void) { + int buf[4]; + volatile size_t n = (SIZE_MAX / sizeof(int)) - 1; + return buf + n; +} + +// CHECK-LABEL: @pointer_overflow +// CHECK: call void @llvm.ubsantrap(i8 19) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Pointer arithmetic overflowed bounds" diff --git a/clang/test/CodeGen/ubsan-trap-reason-shift-out-of-bounds.c b/clang/test/CodeGen/ubsan-trap-reason-shift-out-of-bounds.c new file mode 100644 index 0000000000000..1a7465d93aef5 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-shift-out-of-bounds.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=shift-base -fsanitize-trap=shift-base -emit-llvm %s -o - | FileCheck %s + +int shift_out_of_bounds(void) { + int sh = 32; + return 1 << sh; +} + +// CHECK-LABEL: @shift_out_of_bounds +// CHECK: call void @llvm.ubsantrap(i8 20) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Shift exponent is too large for the type" diff --git a/clang/test/CodeGen/ubsan-trap-reason-sub-overflow.c b/clang/test/CodeGen/ubsan-trap-reason-sub-overflow.c new file mode 100644 index 0000000000000..596d777fa4360 --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-sub-overflow.c @@ -0,0 +1,30 @@ +// 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: -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,DETAILED %s + +// 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]+]] + +// 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/CodeGen/ubsan-trap-reason-type-mismatch.c b/clang/test/CodeGen/ubsan-trap-reason-type-mismatch.c new file mode 100644 index 0000000000000..802ec91b53a0d --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-type-mismatch.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=alignment -fsanitize-trap=alignment -emit-llvm %s -o - | FileCheck %s + +int type_mismatch(int *p) { return *p; } + +// CHECK-LABEL: @type_mismatch +// CHECK: call void @llvm.ubsantrap(i8 22) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Type mismatch in operation" diff --git a/clang/test/CodeGen/ubsan-trap-reason-vla-bound-not-positive.c b/clang/test/CodeGen/ubsan-trap-reason-vla-bound-not-positive.c new file mode 100644 index 0000000000000..ad9c408b5e14d --- /dev/null +++ b/clang/test/CodeGen/ubsan-trap-reason-vla-bound-not-positive.c @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -O0 -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: -fsanitize=vla-bound -fsanitize-trap=vla-bound -emit-llvm %s -o - | FileCheck %s + +int n = 0; + +int vla_bound_not_positive(void) { + int a[n]; + return sizeof a; +} + +// CHECK-LABEL: @vla_bound_not_positive +// CHECK: call void @llvm.ubsantrap(i8 24) {{.*}}!dbg [[LOC:![0-9]+]] +// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[MSG:![0-9]+]], {{.+}}) +// CHECK: [[MSG]] = distinct !DISubprogram(name: "__clang_trap_msg$Undefined Behavior Sanitizer$Variable length array bound evaluates to non-positive value" 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/Inputs/ubsan_add_overflow.c b/lldb/test/Shell/Recognizer/Inputs/ubsan_add_overflow.c new file mode 100644 index 0000000000000..9f12c321bf409 --- /dev/null +++ b/lldb/test/Shell/Recognizer/Inputs/ubsan_add_overflow.c @@ -0,0 +1,3 @@ +#include + +int main() { return INT_MAX + 1; } diff --git a/lldb/test/Shell/Recognizer/ubsan_add_overflow.test b/lldb/test/Shell/Recognizer/ubsan_add_overflow.test new file mode 100644 index 0000000000000..872b5a7a4d585 --- /dev/null +++ b/lldb/test/Shell/Recognizer/ubsan_add_overflow.test @@ -0,0 +1,22 @@ +# UNSUPPORTED: system-windows + +# RUN: %clang_host -g -O0 %S/Inputs/ubsan_add_overflow.c -o %t.out \ +# RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow + +# RUN: %lldb -b -s %s %t.out | FileCheck %s + +run +# 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$signed integer addition overflow in '2147483647 + 1'{{.*}} +# CHECK: frame #1: {{.*}}`main at ubsan_add_overflow.c + +frame info +# CHECK: frame #{{.*}}`main at ubsan_add_overflow.c + +frame recognizer info 0 +# CHECK: frame 0 is recognized by Verbose Trap StackFrame Recognizer + +quit