Bug 61321 - demangler crash on casts in template parameters
Summary: demangler crash on casts in template parameters
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: other (show other bugs)
Version: unknown
: P3 normal
Target Milestone: ---
Assignee: Pedro Alves
URL:
Keywords:
: 63159 63244 63425 63465 65732 67261 (view as bug list)
Depends on:
Blocks:
 
Reported: 2014-05-26 14:03 UTC by Pedro Alves
Modified: 2019-06-15 00:38 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2014-05-26 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Pedro Alves 2014-05-26 14:03:01 UTC
The fix for bug 59195 (C++ demangler handles conversion operator incorrectly)
makes the demangler crash crash due to infinite recursion, in case of casts in template parameters.  For example:

 template<int> struct A {};
 template <typename Y> void function_temp(A<sizeof ((Y)(999))>) {}
 template void function_temp<int>(A<sizeof (int)>);

The function_temp<int> instantiation  mangles to:
  _Z13function_tempIiEv1AIXszcvT_Li999EEE

The demangler parses this as:

typed name
  template
    name 'function_temp'
    template argument list
      builtin type int
  function type
    builtin type void
    argument list
      template
        name 'A'
        template argument list
          unary operator
            operator sizeof 
            unary operator
              cast
                template parameter 0
              literal
                builtin type int
                name '999'

And after this patch, when printing the template argument list of A (what should be "<sizeof (int)>"), the template parameter 0 (that is, "T_") now refers to the first parameter of the the template argument list of the 'A' template, exactly what we were trying to print, while it should actually refer to the first parameter of the 'function_temp' template.  This leads to infinite recursion, and stack exaustion.

(This is particularly nasty for GDB, as it causes an immediate crash at startup.)
Comment 1 Pedro Alves 2014-05-26 15:33:07 UTC
I think we need to distinguish conversion operators from expression casts.  

Working on a patch that adds:

--- c/include/demangle.h
+++ w/include/demangle.h
@@ -373,6 +373,10 @@ enum demangle_component_type
   /* A typecast, represented as a unary operator.  The one subtree is
      the type to which the argument should be cast.  */
   DEMANGLE_COMPONENT_CAST,
+  /* A conversion operator, represented as a unary operator.  The one
+     subtree is the type to which the argument should be converted
+     to.  */
+  DEMANGLE_COMPONENT_CONVERSION,
Comment 2 Pedro Alves 2014-05-26 16:40:40 UTC
That worked.  Running bootstrap/tests.
Comment 4 Cary Coutant 2014-11-10 19:01:19 UTC
*** Bug 63425 has been marked as a duplicate of this bug. ***
Comment 5 Cary Coutant 2014-11-10 19:19:48 UTC
*** Bug 63244 has been marked as a duplicate of this bug. ***
Comment 6 Markus Trippelsdorf 2015-07-06 04:55:10 UTC
*** Bug 65732 has been marked as a duplicate of this bug. ***
Comment 7 Markus Trippelsdorf 2015-07-06 05:14:12 UTC
Pedro could you please ping your patch?
Comment 8 Pedro Alves 2015-07-14 09:39:31 UTC
The patch was meanwhile approved:

 https://gcc.gnu.org/ml/gcc-patches/2014-11/msg01247.html

I'm afraid I won't have time to get back to this for at least a few weeks.
If someone else could push it, I'd appreciated it.
Comment 9 Markus Trippelsdorf 2015-07-27 10:09:00 UTC
*** Bug 63465 has been marked as a duplicate of this bug. ***
Comment 10 Mikhail Maltsev 2015-08-18 23:11:36 UTC
*** Bug 67261 has been marked as a duplicate of this bug. ***
Comment 11 Markus Trippelsdorf 2015-11-17 12:53:44 UTC
Any update, Pedro?
Comment 12 Markus Trippelsdorf 2015-11-17 13:12:08 UTC
_ZSt7forwardIRKZN5Write14DataMapGrammarISt20back_insert_iteratorISsEEC4EvEUlRT_E_EOS5_RNSt16remove_referenceIS5_E4typeE

still recurses endless, even with your patch applied.
Comment 13 Ian Lance Taylor 2015-11-17 13:27:33 UTC
Was the symbol from comment #12, _ZSt7forwardIRKZN5Write14DataMapGrammarISt20back_insert_iteratorISsEEC4EvEUlRT_E_EOS5_RNSt16remove_referenceIS5_E4typeE, generated by g++ or clang?  That is, is it supposed to demangle?  If so, do you have the source code?
Comment 14 Markus Trippelsdorf 2015-11-17 13:31:15 UTC
It was first reported in PR68159. I've opened PR68383 for the issue.
The reporter is CCed in PR68383, so please ask him there directly.
Comment 15 Markus Trippelsdorf 2015-11-26 20:43:57 UTC
Yet another example:

    template <typename T> struct S {};
    template <typename T> S<decltype(*(T*)0)> f() {}
    int main() {f<int>();}

(with Ian's go demangler:)
markus@x4 tmp % c++filt_ _Z1fIiE1SIDTdecvPT_Li0EEEv
S<decltype (*((int*)(0)))> f<int>()

markus@x4 tmp % c++filt _Z1fIiE1SIDTdecvPT_Li0EEEv
[1]    7743 segmentation fault  c++filt _Z1fIiE1SIDTdecvPT_Li0EEEv
Comment 16 Markus Trippelsdorf 2015-11-27 14:48:54 UTC
Author: trippels
Date: Fri Nov 27 14:48:21 2015
New Revision: 231020

URL: https://gcc.gnu.org/viewcvs?rev=231020&root=gcc&view=rev
Log:
PR other/61321 - demangler crash on casts in template parameters

The fix for bug 59195:

 [C++ demangler handles conversion operator incorrectly]
 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59195

unfortunately makes the demangler crash due to infinite recursion, in
case of casts in template parameters.

For example, with:

 template<int> struct A {};
 template <typename Y> void function_temp(A<sizeof ((Y)(999))>) {}
 template void function_temp<int>(A<sizeof (int)>);

The 'function_temp<int>' instantiation above mangles to:

  _Z13function_tempIiEv1AIXszcvT_Li999EEE

The demangler parses this as:

typed name
  template
    name 'function_temp'
    template argument list
      builtin type int
  function type
    builtin type void
    argument list
      template                          (*)
        name 'A'
        template argument list
          unary operator
            operator sizeof
            unary operator
              cast
                template parameter 0    (**)
              literal
                builtin type int
                name '999'

And after the fix for 59195, due to:

 static void
 d_print_cast (struct d_print_info *dpi, int options,
	       const struct demangle_component *dc)
 {
 ...
   /* For a cast operator, we need the template parameters from
      the enclosing template in scope for processing the type.  */
   if (dpi->current_template != NULL)
     {
       dpt.next = dpi->templates;
       dpi->templates = &dpt;
       dpt.template_decl = dpi->current_template;
     }

when printing the template argument list of A (what should be "<sizeof
(int)>"), the template parameter 0 (that is, "T_", the '**' above) now
refers to the first parameter of the the template argument list of the
'A' template (the '*' above), exactly what we were already trying to
print.  This leads to infinite recursion, and stack exaustion.  The
template parameter 0 should actually refer to the first parameter of
the 'function_temp' template.

Where it reads "for the cast operator" in the comment in d_print_cast
(above), it's really talking about a conversion operator, like:

  struct A { template <typename U> explicit operator U(); };

We don't want to inject the template parameters from the enclosing
template in scope when processing a cast _expression_, only when
handling a conversion operator.

The problem is that DEMANGLE_COMPONENT_CAST is currently ambiguous,
and means _both_ 'conversion operator' and 'cast expression'.

Fix this by adding a new DEMANGLE_COMPONENT_CONVERSION component type,
which does what DEMANGLE_COMPONENT_CAST does today, and making
DEMANGLE_COMPONENT_CAST just simply print its component subtree.

I think we could instead reuse DEMANGLE_COMPONENT_CAST and in
d_print_comp_inner still do:

 @@ -5001,9 +5013,9 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
        d_print_comp (dpi, options, dc->u.s_extended_operator.name);
        return;

     case DEMANGLE_COMPONENT_CAST:
       d_append_string (dpi, "operator ");
 -     d_print_cast (dpi, options, dc);
 +     d_print_conversion (dpi, options, dc);
       return;

leaving the unary cast case below calling d_print_cast, but seems to
me that spliting the component types makes it easier to reason about
the code.

g++'s testsuite actually generates three symbols that crash the
demangler in the same way.  I've added those as tests in the demangler
testsuite as well.

And then this fixes PR other/61233 too, which happens to be a
demangler crash originally reported to GDB, at:
https://sourceware.org/bugzilla/show_bug.cgi?id=16957

Bootstrapped and regtested on x86_64 Fedora 20.

Also ran this through GDB's testsuite.  GDB will require a small
update to use DEMANGLE_COMPONENT_CONVERSION in one place it's using
DEMANGLE_COMPONENT_CAST in its sources.

libiberty/
2015-11-27  Pedro Alves  <palves@redhat.com>

        PR other/61321
        PR other/61233
        * demangle.h (enum demangle_component_type)
        <DEMANGLE_COMPONENT_CONVERSION>: New value.
        * cp-demangle.c (d_demangle_callback, d_make_comp): Handle
        DEMANGLE_COMPONENT_CONVERSION.
        (is_ctor_dtor_or_conversion): Handle DEMANGLE_COMPONENT_CONVERSION
        instead of DEMANGLE_COMPONENT_CAST.
        (d_operator_name): Return a DEMANGLE_COMPONENT_CONVERSION
        component if handling a conversion.
        (d_count_templates_scopes, d_print_comp_inner): Handle
        DEMANGLE_COMPONENT_CONVERSION.
        (d_print_comp_inner): Handle DEMANGLE_COMPONENT_CONVERSION instead
        of DEMANGLE_COMPONENT_CAST.
        (d_print_cast): Rename as ...
        (d_print_conversion): ... this.  Adjust comments.
        (d_print_cast): Rewrite - simply print the left subcomponent.
        * cp-demint.c (cplus_demangle_fill_component): Handle
        DEMANGLE_COMPONENT_CONVERSION.

        * testsuite/demangle-expected: Add tests.

Modified:
    trunk/include/demangle.h
    trunk/libiberty/ChangeLog
    trunk/libiberty/cp-demangle.c
    trunk/libiberty/cp-demint.c
    trunk/libiberty/testsuite/demangle-expected
Comment 17 Markus Trippelsdorf 2015-11-27 14:56:49 UTC
fixed.
Comment 18 Mikhail Maltsev 2015-11-28 01:19:07 UTC
*** Bug 63159 has been marked as a duplicate of this bug. ***
Comment 19 Jakub Jelinek 2016-05-19 09:55:33 UTC
Author: jakub
Date: Thu May 19 09:55:01 2016
New Revision: 236449

URL: https://gcc.gnu.org/viewcvs?rev=236449&root=gcc&view=rev
Log:
Move ChangeLog entry to the right file.
	PR other/61321
	PR other/61233
	* demangle.h (enum demangle_component_type)
	<DEMANGLE_COMPONENT_CONVERSION>: New value. 

Modified:
    trunk/include/ChangeLog
    trunk/libiberty/ChangeLog
Comment 20 Jakub Jelinek 2016-05-19 10:41:29 UTC
Author: jakub
Date: Thu May 19 10:40:57 2016
New Revision: 236451

URL: https://gcc.gnu.org/viewcvs?rev=236451&root=gcc&view=rev
Log:
	Backported from mainline
	2015-11-27  Pedro Alves  <palves@redhat.com>

	PR other/61321
	PR other/61233
	* demangle.h (enum demangle_component_type)
	<DEMANGLE_COMPONENT_CONVERSION>: New value.

	* cp-demangle.c (d_demangle_callback, d_make_comp): Handle
	DEMANGLE_COMPONENT_CONVERSION.
	(is_ctor_dtor_or_conversion): Handle DEMANGLE_COMPONENT_CONVERSION
	instead of DEMANGLE_COMPONENT_CAST.
	(d_operator_name): Return a DEMANGLE_COMPONENT_CONVERSION
	component if handling a conversion.
	(d_count_templates_scopes, d_print_comp_inner): Handle
	DEMANGLE_COMPONENT_CONVERSION.
	(d_print_comp_inner): Handle DEMANGLE_COMPONENT_CONVERSION instead
	of DEMANGLE_COMPONENT_CAST.
	(d_print_cast): Rename as ...
	(d_print_conversion): ... this.  Adjust comments.
	(d_print_cast): Rewrite - simply print the left subcomponent.
	* cp-demint.c (cplus_demangle_fill_component): Handle
	DEMANGLE_COMPONENT_CONVERSION.

	* testsuite/demangle-expected: Add tests.

Modified:
    branches/gcc-5-branch/include/ChangeLog
    branches/gcc-5-branch/include/demangle.h
    branches/gcc-5-branch/libiberty/ChangeLog
    branches/gcc-5-branch/libiberty/cp-demangle.c
    branches/gcc-5-branch/libiberty/cp-demint.c
    branches/gcc-5-branch/libiberty/testsuite/demangle-expected
Comment 21 Jakub Jelinek 2016-05-19 12:04:43 UTC
Author: jakub
Date: Thu May 19 12:04:11 2016
New Revision: 236455

URL: https://gcc.gnu.org/viewcvs?rev=236455&root=gcc&view=rev
Log:
	Backported from mainline
	2015-11-27  Pedro Alves  <palves@redhat.com>

	PR other/61321
	PR other/61233
	* demangle.h (enum demangle_component_type)
	<DEMANGLE_COMPONENT_CONVERSION>: New value.

	* cp-demangle.c (d_demangle_callback, d_make_comp): Handle
	DEMANGLE_COMPONENT_CONVERSION.
	(is_ctor_dtor_or_conversion): Handle DEMANGLE_COMPONENT_CONVERSION
	instead of DEMANGLE_COMPONENT_CAST.
	(d_operator_name): Return a DEMANGLE_COMPONENT_CONVERSION
	component if handling a conversion.
	(d_count_templates_scopes, d_print_comp_inner): Handle
	DEMANGLE_COMPONENT_CONVERSION.
	(d_print_comp_inner): Handle DEMANGLE_COMPONENT_CONVERSION instead
	of DEMANGLE_COMPONENT_CAST.
	(d_print_cast): Rename as ...
	(d_print_conversion): ... this.  Adjust comments.
	(d_print_cast): Rewrite - simply print the left subcomponent.
	* cp-demint.c (cplus_demangle_fill_component): Handle
	DEMANGLE_COMPONENT_CONVERSION.

	* testsuite/demangle-expected: Add tests.

Added:
    branches/gcc-4_9-branch/libiberty/testsuite/demangler-fuzzer.c
Modified:
    branches/gcc-4_9-branch/include/ChangeLog
    branches/gcc-4_9-branch/include/demangle.h
    branches/gcc-4_9-branch/libiberty/ChangeLog
    branches/gcc-4_9-branch/libiberty/cp-demangle.c
    branches/gcc-4_9-branch/libiberty/cp-demint.c
    branches/gcc-4_9-branch/libiberty/testsuite/demangle-expected