From: jerrywang1201 <[email protected]>
PR demangler/123988 shows that __cxa_demangle can fail for long but
valid names. The callback-based demangler still needs the recursion-
limit guard because it puts its temporary component arrays on the
stack, but d_demangle prints into an allocated string and does not need
to fail for that reason.
Parse d_demangle input using heap-backed comps/subs arrays instead of
reusing the stack-sensitive callback path. Keep the callback-only path
unchanged, and add a libstdc++ regression test using the long
Type<std::type_identity<...>>::bar() name from PR123988.
---
libiberty/cp-demangle.c | 144 ++++++++++++++----
.../abi/demangle/regression/123988.cc | 55 +++++++
2 files changed, 167 insertions(+), 32 deletions(-)
create mode 100644 libstdc++-v3/testsuite/abi/demangle/regression/123988.cc
diff --git a/libiberty/cp-demangle.c b/libiberty/cp-demangle.c
index 5e6571d419b..13484e03d56 100644
--- a/libiberty/cp-demangle.c
+++ b/libiberty/cp-demangle.c
@@ -6839,18 +6839,20 @@ cplus_demangle_init_info (const char *mangled, int
options, size_t len,
name. OPTIONS is the usual libiberty demangler options. On success,
this returns 1. On failure, returns 0. */
+enum d_demangle_top_level_type
+ {
+ DCT_INVALID,
+ DCT_MANGLED,
+ DCT_GLOBAL_CTORS,
+ DCT_GLOBAL_DTORS,
+ DCT_TYPE
+ };
+
static int
d_demangle_callback (const char *mangled, int options,
- demangle_callbackref callback, void *opaque)
+ demangle_callbackref callback, void *opaque)
{
- enum
- {
- DCT_TYPE,
- DCT_MANGLED,
- DCT_GLOBAL_CTORS,
- DCT_GLOBAL_DTORS
- }
- type;
+ enum d_demangle_top_level_type type;
struct d_info di;
struct demangle_component *dc;
int status;
@@ -6874,25 +6876,26 @@ d_demangle_callback (const char *mangled, int options,
again:
cplus_demangle_init_info (mangled, options, strlen (mangled), &di);
- /* PR 87675 - Check for a mangled string that is so long
- that we do not have enough stack space to demangle it. */
- if (((options & DMGL_NO_RECURSE_LIMIT) == 0)
- /* This check is a bit arbitrary, since what we really want to do is to
- compare the sizes of the di.comps and di.subs arrays against the
- amount of stack space remaining. But there is no portable way to do
- this, so instead we use the recursion limit as a guide to the maximum
- size of the arrays. */
- && (unsigned long) di.num_comps > DEMANGLE_RECURSION_LIMIT)
- {
- /* FIXME: We need a way to indicate that a stack limit has been reached.
*/
- return 0;
- }
-
{
#ifdef CP_DYNAMIC_ARRAYS
__extension__ struct demangle_component comps[di.num_comps];
__extension__ struct demangle_component *subs[di.num_subs];
+ /* PR 87675 - Check for a mangled string that is so long
+ that we do not have enough stack space to demangle it. */
+ if (((options & DMGL_NO_RECURSE_LIMIT) == 0)
+ /* This check is a bit arbitrary, since what we really want to do is to
+ compare the sizes of the di.comps and di.subs arrays against the
+ amount of stack space remaining. But there is no portable way to do
+ this, so instead we use the recursion limit as a guide to the maximum
+ size of the arrays. */
+ && (unsigned long) di.num_comps > DEMANGLE_RECURSION_LIMIT)
+ {
+ /* FIXME: We need a way to indicate that a stack limit has been
+ reached. */
+ return 0;
+ }
+
di.comps = comps;
di.subs = subs;
#else
@@ -6959,22 +6962,99 @@ d_demangle_callback (const char *mangled, int options,
static char *
d_demangle (const char *mangled, int options, size_t *palc)
{
- struct d_growable_string dgs;
- int status;
+ enum d_demangle_top_level_type type;
+ struct d_info di;
+ struct demangle_component *dc;
+ struct demangle_component *comps;
+ struct demangle_component **subs;
+ char *demangled;
+
+ if (mangled[0] == '_' && mangled[1] == 'Z')
+ type = DCT_MANGLED;
+ else if (strncmp (mangled, "_GLOBAL_", 8) == 0
+ && (mangled[8] == '.' || mangled[8] == '_' || mangled[8] == '$')
+ && (mangled[9] == 'D' || mangled[9] == 'I')
+ && mangled[10] == '_')
+ type = mangled[9] == 'I' ? DCT_GLOBAL_CTORS : DCT_GLOBAL_DTORS;
+ else
+ {
+ if ((options & DMGL_TYPES) == 0)
+ {
+ *palc = 0;
+ return NULL;
+ }
+ type = DCT_TYPE;
+ }
- d_growable_string_init (&dgs, 0);
+ cplus_demangle_init_info (mangled, options, strlen (mangled), &di);
- status = d_demangle_callback (mangled, options,
- d_growable_string_callback_adapter, &dgs);
- if (status == 0)
+ comps = ((struct demangle_component *)
+ malloc (di.num_comps * sizeof (*comps)));
+ subs = ((struct demangle_component **)
+ malloc (di.num_subs * sizeof (*subs)));
+ if (comps == NULL || subs == NULL)
{
- free (dgs.buf);
+ free (comps);
+ free (subs);
+ *palc = 1;
+ return NULL;
+ }
+
+ di.unresolved_name_state = 1;
+
+ again:
+ cplus_demangle_init_info (mangled, options, strlen (mangled), &di);
+ di.comps = comps;
+ di.subs = subs;
+
+ switch (type)
+ {
+ case DCT_TYPE:
+ dc = cplus_demangle_type (&di);
+ break;
+ case DCT_MANGLED:
+ dc = cplus_demangle_mangled_name (&di, 1);
+ break;
+ case DCT_GLOBAL_CTORS:
+ case DCT_GLOBAL_DTORS:
+ d_advance (&di, 11);
+ dc = d_make_comp (&di,
+ (type == DCT_GLOBAL_CTORS
+ ? DEMANGLE_COMPONENT_GLOBAL_CONSTRUCTORS
+ : DEMANGLE_COMPONENT_GLOBAL_DESTRUCTORS),
+ d_make_demangle_mangled_name (&di, d_str (&di)),
+ NULL);
+ d_advance (&di, strlen (d_str (&di)));
+ break;
+ default:
+ abort (); /* We have listed all the cases. */
+ }
+
+ if (((options & DMGL_PARAMS) != 0) && d_peek_char (&di) != '\0')
+ dc = NULL;
+
+ if (dc == NULL && di.unresolved_name_state == -1)
+ {
+ di.unresolved_name_state = 0;
+ goto again;
+ }
+
+ if (dc == NULL)
+ {
+ free (subs);
+ free (comps);
*palc = 0;
return NULL;
}
- *palc = dgs.allocation_failure ? 1 : dgs.alc;
- return dgs.buf;
+#ifdef CP_DEMANGLE_DEBUG
+ d_dump (dc, 0);
+#endif
+
+ demangled = cplus_demangle_print (options, dc, 0, palc);
+ free (subs);
+ free (comps);
+ return demangled;
}
#if defined(IN_LIBGCC2) || defined(IN_GLIBCPP_V3)
diff --git a/libstdc++-v3/testsuite/abi/demangle/regression/123988.cc
b/libstdc++-v3/testsuite/abi/demangle/regression/123988.cc
new file mode 100644
index 00000000000..3eedf103e93
--- /dev/null
+++ b/libstdc++-v3/testsuite/abi/demangle/regression/123988.cc
@@ -0,0 +1,55 @@
+// { dg-do run }
+
+// Copyright (C) 2026 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+#include <string>
+#include <testsuite_hooks.h>
+
+static std::string
+expected_name (int depth)
+{
+ std::string name("Type<");
+
+ for (int i = 0; i < depth; ++i)
+ name += "std::type_identity<";
+
+ name += "void>";
+
+ for (int i = 0; i < depth; ++i)
+ name += " >";
+
+ name += "::bar()";
+ return name;
+}
+
+// demangler/123988
+int main()
+{
+ using namespace __gnu_test;
+
+ const char longer[] =
"_ZN4TypeISt13type_identityIS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS"
+ "0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS"
+ "0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS"
+ "0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS"
+ "0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS"
+ "0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS"
+ "0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS"
+ "0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS"
+ "0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS"
+ "0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS"
+ "0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS0_IS"
+ "0_IS0_IS0_IS0_IS0_IS0_IS0_IvEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
+ "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
+ "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
+ "EEEEEEEEEEEE3barEv";
+
+ const std::string longer_expected = expected_name(199);
+
+ verify_demangle(longer, longer_expected.c_str());
+}
--
2.51.0