Hi Guys,

>> I think it would be fine to have a large fixed limit plus a flag to
>> disable the limit.

Great - in which case please may I present version 3 of the patch.  In 
this version:

  * The cplus_demangle_set_recursion_limit() function has been removed
    and instead a new constant - DEMANGLE_RECURSION_LIMIT - is defined in
    demangle.h.

  * The recursion counters in cp-demangle.c have been merged into one
    counter, now contained in the d_info structure.

  * In cplus-dem.c the recursion counter has been moved into the work
    structure.

  * The description of the DMGL_RECURSE_LIMIT option in demangle.h has
    been enhanced to add a note that if the option is not used, then
    bug reports about stack overflows in the demangler will be rejected.

  * The binutils patch has been updated to reflect these changes.  The
    addr2line, cxxfilt, nm and objdump programs now have two new options
    --recurse-limit and --no-recurse-limit, with --recurse-limit being
    the default.  The documentation is updated to describe these options
    and to also add a note about bug reports being rejected if 
    --no-recurse-limit is used.

What do you think, is this version acceptable ?

Cheers
  Nick

libiberty/ChangeLog
2018-11-29  Nick Clifton  <ni...@redhat.com>

        PR 87681
        PR 87675
        PR 87636
        PR 87335
        * cp-demangle.h (struct d_info): Add recursion_limit field.
        * cp-demangle.c (d_function_type): If the recursion limit is 
        enabled and reached, return with a failure result.
        (d_demangle_callback): If the recursion limit is enabled, check
        for a mangled string that is so long that there is not enough
        stack space for the local arrays.
        * cplus-dem.c (struct work): Add recursion_level field.
        (demangle_nested_args): If the recursion limit is enabled and 
        reached, return with a failure result.

include/ChangeLog
2018-11-29  Nick Clifton  <ni...@redhat.com>

        * demangle.h (DMGL_RECURSE_LIMIT): Define.
        (DEMANGLE_RECURSION_LIMIT): Prototype.

binutils/ChangeLog
2018-11-29  Nick Clifton  <ni...@redhat.com>

        * addr2line.c (demangle_flags): New static variable.
        (long_options): Add --recurse-limit and --no-recurse-limit.
        (translate_address): Pass demangle_flags to bfd_demangle.
        (main): Handle --recurse-limit and --no-recurse-limit options.
        * cxxfilt.c (flags): Add DMGL_RECURSE_LIMIT.
        (long_options): Add --recurse-limit and --no-recurse-limit.
        (main): Handle new options.
        * dlltool.c (gen_def_file): Include DMGL_RECURSE_LIMIT in flags
        passed to cplus_demangle.
        * nm.c (demangle_flags): New static variable.
        (long_options): Add --recurse-limit and --no-recurse-limit.
        (main): Handle new options.
        * objdump.c (demangle_flags): New static variable.
        (usage): Add --recurse-limit and --no-recurse-limit.
        (long_options): Likewise.
        (objdump_print_symname): Pass demangle_flags to bfd_demangle.
        (disassemble_section): Likewise.
        (dump_dymbols): Likewise.
        (main): Handle new options.
        * prdbg.c (demangle_flags): New static variable.
        (tg_variable): Pass demangle_flags to demangler.
        (tg_start_function): Likewise.
        * stabs.c (demangle_flags): New static variable.
        (stab_demangle_template): Pass demangle_flags to demangler.
        (stab_demangle_v3_argtypes): Likewise.
        (stab_demangle_v3_arg): Likewise.
        * doc/binutuls.texi: Document new command line options.
        * NEWS: Mention the new feature.
        * testsuite/config/default.exp (CXXFILT): Define if not already
        defined.
        (CXXFILTFLAGS): Likewise.
        * testsuite/binutils-all/cxxfilt.exp: New file.  Runs a few
        simple tests of the cxxfilt program.
diff --git a/binutils/NEWS b/binutils/NEWS
index a3ee86ef7f..5ed61c8513 100644
--- a/binutils/NEWS
+++ b/binutils/NEWS
@@ -1,5 +1,16 @@
 -*- text -*-
 
+* The addr2line, c++filt, nm and objdump tools now have a limit on the
+  maximum amount of recursion that is allowed whilst demangling strings.
+  The value for this limit is defined by the DEMANGLE_RECRUSE_LIMIT
+  constant declared in the include/demangle.h header file.  At the time
+  of writing this constant has the value of 1024.
+
+  The --no-recurse-limit option can be used to remove the limit, restoring
+  the behaviour of earlier versions of these tools.  This may be needed in
+  order to dmangle truely complicated names, but it also leaves the tools
+  vulnerable to stack exhaustion from maliciously constructed mangled names.
+
 * Objdump's --disassemble option can now take a parameter, specifying the
   starting symbol for disassembly.  Disassembly will continue from this
   symbol up to the next symbol.
diff --git a/binutils/addr2line.c b/binutils/addr2line.c
index 008e62026e..3085806a4a 100644
--- a/binutils/addr2line.c
+++ b/binutils/addr2line.c
@@ -45,6 +45,9 @@ static bfd_boolean do_demangle;		/* -C, demangle names.  */
 static bfd_boolean pretty_print;	/* -p, print on one line.  */
 static bfd_boolean base_names;		/* -s, strip directory names.  */
 
+/* Flags passed to the name demangler.  */
+static int demangle_flags = DMGL_PARAMS | DMGL_ANSI | DMGL_RECURSE_LIMIT;
+
 static int naddr;		/* Number of addresses to process.  */
 static char **addr;		/* Hex addresses to process.  */
 
@@ -59,6 +62,10 @@ static struct option long_options[] =
   {"functions", no_argument, NULL, 'f'},
   {"inlines", no_argument, NULL, 'i'},
   {"pretty-print", no_argument, NULL, 'p'},
+  {"recurse-limit", no_argument, NULL, 'R'},
+  {"recursion-limit", no_argument, NULL, 'R'},  
+  {"no-recurse-limit", no_argument, NULL, 'r'},
+  {"no-recursion-limit", no_argument, NULL, 'r'},  
   {"section", required_argument, NULL, 'j'},
   {"target", required_argument, NULL, 'b'},
   {"help", no_argument, NULL, 'H'},
@@ -91,6 +98,8 @@ usage (FILE *stream, int status)
   -s --basenames         Strip directory names\n\
   -f --functions         Show function names\n\
   -C --demangle[=style]  Demangle function names\n\
+  -R --recurse-limit     Enable a limit on recursion whilst demangling.  [Default]\n\
+  -r --no-recurse-limit  Disable a limit on recursion whilst demangling\n\
   -h --help              Display this information\n\
   -v --version           Display the program's version\n\
 \n"));
@@ -289,7 +298,7 @@ translate_addresses (bfd *abfd, asection *section)
                     name = "??";
                   else if (do_demangle)
                     {
-                      alloc = bfd_demangle (abfd, name, DMGL_ANSI | DMGL_PARAMS);
+                      alloc = bfd_demangle (abfd, name, demangle_flags);
                       if (alloc != NULL)
                         name = alloc;
                     }
@@ -442,7 +451,7 @@ main (int argc, char **argv)
   file_name = NULL;
   section_name = NULL;
   target = NULL;
-  while ((c = getopt_long (argc, argv, "ab:Ce:sfHhij:pVv", long_options, (int *) 0))
+  while ((c = getopt_long (argc, argv, "ab:Ce:rRsfHhij:pVv", long_options, (int *) 0))
 	 != EOF)
     {
       switch (c)
@@ -469,6 +478,12 @@ main (int argc, char **argv)
 	      cplus_demangle_set_style (style);
 	    }
 	  break;
+	case 'R':
+	  demangle_flags |= DMGL_RECURSE_LIMIT;
+	  break;
+	case 'r':
+	  demangle_flags &= ~ DMGL_RECURSE_LIMIT;
+	  break;
 	case 'e':
 	  file_name = optarg;
 	  break;
diff --git a/binutils/cxxfilt.c b/binutils/cxxfilt.c
index e7272445c9..f1027c020c 100644
--- a/binutils/cxxfilt.c
+++ b/binutils/cxxfilt.c
@@ -29,7 +29,7 @@
 #include "safe-ctype.h"
 #include "bucomm.h"
 
-static int flags = DMGL_PARAMS | DMGL_ANSI | DMGL_VERBOSE;
+static int flags = DMGL_PARAMS | DMGL_ANSI | DMGL_VERBOSE | DMGL_RECURSE_LIMIT;
 static int strip_underscore = TARGET_PREPENDS_UNDERSCORE;
 
 static const struct option long_options[] =
@@ -42,6 +42,10 @@ static const struct option long_options[] =
   {"no-verbose", no_argument, NULL, 'i'},
   {"types", no_argument, NULL, 't'},
   {"version", no_argument, NULL, 'v'},
+  {"recurse-limit", no_argument, NULL, 'R'},
+  {"recursion-limit", no_argument, NULL, 'R'},
+  {"no-recurse-limit", no_argument, NULL, 'r'},
+  {"no-recursion-limit", no_argument, NULL, 'r'},
   {NULL, no_argument, NULL, 0}
 };
 
@@ -102,6 +106,8 @@ Options are:\n\
   fprintf (stream, "\
   [-p|--no-params]            Do not display function arguments\n\
   [-i|--no-verbose]           Do not show implementation details (if any)\n\
+  [-R|--recurse-limit]        Enable a limit on recursion whilst demangling.  [Default]\n\
+  ]-r|--no-recurse-limit]     Disable a limit on recursion whilst demangling\n\
   [-t|--types]                Also attempt to demangle type encodings\n\
   [-s|--format ");
   print_demangler_list (stream);
@@ -180,7 +186,7 @@ main (int argc, char **argv)
 
   expandargv (&argc, &argv);
 
-  while ((c = getopt_long (argc, argv, "_hinps:tv", long_options, (int *) 0)) != EOF)
+  while ((c = getopt_long (argc, argv, "_hinprRs:tv", long_options, (int *) 0)) != EOF)
     {
       switch (c)
 	{
@@ -195,6 +201,12 @@ main (int argc, char **argv)
 	case 'p':
 	  flags &= ~ DMGL_PARAMS;
 	  break;
+	case 'R':
+	  flags |= DMGL_RECURSE_LIMIT;
+	  break;
+	case 'r':
+	  flags &= ~ DMGL_RECURSE_LIMIT;
+	  break;
 	case 't':
 	  flags |= DMGL_TYPES;
 	  break;
diff --git a/binutils/dlltool.c b/binutils/dlltool.c
index 2c751241f1..963d541b8e 100644
--- a/binutils/dlltool.c
+++ b/binutils/dlltool.c
@@ -1802,7 +1802,7 @@ gen_def_file (void)
   for (i = 0, exp = d_exports; exp; i++, exp = exp->next)
     {
       char *quote = strchr (exp->name, '.') ? "\"" : "";
-      char *res = cplus_demangle (exp->internal_name, DMGL_ANSI | DMGL_PARAMS);
+      char *res = cplus_demangle (exp->internal_name, DMGL_ANSI | DMGL_PARAMS | DMGL_RECURSE_LIMIT);
 
       if (res)
 	{
diff --git a/binutils/doc/binutils.texi b/binutils/doc/binutils.texi
index 9954adf484..0f1b493e39 100644
--- a/binutils/doc/binutils.texi
+++ b/binutils/doc/binutils.texi
@@ -769,7 +769,9 @@ nm [@option{-A}|@option{-o}|@option{--print-file-name}] [@option{-a}|@option{--d
    [@option{-s}|@option{--print-armap}] [@option{-t} @var{radix}|@option{--radix=}@var{radix}]
    [@option{-u}|@option{--undefined-only}] [@option{-V}|@option{--version}]
    [@option{-X 32_64}] [@option{--defined-only}] [@option{--no-demangle}]
-   [@option{--plugin} @var{name}] [@option{--size-sort}] [@option{--special-syms}]
+   [@option{--plugin} @var{name}]
+   [@option{--no-recurse-limit}|@option{--recurse-limit}]]
+   [@option{--size-sort}] [@option{--special-syms}]
    [@option{--synthetic}] [@option{--with-symbol-versions}] [@option{--target=}@var{bfdname}]
    [@var{objfile}@dots{}]
 @c man end
@@ -939,6 +941,22 @@ for more information on demangling.
 @item --no-demangle
 Do not demangle low-level symbol names.  This is the default.
 
+@item --recurse-limit
+@itemx --no-recurse-limit
+@itemx --recursion-limit
+@itemx --no-recursion-limit
+Enables or disables a limit on the amount of recursion performed
+whilst demangling strings.  Since the name mangling formats allow for
+an inifinite level of recursion it is possible to create strings whose
+decoding will exhaust the amount of stack space available on the host
+machine, triggering a memory fault.  The limit tries to prevent this
+from happening by restricting recursion to 1024 levels of nesting.
+
+The default is for this limit to be enabled, but disabling it may be
+necessary in order to demangle truely complicated names.  Note however
+that if the recursion limit is disabled then stack exhaustion is
+possible and any bug reports about such an event will be rejected.
+
 @item -D
 @itemx --dynamic
 @cindex dynamic symbols
@@ -2098,6 +2116,7 @@ objdump [@option{-a}|@option{--archive-headers}]
         [@option{--adjust-vma=}@var{offset}]
         [@option{--dwarf-depth=@var{n}}]
         [@option{--dwarf-start=@var{n}}]
+        [@option{--no-recurse-limit}|@option{--recurse-limit}]
         [@option{--special-syms}]
         [@option{--prefix=}@var{prefix}]
         [@option{--prefix-strip=}@var{level}]
@@ -2174,6 +2193,22 @@ mangling styles. The optional demangling style argument can be used to
 choose an appropriate demangling style for your compiler. @xref{c++filt},
 for more information on demangling.
 
+@item --recurse-limit
+@itemx --no-recurse-limit
+@itemx --recursion-limit
+@itemx --no-recursion-limit
+Enables or disables a limit on the amount of recursion performed
+whilst demangling strings.  Since the name mangling formats allow for
+an inifinite level of recursion it is possible to create strings whose
+decoding will exhaust the amount of stack space available on the host
+machine, triggering a memory fault.  The limit tries to prevent this
+from happening by restricting recursion to 1024 levels of nesting.
+
+The default is for this limit to be enabled, but disabling it may be
+necessary in order to demangle truely complicated names.  Note however
+that if the recursion limit is disabled then stack exhaustion is
+possible and any bug reports about such an event will be rejected.
+
 @item -g
 @itemx --debugging
 Display debugging information.  This attempts to parse STABS
@@ -3403,6 +3438,8 @@ c++filt [@option{-_}|@option{--strip-underscore}]
         [@option{-p}|@option{--no-params}]
         [@option{-t}|@option{--types}]
         [@option{-i}|@option{--no-verbose}]
+        [@option{-r}|@option{--no-recurse-limit}]
+        [@option{-R}|@option{--recurse-limit}]
         [@option{-s} @var{format}|@option{--format=}@var{format}]
         [@option{--help}]  [@option{--version}]  [@var{symbol}@dots{}]
 @c man end
@@ -3507,6 +3544,28 @@ demangled to ``signed char''.
 Do not include implementation details (if any) in the demangled
 output.
 
+@item -r
+@itemx -R
+@itemx --recurse-limit
+@itemx --no-recurse-limit
+@itemx --recursion-limit
+@itemx --no-recursion-limit
+Enables or disables a limit on the amount of recursion performed
+whilst demangling strings.  Since the name mangling formats allow for
+an inifinite level of recursion it is possible to create strings whose
+decoding will exhaust the amount of stack space available on the host
+machine, triggering a memory fault.  The limit tries to prevent this
+from happening by restricting recursion to 1024 levels of nesting.
+
+The default is for this limit to be enabled, but disabling it may be
+necessary in order to demangle truely complicated names.  Note however
+that if the recursion limit is disabled then stack exhaustion is
+possible and any bug reports about such an event will be rejected.
+
+The @option{-r} option is a snyonym for the
+@option{--no-recurse-limit} option.  The @option{-R} option is a
+synonym for the @option{--recurse-limit} option.
+
 @item -s @var{format}
 @itemx --format=@var{format}
 @command{c++filt} can decode various methods of mangling, used by
@@ -3580,6 +3639,8 @@ c++filt @var{option} @var{symbol}
 addr2line [@option{-a}|@option{--addresses}]
           [@option{-b} @var{bfdname}|@option{--target=}@var{bfdname}]
           [@option{-C}|@option{--demangle}[=@var{style}]]
+          [@option{-r}|@option{--no-recurse-limit}]
+          [@option{-R}|@option{--recurse-limit}]
           [@option{-e} @var{filename}|@option{--exe=}@var{filename}]
           [@option{-f}|@option{--functions}] [@option{-s}|@option{--basename}]
           [@option{-i}|@option{--inlines}]
@@ -3705,6 +3766,32 @@ Read offsets relative to the specified section instead of absolute addresses.
 Make the output more human friendly: each location are printed on one line.
 If option @option{-i} is specified, lines for all enclosing scopes are
 prefixed with @samp{(inlined by)}.
+
+@item -r
+@itemx -R
+@itemx --recurse-limit
+@itemx --no-recurse-limit
+@itemx --recursion-limit
+@itemx --no-recursion-limit
+Enables or disables a limit on the amount of recursion performed
+whilst demangling strings.  Since the name mangling formats allow for
+an inifinite level of recursion it is possible to create strings whose
+decoding will exhaust the amount of stack space available on the host
+machine, triggering a memory fault.  The limit tries to prevent this
+from happening by restricting recursion to 1024 levels of nesting.
+
+The default is for this limit to be enabled, but disabling it may be
+necessary in order to demangle truely complicated names.  Note however
+that if the recursion limit is disabled then stack exhaustion is
+possible and any bug reports about such an event will be rejected.
+
+The @option{-r} option is a snyonym for the
+@option{--no-recurse-limit} option.  The @option{-R} option is a
+synonym for the @option{--recurse-limit} option.
+
+Note this option is only effective if the @option{-C} or
+@option{--demangle} option has been enabled.
+
 @end table
 
 @c man end
diff --git a/binutils/nm.c b/binutils/nm.c
index bc4fccb5fc..1172d6222c 100644
--- a/binutils/nm.c
+++ b/binutils/nm.c
@@ -162,6 +162,8 @@ static int line_numbers = 0;	/* Print line numbers for symbols.  */
 static int allow_special_symbols = 0;  /* Allow special symbols.  */
 static int with_symbol_versions = 0; /* Include symbol version information in the output.  */
 
+static int demangle_flags = DMGL_ANSI | DMGL_PARAMS | DMGL_RECURSE_LIMIT;
+
 /* When to print the names of files.  Not mutually exclusive in SYSV format.  */
 static int filename_per_file = 0;	/* Once per file, on its own line.  */
 static int filename_per_symbol = 0;	/* Once per symbol, at start of line.  */
@@ -194,9 +196,14 @@ static const char *plugin_target = NULL;
 static bfd *lineno_cache_bfd;
 static bfd *lineno_cache_rel_bfd;
 
-#define OPTION_TARGET 200
-#define OPTION_PLUGIN (OPTION_TARGET + 1)
-#define OPTION_SIZE_SORT (OPTION_PLUGIN + 1)
+enum long_option_values
+{
+  OPTION_TARGET = 200,
+  OPTION_PLUGIN,
+  OPTION_SIZE_SORT,
+  OPTION_RECURSE_LIMIT,
+  OPTION_NO_RECURSE_LIMIT
+};
 
 static struct option long_options[] =
 {
@@ -209,6 +216,8 @@ static struct option long_options[] =
   {"line-numbers", no_argument, 0, 'l'},
   {"no-cplus", no_argument, &do_demangle, 0},  /* Linux compatibility.  */
   {"no-demangle", no_argument, &do_demangle, 0},
+  {"no-recurse-limit", no_argument, NULL, OPTION_NO_RECURSE_LIMIT},
+  {"no-recursion-limit", no_argument, NULL, OPTION_NO_RECURSE_LIMIT},
   {"no-sort", no_argument, 0, 'p'},
   {"numeric-sort", no_argument, 0, 'n'},
   {"plugin", required_argument, 0, OPTION_PLUGIN},
@@ -217,6 +226,8 @@ static struct option long_options[] =
   {"print-file-name", no_argument, 0, 'o'},
   {"print-size", no_argument, 0, 'S'},
   {"radix", required_argument, 0, 't'},
+  {"recurse-limit", no_argument, NULL, OPTION_RECURSE_LIMIT},
+  {"recursion-limit", no_argument, NULL, OPTION_RECURSE_LIMIT},
   {"reverse-sort", no_argument, &reverse_sort, 1},
   {"size-sort", no_argument, 0, OPTION_SIZE_SORT},
   {"special-syms", no_argument, &allow_special_symbols, 1},
@@ -245,6 +256,8 @@ usage (FILE *stream, int status)
                           `gnu', `lucid', `arm', `hp', `edg', `gnu-v3', `java'\n\
                           or `gnat'\n\
       --no-demangle      Do not demangle low-level symbol names\n\
+      --recurse-limit    Enable a demangling recursion limit.  This is the default.\n\
+      --no-recurse-limit Disable a demangling recursion limit.\n\
   -D, --dynamic          Display dynamic symbols instead of normal symbols\n\
       --defined-only     Display only defined symbols\n\
   -e                     (ignored)\n\
@@ -407,7 +420,7 @@ print_symname (const char *form, const char *name, bfd *abfd)
 {
   if (do_demangle && *name)
     {
-      char *res = bfd_demangle (abfd, name, DMGL_ANSI | DMGL_PARAMS);
+      char *res = bfd_demangle (abfd, name, demangle_flags);
 
       if (res != NULL)
 	{
@@ -1687,6 +1700,12 @@ main (int argc, char **argv)
 	      cplus_demangle_set_style (style);
 	    }
 	  break;
+	case OPTION_RECURSE_LIMIT:
+	  demangle_flags |= DMGL_RECURSE_LIMIT;
+	  break;
+	case OPTION_NO_RECURSE_LIMIT:
+	  demangle_flags &= ~ DMGL_RECURSE_LIMIT;
+	  break;
 	case 'D':
 	  dynamic = 1;
 	  break;
diff --git a/binutils/objdump.c b/binutils/objdump.c
index 21f1284319..e0234232a4 100644
--- a/binutils/objdump.c
+++ b/binutils/objdump.c
@@ -120,6 +120,8 @@ static size_t prefix_length;
 static bfd_boolean unwind_inlines;	/* --inlines.  */
 static const char * disasm_sym;		/* Disassembly start symbol.  */
 
+static int demangle_flags = DMGL_ANSI | DMGL_PARAMS | DMGL_RECURSE_LIMIT;
+
 /* A structure to record the sections mentioned in -j switches.  */
 struct only
 {
@@ -252,6 +254,8 @@ usage (FILE *stream, int status)
                                   The STYLE, if specified, can be `auto', `gnu',\n\
                                   `lucid', `arm', `hp', `edg', `gnu-v3', `java'\n\
                                   or `gnat'\n\
+      --recurse-limit            Enable a limit on recursion whilst demangling.  [Default]\n\
+      --no-recurse-limit         Disable a limit on recursion whilst demangling\n\
   -w, --wide                     Format output for more than 80 columns\n\
   -z, --disassemble-zeroes       Do not skip blocks of zeroes when disassembling\n\
       --start-address=ADDR       Only process data whose address is >= ADDR\n\
@@ -302,6 +306,8 @@ enum option_values
     OPTION_DWARF_DEPTH,
     OPTION_DWARF_CHECK,
     OPTION_DWARF_START,
+    OPTION_RECURSE_LIMIT,
+    OPTION_NO_RECURSE_LIMIT,
     OPTION_INLINES
   };
 
@@ -333,6 +339,10 @@ static struct option long_options[]=
   {"line-numbers", no_argument, NULL, 'l'},
   {"no-show-raw-insn", no_argument, &show_raw_insn, -1},
   {"prefix-addresses", no_argument, &prefix_addresses, 1},
+  {"recurse-limit", no_argument, NULL, OPTION_RECURSE_LIMIT},
+  {"recursion-limit", no_argument, NULL, OPTION_RECURSE_LIMIT},
+  {"no-recurse-limit", no_argument, NULL, OPTION_NO_RECURSE_LIMIT},
+  {"no-recursion-limit", no_argument, NULL, OPTION_NO_RECURSE_LIMIT},
   {"reloc", no_argument, NULL, 'r'},
   {"section", required_argument, NULL, 'j'},
   {"section-headers", no_argument, NULL, 'h'},
@@ -884,7 +894,7 @@ objdump_print_symname (bfd *abfd, struct disassemble_info *inf,
   if (do_demangle && name[0] != '\0')
     {
       /* Demangle the name.  */
-      alloc = bfd_demangle (abfd, name, DMGL_ANSI | DMGL_PARAMS);
+      alloc = bfd_demangle (abfd, name, demangle_flags);
       if (alloc != NULL)
 	name = alloc;
     }
@@ -2290,7 +2300,7 @@ disassemble_section (bfd *abfd, asection *section, void *inf)
 	  if (do_demangle && name[0] != '\0')
 	    {
 	      /* Demangle the name.  */
-	      alloc = bfd_demangle (abfd, name, DMGL_ANSI | DMGL_PARAMS);
+	      alloc = bfd_demangle (abfd, name, demangle_flags);
 	      if (alloc != NULL)
 		name = alloc;
 	    }
@@ -3268,7 +3278,7 @@ dump_symbols (bfd *abfd ATTRIBUTE_UNUSED, bfd_boolean dynamic)
 	      /* If we want to demangle the name, we demangle it
 		 here, and temporarily clobber it while calling
 		 bfd_print_symbol.  FIXME: This is a gross hack.  */
-	      alloc = bfd_demangle (cur_bfd, name, DMGL_ANSI | DMGL_PARAMS);
+	      alloc = bfd_demangle (cur_bfd, name, demangle_flags);
 	      if (alloc != NULL)
 		(*current)->name = alloc;
 	      bfd_print_symbol (cur_bfd, stdout, *current,
@@ -3927,6 +3937,12 @@ main (int argc, char **argv)
 	      cplus_demangle_set_style (style);
 	    }
 	  break;
+	case OPTION_RECURSE_LIMIT:
+	  demangle_flags |= DMGL_RECURSE_LIMIT;
+	  break;
+	case OPTION_NO_RECURSE_LIMIT:
+	  demangle_flags &= ~ DMGL_RECURSE_LIMIT;
+	  break;
 	case 'w':
 	  do_wide = wide_output = TRUE;
 	  break;
diff --git a/binutils/prdbg.c b/binutils/prdbg.c
index 4b9fa06c26..c981555521 100644
--- a/binutils/prdbg.c
+++ b/binutils/prdbg.c
@@ -286,6 +286,8 @@ static const struct debug_write_fns tg_fns =
   pr_end_function,		/* Same, does nothing.  */
   tg_lineno
 };
+
+static int demangle_flags = DMGL_ANSI | DMGL_PARAMS | DMGL_RECURSE_LIMIT;
 
 /* Print out the generic debugging information recorded in dhandle.  */
 
@@ -2600,7 +2602,7 @@ tg_variable (void *p, const char *name, enum debug_var_kind kind,
 
   dname = NULL;
   if (info->demangler)
-    dname = info->demangler (info->abfd, name, DMGL_ANSI | DMGL_PARAMS);
+    dname = info->demangler (info->abfd, name, demangle_flags);
 
   from_class = NULL;
   if (dname != NULL)
@@ -2661,7 +2663,7 @@ tg_start_function (void *p, const char *name, bfd_boolean global)
 
   dname = NULL;
   if (info->demangler)
-    dname = info->demangler (info->abfd, name, DMGL_ANSI | DMGL_PARAMS);
+    dname = info->demangler (info->abfd, name, demangle_flags);
 
   if (! substitute_type (info, dname ? dname : name))
     return FALSE;
diff --git a/binutils/stabs.c b/binutils/stabs.c
index bf53607560..dcd2aba57f 100644
--- a/binutils/stabs.c
+++ b/binutils/stabs.c
@@ -215,6 +215,8 @@ static debug_type stab_demangle_v3_arg
   (void *, struct stab_handle *, struct demangle_component *, debug_type,
    bfd_boolean *);
 
+static int demangle_flags = DMGL_ANSI | DMGL_RECURSE_LIMIT;
+
 /* Save a string in memory.  */
 
 static char *
@@ -4517,7 +4519,7 @@ stab_demangle_template (struct stab_demangle_info *minfo, const char **pp,
 
       free (s1);
 
-      s3 = cplus_demangle (s2, DMGL_ANSI);
+      s3 = cplus_demangle (s2, demangle_flags);
 
       free (s2);
 
@@ -5243,7 +5245,7 @@ stab_demangle_v3_argtypes (void *dhandle, struct stab_handle *info,
   void *mem;
   debug_type *pargs;
 
-  dc = cplus_demangle_v3_components (physname, DMGL_PARAMS | DMGL_ANSI, &mem);
+  dc = cplus_demangle_v3_components (physname, DMGL_PARAMS | demangle_flags, &mem);
   if (dc == NULL)
     {
       stab_bad_demangle (physname);
@@ -5418,7 +5420,7 @@ stab_demangle_v3_arg (void *dhandle, struct stab_handle *info,
 	/* We print this component to get a class name which we can
 	   use.  FIXME: This probably won't work if the template uses
 	   template parameters which refer to an outer template.  */
-	p = cplus_demangle_print (DMGL_PARAMS | DMGL_ANSI, dc, 20, &alc);
+	p = cplus_demangle_print (DMGL_PARAMS | demangle_flags, dc, 20, &alc);
 	if (p == NULL)
 	  {
 	    fprintf (stderr, _("Failed to print demangled template\n"));
@@ -5498,7 +5500,7 @@ stab_demangle_v3_arg (void *dhandle, struct stab_handle *info,
 	/* We print this component in order to find out the type name.
 	   FIXME: Should we instead expose the
 	   demangle_builtin_type_info structure?  */
-	p = cplus_demangle_print (DMGL_PARAMS | DMGL_ANSI, dc, 20, &alc);
+	p = cplus_demangle_print (DMGL_PARAMS | demangle_flags, dc, 20, &alc);
 	if (p == NULL)
 	  {
 	    fprintf (stderr, _("Couldn't get demangled builtin type\n"));
diff --git a/binutils/testsuite/config/default.exp b/binutils/testsuite/config/default.exp
index b34e45cd20..9ecfcf3e15 100644
--- a/binutils/testsuite/config/default.exp
+++ b/binutils/testsuite/config/default.exp
@@ -93,6 +93,12 @@ if ![info exists WINDRES] then {
 if ![info exists DLLTOOL] then {
     set DLLTOOL [findfile $base_dir/dlltool]
 }
+if ![info exists CXXFILT] then {
+    set CXXFILT [findfile $base_dir/cxxfilt]
+}
+if ![info exists CXXFILTFLAGS] then {
+    set CXXFILTFLAGS ""
+}
 
 if ![file isdirectory tmpdir] {catch "exec mkdir tmpdir" status}
 
--- /dev/null	2018-11-29 08:15:54.667999248 +0000
+++ binutils/testsuite/binutils-all/cxxfilt.exp	2018-11-29 13:09:29.789147447 +0000
@@ -0,0 +1,44 @@
+#   Copyright (C) 2018 Free Software Foundation, Inc.
+
+# This program 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 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+
+proc test_cxxfilt {options mangled_string demangled_string} {
+    global CXXFILT
+    global CXXFILTFLAGS
+    
+    set testname "cxxfilt: demangling $mangled_string"
+    set got [binutils_run $CXXFILT "$options $CXXFILTFLAGS $mangled_string"]
+
+    if ![regexp $demangled_string $got] then {
+	fail "$testname"
+	verbose 0 "expected: $demangled_string"
+	return
+    }
+
+    pass $testname
+}
+
+# Mangled and demangled strings stolen from libiberty/testsuite/demangle-expected.
+test_cxxfilt {} \
+    "AddAlignment__9ivTSolverUiP12ivInteractorP7ivTGlue" \
+    "ivTSolver::AddAlignment(unsigned int, ivInteractor ., ivTGlue .)*"
+
+test_cxxfilt {--format=lucid} \
+    "__ct__12strstreambufFPFl_PvPFPv_v" \
+    "strstreambuf..strstreambuf(void .(.)(long), void (.)(void .))*"
+
+test_cxxfilt {--recurse-limit=2} \
+    "Z3fooiPiPS_PS0_PS1_PS2_PS3_PS4_PS5_PS6_PS7_PS8_PS9_PSA_PSB_PSC_" \
+    "foo(int, int., int.., int..., int...., int....., int......, int......., int........, int........., int.........., int..........., int............, int............., int.............., int...............)*"
Index: libiberty/cp-demangle.c
===================================================================
--- libiberty/cp-demangle.c	(revision 266657)
+++ libiberty/cp-demangle.c	(working copy)
@@ -2852,21 +2852,35 @@
 static struct demangle_component *
 d_function_type (struct d_info *di)
 {
-  struct demangle_component *ret;
+  struct demangle_component *ret = NULL;
 
-  if (! d_check_char (di, 'F'))
-    return NULL;
-  if (d_peek_char (di) == 'Y')
+  if (di->options & DMGL_RECURSE_LIMIT)
     {
-      /* Function has C linkage.  We don't print this information.
-	 FIXME: We should print it in verbose mode.  */
-      d_advance (di, 1);
+      if (di->recursion_level > DEMANGLE_RECURSION_LIMIT)
+	/* FIXME: There ought to be a way to report
+	   that the recursion limit has been reached.  */
+	return NULL;
+
+      di->recursion_level ++;
     }
-  ret = d_bare_function_type (di, 1);
-  ret = d_ref_qualifier (di, ret);
 
-  if (! d_check_char (di, 'E'))
-    return NULL;
+  if (d_check_char (di, 'F'))
+    {
+      if (d_peek_char (di) == 'Y')
+	{
+	  /* Function has C linkage.  We don't print this information.
+	     FIXME: We should print it in verbose mode.  */
+	  d_advance (di, 1);
+	}
+      ret = d_bare_function_type (di, 1);
+      ret = d_ref_qualifier (di, ret);
+      
+      if (! d_check_char (di, 'E'))
+	ret = NULL;
+    }
+
+  if (di->options & DMGL_RECURSE_LIMIT)
+    di->recursion_level --;
   return ret;
 }
 
@@ -6203,6 +6217,7 @@
   di->expansion = 0;
   di->is_expression = 0;
   di->is_conversion = 0;
+  di->recursion_level = 0;
 }
 
 /* Internal implementation for the demangler.  If MANGLED is a g++ v3 ABI
@@ -6242,6 +6257,20 @@
 
   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_RECURSE_LIMIT)
+      /* 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];
Index: libiberty/cp-demangle.h
===================================================================
--- libiberty/cp-demangle.h	(revision 266657)
+++ libiberty/cp-demangle.h	(working copy)
@@ -122,6 +122,9 @@
   /* Non-zero if we are parsing the type operand of a conversion
      operator, but not when in an expression.  */
   int is_conversion;
+  /* If DMGL_RECURSE_LIMIT is active then this is set to the
+     current recursion level.  */
+  unsigned int recursion_level;
 };
 
 /* To avoid running past the ending '\0', don't:
Index: libiberty/cplus-dem.c
===================================================================
--- libiberty/cplus-dem.c	(revision 266657)
+++ libiberty/cplus-dem.c	(working copy)
@@ -146,6 +146,7 @@
   int *proctypevec;     /* Indices of currently processed remembered typevecs.  */
   int proctypevec_size;
   int nproctypes;
+  unsigned int recursion_level;
 };
 
 #define PRINT_ANSI_QUALIFIERS (work -> options & DMGL_ANSI)
@@ -4693,10 +4694,21 @@
 demangle_nested_args (struct work_stuff *work, const char **mangled,
                       string *declp)
 {
+  static unsigned long recursion_level = 0;
   string* saved_previous_argument;
   int result;
   int saved_nrepeats;
 
+  if ((work->options & DMGL_RECURSE_LIMIT)
+      && work->recursion_level > DEMANGLE_RECURSION_LIMIT)
+    {
+      /* FIXME: There ought to be a way to report that the recursion limit
+	 has been reached.  */
+      return 0;
+    }
+
+  recursion_level ++;
+
   /* The G++ name-mangling algorithm does not remember types on nested
      argument lists, unless -fsquangling is used, and in that case the
      type vector updated by remember_type is not used.  So, we turn
@@ -4723,6 +4735,7 @@
   --work->forgetting_types;
   work->nrepeats = saved_nrepeats;
 
+  --recursion_level;
   return result;
 }
 
Index: include/demangle.h
===================================================================
--- include/demangle.h	(revision 266657)
+++ include/demangle.h	(working copy)
@@ -68,6 +68,16 @@
 /* If none of these are set, use 'current_demangling_style' as the default. */
 #define DMGL_STYLE_MASK (DMGL_AUTO|DMGL_GNU|DMGL_LUCID|DMGL_ARM|DMGL_HP|DMGL_EDG|DMGL_GNU_V3|DMGL_JAVA|DMGL_GNAT|DMGL_DLANG|DMGL_RUST)
 
+/* Enable a limit on the depth of recursion in mangled strings.
+   Note if this limit is not enabled then stack exhaustion is possible when
+   demangling pathologically complicated strings.  Bug reports about stack
+   exhaustion when the option is not enabled will be rejected.  */  
+#define DMGL_RECURSE_LIMIT (1 << 18)	
+
+/* If DMGL_RECURE_LIMIT is enabled, then this is the value
+   used as the maximum depth of recursion allowed.  */
+#define DEMANGLE_RECURSION_LIMIT 1024
+  
 /* Enumeration of possible demangling styles.
 
    Lucid and ARM styles are still kept logically distinct, even though

Reply via email to