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
Is the input a valid C++ mangled name of not?
(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.
No, it's not a valid name. I can't reproduce a crash using the latest code from GCC though.
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.
(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?
(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)
(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.
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
(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
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.
(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.
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
Fixed by commit 266886.
Could you please help to know which version of gcc has this fix? Thank you in advance. Best Regards,
(In reply to Trupti Pardeshi from comment #14) > Could you please help to know which version of gcc has this fix? GCC 9
(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.