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