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

Reply via email to