Bug 87335 - The stack overflow in function cplus_demangle_type in cp-demangle.c:2565 (c++filt -t)
Summary: The stack overflow in function cplus_demangle_type in cp-demangle.c:2565 (c++...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: unknown
: P3 normal
Target Milestone: 9.0
Assignee: Not yet assigned to anyone
URL:
Keywords: ice-on-invalid-code
Depends on:
Blocks:
 
Reported: 2018-09-17 14:56 UTC by Cheng Wen
Modified: 2020-05-08 07:35 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2018-09-18 00:00:00


Attachments
Stack_overflow_in_c++filt-t (56 bytes, text/plain)
2018-09-17 14:56 UTC, Cheng Wen
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Cheng Wen 2018-09-17 14:56:44 UTC
Created attachment 44706 [details]
Stack_overflow_in_c++filt-t

Hi,

We have found a stack overflow in function cplus_demangle_type in cp-demangle.c:2565 in c++filt of the latest binutils code base. 

Here is the POC file. Please use the “c++filt -t < $POC ” to reproduce the bug. Thank you very much.


Command:“c++filt -t < $POC ” (Please remember to use the option -t)

AddressSanitizer:DEADLYSIGNAL
=================================================================
==21814==ERROR: AddressSanitizer: stack-overflow on address 0x7ffcafaefbc0 (pc 0x0000008d3eb1 bp 0x7ffcafaf02d0 sp 0x7ffcafaefbc0 T0)
    #0 0x8d3eb0 in cplus_demangle_type /binutils-2.31/libiberty/./cp-demangle.c:2367
    #1 0x8d523c in cplus_demangle_type /binutils-2.31/libiberty/./cp-demangle.c:2565:5
    #2 0x8d523c in cplus_demangle_type /binutils-2.31/libiberty/./cp-demangle.c:2565:5
    #3 0x8d523c in cplus_demangle_type /binutils-2.31/libiberty/./cp-demangle.c:2565:5
    #4 0x8d523c in cplus_demangle_type /binutils-2.31/libiberty/./cp-demangle.c:2565:5
    #5 0x8d523c in cplus_demangle_type /binutils-2.31/libiberty/./cp-demangle.c:2565:5
    #6 0x8d523c in cplus_demangle_type /binutils-2.31/libiberty/./cp-demangle.c:2565:5
    #7 0x8d523c in cplus_demangle_type /binutils-2.31/libiberty/./cp-demangle.c:2565:5
    #8 0x8d523c in cplus_demangle_type /binutils-2.31/libiberty/./cp-demangle.c:2565:5
    #9 0x8d523c in cplus_demangle_type /binutils-2.31/libiberty/./cp-demangle.c:2565:5
    ...
    ...
    ...
    #246 0x8d523c in cplus_demangle_type /binutils-2.31/libiberty/./cp-demangle.c:2565:5
    #247 0x8d523c in cplus_demangle_type /binutils-2.31/libiberty/./cp-demangle.c:2565:5
    #248 0x8d523c in cplus_demangle_type /binutils-2.31/libiberty/./cp-demangle.c:2565:5
    #249 0x8d523c in cplus_demangle_type /binutils-2.31/libiberty/./cp-demangle.c:2565:5

SUMMARY: AddressSanitizer: stack-overflow /binutils-2.31/libiberty/./cp-demangle.c:2367 in cplus_demangle_type
==21814==ABORTING
Aborted
Comment 1 Martin Liška 2018-09-18 08:08:54 UTC
Is the input a valid C++ mangled name of not?
Comment 2 Cheng Wen 2018-09-18 08:46:58 UTC
(In reply to Martin Liška from comment #1)
> Is the input a valid C++ mangled name of not?

Hi,
This input is obtained through fuzzing technology. Our fuzzer get some test cases by mutating a valid input. This can not guarantee that this is a valid C++ mangled name. 

The program c++filt accepts the test case I uploaded. And this test case can prove that c++filt have problems. When program c++filt executing this input, a stack-overflow problem occurs. Please check this input and try to fix this bug if necessary.

Thank you very much.
Comment 3 Jonathan Wakely 2018-09-18 09:15:30 UTC
No, it's not a valid name. I can't reproduce a crash using the latest code from GCC though.
Comment 4 Jonathan Wakely 2018-09-18 09:50:10 UTC
Are you sure you attached the right file? When I try to demangle the attachment it doesn't crash, the __cxa_demangle file returns -2, meaning the name is not valid. That seems like the right result.
Comment 5 Cheng Wen 2018-09-18 09:56:47 UTC
(In reply to Jonathan Wakely from comment #4)
> Are you sure you attached the right file? When I try to demangle the
> attachment it doesn't crash, the __cxa_demangle file returns -2, meaning the
> name is not valid. That seems like the right result.

I have tried to reproduce this bug on different machines.
There are some questions to be confirmed.

(1) Do you use the latest version of binutils(binutils-2.32/binutils-2.31)? I downloaded the package from here.
https://www.gnu.org/software/binutils/

(2) Please confirm that you have used the option "-t".
The command should be "./c++filt -t < $POC"

(3) Do you confirm this POC with address sanitizer?
Comment 6 Jonathan Wakely 2018-09-18 10:23:35 UTC
(In reply to Cheng Wen from comment #5)
> (In reply to Jonathan Wakely from comment #4)
> > Are you sure you attached the right file? When I try to demangle the
> > attachment it doesn't crash, the __cxa_demangle file returns -2, meaning the
> > name is not valid. That seems like the right result.
> 
> I have tried to reproduce this bug on different machines.
> There are some questions to be confirmed.
> 
> (1) Do you use the latest version of binutils(binutils-2.32/binutils-2.31)?
> I downloaded the package from here.
> https://www.gnu.org/software/binutils/

Built from the binutils-gdb git repo:

$ /tmp/binutils/bin/c++filt -v
GNU c++filt (GNU Binutils) 2.31.51.20180918
Copyright (C) 2018 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) any later version.
This program has absolutely no warranty.


> (2) Please confirm that you have used the option "-t".
> The command should be "./c++filt -t < $POC"

$ /tmp/binutils/bin/c++filt -t < POC-t | wc 
      0       1   26539
$ echo $?
0


> (3) Do you confirm this POC with address sanitizer?

Yes it's linked to libasan

$ ldd /tmp/binutils/bin/c++filt 
        linux-vdso.so.1 (0x00007fff0618b000)
        libasan.so.4 => /lib64/libasan.so.4 (0x00007fc372241000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007fc37203d000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fc371c87000)
        librt.so.1 => /lib64/librt.so.1 (0x00007fc371a7f000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fc371861000)
        libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007fc3714d9000)
        libm.so.6 => /lib64/libm.so.6 (0x00007fc37118e000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fc370f77000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fc3731f9000)
Comment 7 Cheng Wen 2018-09-18 10:28:32 UTC
(In reply to Jonathan Wakely from comment #6)

Considering the memory size of different machines, maybe more 'P' is needed to trigger this bug in the input.
Comment 8 Jonathan Wakely 2018-09-18 10:34:17 UTC
It still works for up to ten million characters:

$ for i in `seq 1 10` ; do printf P ; done | /tmp/binutils/bin/c++filt -t ; echo
PPPPPPPPPP
$ for i in `seq 1 100` ; do printf P ; done | /tmp/binutils/bin/c++filt -t | wc 
      0       1     100
$ for i in `seq 1 1000` ; do printf P ; done | /tmp/binutils/bin/c++filt -t | wc 
      0       1    1000
$ for i in `seq 1 10000` ; do printf P ; done | /tmp/binutils/bin/c++filt -t | wc 
      0       1   10000
$ for i in `seq 1 100000` ; do printf P ; done | /tmp/binutils/bin/c++filt -t | wc 
      0       1  100000
$ for i in `seq 1 1000000` ; do printf P ; done | /tmp/binutils/bin/c++filt -t | wc 
      0       1 1000000
$ for i in `seq 1 10000000` ; do printf P ; done | /tmp/binutils/bin/c++filt -t | wc 
      0       1 10000000
Comment 9 Cheng Wen 2018-09-18 12:53:44 UTC
(In reply to Jonathan Wakely from comment #8)

Hi Jonathan,

I debugged with this POC again. I still think it's a problem. I will show you the debug process as follow.

> $ gdb ./c++filt
> Reading symbols from ./c++filt...done.
> (gdb) set args -t < POC-t
> (gdb) b cp-demangle.c:2565
> Breakpoint 1 at 0x8d5227: file ./cp-demangle.c, line 2565.
> (gdb) start
> (gdb) c
> Continuing.
> Breakpoint 1, cplus_demangle_type (di=0x7fffffffd560) at ./cp-demangle.c:2565
> 2565	cplus_demangle_type (di), NULL);
> (gdb) c
> Continuing.
> Breakpoint 1, cplus_demangle_type (di=0x7fffffffd560) at ./cp-demangle.c:2565
> 2565	cplus_demangle_type (di), NULL);
> ...
> ...
> ...
> (gdb) c
> Continuing.
> Breakpoint 1, cplus_demangle_type (di=0x7fffffffd560) at ./cp-demangle.c:2565
> 2565	cplus_demangle_type (di), NULL);
> (gdb) bt
> #0  cplus_demangle_type (di=0x7fffffffd560) at ./cp-demangle.c:2565
> #1  0x00000000008d523d in cplus_demangle_type (di=0x7fffffffd560) at ./cp-demangle.c:2565
> #2  0x00000000008d523d in cplus_demangle_type (di=0x7fffffffd560) at ./cp-demangle.c:2565
> #3  0x00000000008d523d in cplus_demangle_type (di=0x7fffffffd560) at ./cp-demangle.c:2565
> #4  0x00000000008d523d in cplus_demangle_type (di=0x7fffffffd560) at ./cp-demangle.c:2565
> ...
> ...
> ...
> #456 0x00000000008d523d in cplus_demangle_type (di=0x7fffffffd560) at ./cp-demangle.c:2565
> #457 0x00000000008d523d in cplus_demangle_type (di=0x7fffffffd560) at ./cp-demangle.c:2565
> #458 0x00000000008dd318 in d_demangle_callback (mangled=0x18b2e40 <main.mbuffer> 'P' <repeats 200 times>..., options=283,
>   callback=0x8dc110 <d_growable_string_callback_adapter>, opaque=0x7fffffffd860) at ./cp-demangle.c:6245
> #459 0x00000000008dc84f in d_demangle (mangled=0x18b2e40 <main.mbuffer> 'P' <repeats 200 times>..., options=283,
>   palc=0x7fffffffd9e0) at ./cp-demangle.c:6299
> #460 0x00000000008dc696 in cplus_demangle_v3 (mangled=0x18b2e40 <main.mbuffer> 'P' <repeats 200 times>..., options=283)
>   at ./cp-demangle.c:6456
> #461 0x00000000008b1cf4 in cplus_demangle (mangled=0x18b2e40 <main.mbuffer> 'P' <repeats 200 times>..., options=27)
>   at ./cplus-dem.c:880
> #462 0x0000000000517676 in demangle_it (mangled_name=0x18b2e40 <main.mbuffer> 'P' <repeats 200 times>...) at cxxfilt.c:62
> #463 0x000000000051726a in main (argc=2, argv=0x7fffffffe008) at cxxfilt.c:276


Using gdb to debug it. I set a breakpoint in cp-demangle.c:2565. After reaching this breakpoint for any time. You can see the stack backtrace.
This will consume a lot of stack memory.
(Caution: the command such as "gdb --args ./c++filt -t < $POC" is not valid. Please use "gdb ./c++filt", then "set args -t < $POC")

Thanks
Cheng Wen
Comment 10 Scott Gayou 2018-11-29 17:13:06 UTC
This reproducer seems to require adjusting the maximum stack size.

i.e.:

$ ulimit -s 8192 && c++filt < poc -t

does NOT crash

whereas:

$ ulimit -s 2048 && c++filt < poc -t
Segmentation fault (core dumped)

This looks to be another potentially duplicated CVE.

See the following:

CVE-2018-18484: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87636
CVE-2018-18701: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87675
CVE-2018-18700: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87681

All appear to be the same root cause.
Comment 11 Cheng Wen 2018-11-30 04:35:37 UTC
(In reply to Scott Gayou from comment #10)

> does NOT crash

That depends on your compilation options. Because stack memory is very small, generally only 1M to 2M. You can debug it with GDB and see the backtrace.

> This looks to be another potentially duplicated CVE.

Unlike several other errors, this error is to call itself. In addition, This problem was discovered earlier than those CVEs.

> All appear to be the same root cause.

Let's analyze the source code.

struct demangle_component * 
cplus_demangle_type (struct d_info *di) {
  switch (peek)
  {
    // ...
    case 'F':
      ret = d_function_type (di); break;
    // ...
    case 'P':
      ret = d_make_comp (di, DEMANGLE_COMPONENT_POINTER,
			 cplus_demangle_type (di), NULL);
      break;
    case 'C':
      ret = d_make_comp (di, DEMANGLE_COMPONENT_COMPLEX,
			 cplus_demangle_type (di), NULL);
      break;
    case 'G':
      ret = d_make_comp (di, DEMANGLE_COMPONENT_IMAGINARY,
			 cplus_demangle_type (di), NULL);
      break;
    // ...
  }
  // ...
}

Intuitively, in some cases, function cplus_demangle_type shows the behavior of recursive calls. When the function cplus_demangle_type receive character 'P'(The same as 'C' and 'G'), the cplus_demangle_type function making recursive calls to itself(Line 13, 18, 23). Another situation is that the function receive character 'F', then there's a recursed stack frame: cplus_demangle_type, d_bare_function_type, d_function_type(Line 8, 32, 39).

So different stack memory exhaustion can lead to stack memory exhaustion DoS. That depends on your compilation options. You can use my compilation options.

> CC=clang LDFLAGS="-ldl" CFLAGS="-DFORTIFY_SOURCE=2 -fstack-protector-all -fsanitize=undefined,address -fno-omit-frame-pointer -g -O0 -Wno-error" ./configure --disable-shared --disable-gdb --disable-libdecnumber --disable-sim

If you have any question, please let me know.
Comment 12 Nick Clifton 2018-12-07 10:34:03 UTC
Author: nickc
Date: Fri Dec  7 10:33:30 2018
New Revision: 266886

URL: https://gcc.gnu.org/viewcvs?rev=266886&root=gcc&view=rev
Log:
Add a recursion limit to libiberty's demangling code.  The limit is enabled by default, but can be disabled via a new demangling option.

include	* demangle.h (DMGL_NO_RECURSE_LIMIT): Define.
        (DEMANGLE_RECURSION_LIMIT): Define

	PR 87681
	PR 87675
	PR 87636
	PR 87350
	PR 87335
libiberty * cp-demangle.h (struct d_info): Add recursion_level field.
	* cp-demangle.c (d_function_type): Add recursion counter.
	If the recursion limit is reached and the check is not disabled,
	then return with a failure result.
	(cplus_demangle_init_info): Initialise the recursion_level field.
        (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.
	(squangle_mop_up): Set the numb and numk fields to zero.
	(work_stuff_copy_to_from): Handle the case where a btypevec or 
	ktypevec field is NULL.
	(demangle_nested_args): Add recursion counter.  If
	the recursion limit is not disabled and reached, return with a
	failure result.

Modified:
    trunk/include/ChangeLog
    trunk/include/demangle.h
    trunk/libiberty/ChangeLog
    trunk/libiberty/cp-demangle.c
    trunk/libiberty/cp-demangle.h
    trunk/libiberty/cplus-dem.c
Comment 13 Nick Clifton 2018-12-07 12:57:29 UTC
Fixed by commit 266886.
Comment 14 Trupti Pardeshi 2020-05-08 05:10:11 UTC
Could you please help to know which version of gcc has this fix?

Thank you in advance.

Best Regards,
Comment 15 Andrew Pinski 2020-05-08 06:00:12 UTC
(In reply to Trupti Pardeshi from comment #14)
> Could you please help to know which version of gcc has this fix?

GCC 9
Comment 16 Trupti Pardeshi 2020-05-08 06:21:28 UTC
(In reply to Andrew Pinski from comment #15)
> (In reply to Trupti Pardeshi from comment #14)
> > Could you please help to know which version of gcc has this fix?
> 
> GCC 9

Thank you Andrew for the reply. 

PS: Kind thought to ease the findings. When gcc new version is released incorporating selective defects, those defects shouldn't be updated back to mention gcc fixed in version. Thank you once again.