https://github.com/Michael137 updated https://github.com/llvm/llvm-project/pull/174750
>From 6a8885294c8d22b990654b8ae2255e6c40b96b97 Mon Sep 17 00:00:00 2001 From: Michael Buch <[email protected]> Date: Tue, 6 Jan 2026 16:38:54 +0000 Subject: [PATCH 1/4] [lldb][Format] Reject recursive format entities Depends on: * https://github.com/llvm/llvm-project/pull/174618 If a format entity calls back into `Format` and passes it a format entity type that we're already in the process of parsing, we are likely going to run into infinite recursion and blow the stack. An example of this can be seen in the test-case adjusted by this patch. These seems to be causing actual crashes in the field, so this patch adds basic tracking to `Formatter::Format` that checks whether we're recursively parsing the same entity. This may very well be intended by some entities (e.g., `Root` and `Scope`), so there is an escape hatch for those. Adding a unit-test for this is unfortunately tricky because the only format entity that currently suffers from this is `${function.name-with-args}`, which requires a language plugin and valid target. If we really wanted to we could probably mock all of those, but the shell test provides test coverage for the previously crashing case. rdar://166890120 --- lldb/include/lldb/Core/FormatEntity.h | 20 +++++++++++++++++++ lldb/source/Core/FormatEntity.cpp | 17 ++++++++++++++++ .../Settings/TestCxxFrameFormatRecursive.test | 4 ---- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/lldb/include/lldb/Core/FormatEntity.h b/lldb/include/lldb/Core/FormatEntity.h index 1a4b3db30ad22..e01009a44aac7 100644 --- a/lldb/include/lldb/Core/FormatEntity.h +++ b/lldb/include/lldb/Core/FormatEntity.h @@ -11,6 +11,7 @@ #include "lldb/lldb-enumerations.h" #include "lldb/lldb-types.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallVector.h" #include <algorithm> #include <cstddef> @@ -248,11 +249,30 @@ class Formatter { bool FormatFunctionNameForLanguage(Stream &s); + /// Returns \c true if \a Format has been called for an \a Entry + /// with the specified \c type recusrively. Some types are permitted + /// to be formatted recursively, in which case this function returns + /// \c false. + bool IsInvalidRecursiveFormat(Entry::Type type); + + /// While the returned \a llvm::scope_exit is alive, the specified \c type + /// is tracked by this \c Formatter object for recursion. Once the returned + /// scope guard is destructed, the entry stops being tracked. + auto PushEntryType(Entry::Type type) { + m_entry_type_stack.push_back(type); + return llvm::scope_exit([this] { + assert(!m_entry_type_stack.empty()); + m_entry_type_stack.pop_back(); + }); + } + const SymbolContext *const m_sc = nullptr; const ExecutionContext *const m_exe_ctx = nullptr; const Address *const m_addr = nullptr; const bool m_function_changed = false; const bool m_initial_function = false; + + llvm::SmallVector<Entry::Type, 1> m_entry_type_stack; }; Status Parse(const llvm::StringRef &format, Entry &entry); diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index 6afff104d3cc9..244f901ad11c2 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -1312,6 +1312,15 @@ bool FormatEntity::Formatter::FormatStringRef(const llvm::StringRef &format_str, bool FormatEntity::Formatter::Format(const Entry &entry, Stream &s, ValueObject *valobj) { + if (IsInvalidRecursiveFormat(entry.type)) { + LLDB_LOG(GetLog(LLDBLog::DataFormatters), + "Error: detected recursive format entity: {0}", + FormatEntity::Entry::TypeToCString(entry.type)); + return false; + } + + auto entry_stack_guard = PushEntryType(entry.type); + switch (entry.type) { case Entry::Type::Invalid: case Entry::Type::ParentNumber: // Only used for @@ -2704,3 +2713,11 @@ Status FormatEntity::Parse(const llvm::StringRef &format_str, Entry &entry) { llvm::StringRef modifiable_format(format_str); return ParseInternal(modifiable_format, entry, 0); } + +bool FormatEntity::Formatter::IsInvalidRecursiveFormat(Entry::Type type) { + // It is expected that Scope and Root format entities recursively call Format. + if (llvm::is_contained({Entry::Type::Scope, Entry::Type::Root}, type)) + return false; + + return llvm::is_contained(m_entry_type_stack, type); +} diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test b/lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test index 465b1bb28e327..effc47ed228a2 100644 --- a/lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test +++ b/lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test @@ -1,7 +1,3 @@ -# Flaky on Linux, see https://github.com/llvm/llvm-project/issues/142726 -# UNSUPPORTED: system-linux -# XFAIL: * - # Test disallowed variables inside the # plugin.cplusplus.display.function-name-format setting. >From fe60fe1f984834c2f821236b09a23a0220cb6716 Mon Sep 17 00:00:00 2001 From: Michael Buch <[email protected]> Date: Wed, 7 Jan 2026 15:39:37 +0000 Subject: [PATCH 2/4] fixup! permit recursive Variable entries --- lldb/source/Core/FormatEntity.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index 244f901ad11c2..a7882a633ae76 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -2716,7 +2716,13 @@ Status FormatEntity::Parse(const llvm::StringRef &format_str, Entry &entry) { bool FormatEntity::Formatter::IsInvalidRecursiveFormat(Entry::Type type) { // It is expected that Scope and Root format entities recursively call Format. - if (llvm::is_contained({Entry::Type::Scope, Entry::Type::Root}, type)) + // + // Variable may also be formatted recursively in some special cases. The main + // use-case being array summary strings, in which case Format will call itself + // with the subrange ValueObject and apply a freshly created Variable entry. + // E.g., ${var[1-3]} will format the [1-3] range with ${var%S}. + if (llvm::is_contained( + {Entry::Type::Scope, Entry::Type::Root, Entry::Type::Variable}, type)) return false; return llvm::is_contained(m_entry_type_stack, type); >From b0e4fa8b07573d81fdf1aaa13120d027c578f239 Mon Sep 17 00:00:00 2001 From: Michael Buch <[email protected]> Date: Wed, 7 Jan 2026 16:07:51 +0000 Subject: [PATCH 3/4] fixup! adjust CHECK --- lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test b/lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test index effc47ed228a2..017f8c227fb95 100644 --- a/lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test +++ b/lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test @@ -19,5 +19,4 @@ run bt # CHECK: bt -# CHECK-NOT: custom-frame -# CHECK: main +# CHECK: custom-frame 'main >From 59a18f102f605465a3978f058b636a9b6718acce Mon Sep 17 00:00:00 2001 From: Michael Buch <[email protected]> Date: Thu, 8 Jan 2026 16:06:49 +0000 Subject: [PATCH 4/4] fixup! constexpr array for permitted entries --- lldb/source/Core/FormatEntity.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index a7882a633ae76..3646f88ac9206 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -2721,8 +2721,10 @@ bool FormatEntity::Formatter::IsInvalidRecursiveFormat(Entry::Type type) { // use-case being array summary strings, in which case Format will call itself // with the subrange ValueObject and apply a freshly created Variable entry. // E.g., ${var[1-3]} will format the [1-3] range with ${var%S}. - if (llvm::is_contained( - {Entry::Type::Scope, Entry::Type::Root, Entry::Type::Variable}, type)) + static constexpr std::array s_permitted_recursive_entities = { + Entry::Type::Scope, Entry::Type::Root, Entry::Type::Variable}; + + if (llvm::is_contained(s_permitted_recursive_entities, type)) return false; return llvm::is_contained(m_entry_type_stack, type); _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
