Bug 29374

Summary: Internal-error when printing exception backtrace
Product: gdb Reporter: ks132 <ks132>
Component: backtraceAssignee: Not yet assigned to anyone <unassigned>
Status: RESOLVED FIXED    
Severity: normal CC: simark, simon.marchi
Priority: P2    
Version: 12.1   
Target Milestone: ---   
Host: Target:
Build: Last reconfirmed: 2022-07-24 00:00:00

Description ks132 2022-07-17 20:31:45 UTC
For this code:

#include <map>
#include <stdexcept>
#include <sstream>

class Handler
{
public:
   typedef int key_type;
   typedef std::map<key_type, int> map_type;

public:
   void operator()(const key_type& key) const
   {
      find(key);
   }

private:
   int find( const key_type& key ) const
   {
      typename map_type::const_iterator it = data.find(key);
      if(it == data.end())
      {
         std::ostringstream ost;
         throw std::runtime_error(ost.str());
      }

      return it->second;
   }

private:
   map_type data;
};

class State
{
public:
   void find( int type )
   {
      handlers( type );
   }

private:
   Handler  handlers;
};

int main()
{
   State state;
   state.find( 0 );
}

built with GCC 12.1.1 with optimization:
g++ -O1 -g repro.cpp

GDB crashes with internal-error:
$ gdb -batch -ex "catch throw" -ex r -ex bt a.out 
Catchpoint 1 (throw)
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Catchpoint 1 (exception thrown), 0x00007ffff7e256d1 in __cxa_throw () from /lib64/libstdc++.so.6
#0  0x00007ffff7e256d1 in __cxa_throw () from /lib64/libstdc++.so.6
#1  0x0000000000401430 in Handler::find (this=this@entry=0x7fffffffde90, ../../gdb/../gdbsupport/array-view.h:217: internal-error: copy: Assertion `dest.size () == src.size ()' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
----- Backtrace -----
0x55895414fe3f ???
0x558954589ab4 ???
0x558954589d50 ???
0x55895472cbf4 ???
0x5589541f729d ???
0x55895420f912 ???
0x558954214239 ???
0x55895421434a ???
0x55895421446a ???
0x5589544b2e88 ???
0x5589544b36d0 ???
0x5589544b80cc ???
0x5589544b9277 ???
0x558954187074 ???
0x5589545205b7 ???
0x55895433c5b1 ???
0x55895433c681 ???
0x55895433e613 ???
0x55895433f13e ???
0x55895407de6d ???
0x7f1fbff9754f ???
0x7f1fbff97608 ???
0x558954086744 ???
0xffffffffffffffff ???
---------------------

This is a bug, please report it.  For instructions, see:
<https://www.gnu.org/software/gdb/bugs/>.

Aborted (core dumped)
Comment 1 ks132 2022-07-20 20:15:38 UTC
Also reproduced in master

$ ~/build-gdb/gdb/gdb -batch -ex "catch throw" -ex r -ex bt a.out 
Catchpoint 1 (throw)
warning: Cannot parse .gnu_debugdata section; LZMA support was disabled at compile time
warning: Cannot parse .gnu_debugdata section; LZMA support was disabled at compile time
warning: Cannot parse .gnu_debugdata section; LZMA support was disabled at compile time
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Catchpoint 1 (exception thrown), 0x00007ffff7e256d1 in __cxa_throw () from /lib64/libstdc++.so.6
#0  0x00007ffff7e256d1 in __cxa_throw () from /lib64/libstdc++.so.6
#1  0x0000000000401430 in Handler::find (this=this@entry=0x7fffffffde90, ../../binutils-gdb/gdb/../gdbsupport/array-view.h:217: internal-error: copy: Assertion `dest.size () == src.size ()' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
----- Backtrace -----
0x4b9b62 gdb_internal_backtrace_1
	../../binutils-gdb/gdb/bt-utils.c:122
0x4b9b62 _Z22gdb_internal_backtracev
	../../binutils-gdb/gdb/bt-utils.c:168
0x7db474 internal_vproblem
	../../binutils-gdb/gdb/utils.c:396
0x7db6b8 _Z15internal_verrorPKciS0_P13__va_list_tag
	../../binutils-gdb/gdb/utils.c:476
0x8ef221 _Z14internal_errorPKciS0_z
	../../binutils-gdb/gdbsupport/errors.cc:55
0x54d2e1 _ZN3gdb4copyIKhhEEvNS_10array_viewIT_EENS2_IT0_EE
	../../binutils-gdb/gdb/../gdbsupport/array-view.h:217
0x54d2e1 _ZN3gdb4copyIKhhEEvNS_10array_viewIT_EENS2_IT0_EE
	../../binutils-gdb/gdb/../gdbsupport/array-view.h:215
0x54d2e1 _ZN18dwarf_expr_context12fetch_resultEP4typeS1_lb
	../../binutils-gdb/gdb/dwarf2/expr.c:1039
0x56398c dwarf2_evaluate_loc_desc_full
	../../binutils-gdb/gdb/dwarf2/loc.c:1519
0x56603e _Z24dwarf2_evaluate_loc_descP4typeP10frame_infoPKhmP18dwarf2_per_cu_dataP18dwarf2_per_objfileb
	../../binutils-gdb/gdb/dwarf2/loc.c:1563
0x56603e dwarf_entry_parameter_to_value
	../../binutils-gdb/gdb/dwarf2/loc.c:1269
0x56603e value_of_dwarf_reg_entry
	../../binutils-gdb/gdb/dwarf2/loc.c:1366
0x56612c value_of_dwarf_block_entry
	../../binutils-gdb/gdb/dwarf2/loc.c:1399
0x566217 loclist_read_variable_at_entry
	../../binutils-gdb/gdb/dwarf2/loc.c:3920
0x736648 _Z14read_frame_argRK19frame_print_optionsP6symbolP10frame_infoP9frame_argS7_
	../../binutils-gdb/gdb/stack.c:560
0x736dee print_frame_args
	../../binutils-gdb/gdb/stack.c:888
0x738611 print_frame
	../../binutils-gdb/gdb/stack.c:1391
0x738611 _Z16print_frame_infoRK19frame_print_optionsP10frame_infoi10print_whatii
	../../binutils-gdb/gdb/stack.c:1117
0x739dcf backtrace_command_1
	../../binutils-gdb/gdb/stack.c:2070
0x739dcf backtrace_command
	../../binutils-gdb/gdb/stack.c:2189
0x4e9514 _Z8cmd_funcP16cmd_list_elementPKci
	../../binutils-gdb/gdb/cli/cli-decode.c:2516
0x795b7a _Z15execute_commandPKci
	../../binutils-gdb/gdb/top.c:699
0x667de1 catch_command_errors
	../../binutils-gdb/gdb/main.c:513
0x667eaf execute_cmdargs
	../../binutils-gdb/gdb/main.c:608
0x669c4c captured_main_1
	../../binutils-gdb/gdb/main.c:1298
0x66a73a captured_main
	../../binutils-gdb/gdb/main.c:1319
0x66a73a _Z8gdb_mainP18captured_main_args
	../../binutils-gdb/gdb/main.c:1344
0x428bf4 main
	../../binutils-gdb/gdb/gdb.c:32
---------------------

This is a bug, please report it.  For instructions, see:
<https://www.gnu.org/software/gdb/bugs/>.

Aborted (core dumped)
Comment 2 ks132 2022-07-23 13:53:03 UTC
Found the first bad commit with git bisect
# first bad commit: [4bce7cdaf481901edbc5ee47d953ea7e8efb56ca] gdbsupport: add array_view copy function
Comment 3 Simon Marchi 2022-07-24 01:55:37 UTC
Thanks for the reproducer, I am able to reproduce.  And thanks for the bisection.  The patch you found added some additional checks, and it looks like it caught a pre-existing problem.

Here:

https://gitlab.com/gnutools/binutils-gdb/-/blob/6577f365ebdee7dda71cb996efa29d3714cbccd0/gdb/dwarf2/expr.c#L1027

We try to get the length of subobj_type, but it is a typedef whose actual size hasn't been computed yet:

(top-gdb) p subobj_type.main_type.name
$1 = 0x6210001ef820 "Handler::key_type"
(top-gdb) p subobj_type.main_type.code
$2 = TYPE_CODE_TYPEDEF
(top-gdb) p subobj_type.length 
$3 = 0

If I add a check_typedef at the beginning of fetch_result, it looks like it works:

From 13d2d8b935f22ac2345c76a69ba009e583f9dc50 Mon Sep 17 00:00:00 2001
From: Simon Marchi <simon.marchi@polymtl.ca>
Date: Sat, 23 Jul 2022 21:41:55 -0400
Subject: [PATCH] patch

Change-Id: I182733ad08e34df40d8bcc47af72c482fabf4900
---
 gdb/dwarf2/expr.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 592dbe19d562..d2b7a131de33 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -930,6 +930,11 @@ dwarf_expr_context::fetch_result (struct type *type, struct type *subobj_type,
   if (subobj_type == nullptr)
     subobj_type = type;
 
+  /* Ensure that, if TYPE or SUBOBJ_TYPE are typedefs, their length is filled
+     in instead of being zero.  */
+  check_typedef (type);
+  check_typedef (subobj_type);
+
   if (this->m_pieces.size () > 0)
     {
       ULONGEST bit_size = 0;

base-commit: 4bce7cdaf481901edbc5ee47d953ea7e8efb56ca
-- 
2.37.1

$./gdb -nx --data-directory=data-directory -q -batch -ex "catch throw" -ex r -ex bt a.out 
Catchpoint 1 (throw)

This GDB supports auto-downloading debuginfo from the following URLs:
https://debuginfod.archlinux.org
Enable debuginfod for this session? (y or [n]) [answered N; input not from terminal]
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/../lib/libthread_db.so.1".

Catchpoint 1 (exception thrown), 0x00007ffff7ca5e91 in __cxxabiv1::__cxa_throw (obj=0x55555556af30, tinfo=0x555555557d20 <typeinfo for std::runtime_error@GLIBCXX_3.4>, dest=0x7ffff7cbd370 <std::runtime_error::~runtime_error()>) at /usr/src/debug/gcc/libstdc++-v3/libsupc++/eh_throw.cc:81
81      /usr/src/debug/gcc/libstdc++-v3/libsupc++/eh_throw.cc: No such file or directory.
#0  0x00007ffff7ca5e91 in __cxxabiv1::__cxa_throw (obj=0x55555556af30, tinfo=0x555555557d20 <typeinfo for std::runtime_error@GLIBCXX_3.4>, dest=0x7ffff7cbd370 <std::runtime_error::~runtime_error()>) at /usr/src/debug/gcc/libstdc++-v3/libsupc++/eh_throw.cc:81
#1  0x00005555555554a9 in Handler::find (this=this@entry=0x7fffffffdb20, key=key@entry=@0x7fffffffdb1c: 0) at /usr/include/c++/12.1.0/bits/new_allocator.h:90
#2  0x000055555555526e in Handler::operator() (key=@0x7fffffffdb1c: 0, this=0x7fffffffdb20) at repro.cpp:14
#3  State::find (type=<optimized out>, this=0x7fffffffdb20) at repro.cpp:39
#4  main () at repro.cpp:49
Comment 4 Simon Marchi 2022-07-24 04:00:20 UTC
Sent patch here: https://sourceware.org/pipermail/gdb-patches/2022-July/191035.html

I don't have a test though.
Comment 5 Simon Marchi 2022-07-28 02:34:38 UTC
New version with a test: https://sourceware.org/pipermail/gdb-patches/2022-July/191117.html
Comment 6 Sourceware Commits 2022-08-18 16:13:35 UTC
The master branch has been updated by Simon Marchi <simark@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=bde195b84a862f31ac111c0881ad13b89ee89492

commit bde195b84a862f31ac111c0881ad13b89ee89492
Author: Simon Marchi <simon.marchi@polymtl.ca>
Date:   Wed Jul 27 21:34:22 2022 -0400

    gdb: call check_typedef at beginning of dwarf_expr_context::fetch_result
    
    Bug 29374 shows this crash:
    
        $ ./gdb -nx --data-directory=data-directory -q -batch -ex "catch throw" -ex r -ex bt a.out
        ...
        /home/simark/src/binutils-gdb/gdb/../gdbsupport/array-view.h:217: internal-error: copy: Assertion `dest.size () == src.size ()' failed.
    
    The backtrace is:
    
        #0  internal_error (file=0x5555606504c0 "/home/simark/src/binutils-gdb/gdb/../gdbsupport/array-view.h", line=217, fmt=0x55556064b700 "%s: Assertion `%s' failed.") at /home/simark/src/binutils-gdb/gdbsupport/errors.cc:51
        #1  0x000055555d41c0bb in gdb::copy<unsigned char const, unsigned char> (src=..., dest=...) at /home/simark/src/binutils-gdb/gdb/../gdbsupport/array-view.h:217
        #2  0x000055555deef28c in dwarf_expr_context::fetch_result (this=0x7fffffffb830, type=0x621007a86830, subobj_type=0x621007a86830, subobj_offset=0, as_lval=false) at /home/simark/src/binutils-gdb/gdb/dwarf2/expr.c:1040
        #3  0x000055555def0015 in dwarf_expr_context::evaluate (this=0x7fffffffb830, addr=0x62f00004313e "0", len=1, as_lval=false, per_cu=0x60b000069550, frame=0x621007c9e910, addr_info=0x0, type=0x621007a86830, subobj_type=0x621007a86830, subobj_offset=0) at /home/simark/src/binutils-gdb/gdb/dwarf2/expr.c:1091
        #4  0x000055555e084327 in dwarf2_evaluate_loc_desc_full (type=0x621007a86830, frame=0x621007c9e910, data=0x62f00004313e "0", size=1, per_cu=0x60b000069550, per_objfile=0x613000006080, subobj_type=0x621007a86830, subobj_byte_offset=0, as_lval=false) at /home/simark/src/binutils-gdb/gdb/dwarf2/loc.c:1485
        #5  0x000055555e0849e2 in dwarf2_evaluate_loc_desc (type=0x621007a86830, frame=0x621007c9e910, data=0x62f00004313e "0", size=1, per_cu=0x60b000069550, per_objfile=0x613000006080, as_lval=false) at /home/simark/src/binutils-gdb/gdb/dwarf2/loc.c:1529
        #6  0x000055555e0828c6 in dwarf_entry_parameter_to_value (parameter=0x621007a96e58, deref_size=0x0, type=0x621007a86830, caller_frame=0x621007c9e910, per_cu=0x60b000069550, per_objfile=0x613000006080) at /home/simark/src/binutils-gdb/gdb/dwarf2/loc.c:1235
        #7  0x000055555e082f55 in value_of_dwarf_reg_entry (type=0x621007a86890, frame=0x621007acc510, kind=CALL_SITE_PARAMETER_DWARF_REG, kind_u=...) at /home/simark/src/binutils-gdb/gdb/dwarf2/loc.c:1332
        #8  0x000055555e083449 in value_of_dwarf_block_entry (type=0x621007a86890, frame=0x621007acc510, block=0x61e000033568 "T\004\205\001\240\004\004\243\001T\237\004\240\004\261\004\001T\004\261\004\304\005\004\243\001T\237\004\304\005\310\005\001T\004\310\005\311\005\004\243\001T\237", block_len=1) at /home/simark/src/binutils-gdb/gdb/dwarf2/loc.c:1365
        #9  0x000055555e094d40 in loclist_read_variable_at_entry (symbol=0x621007a99bd0, frame=0x621007acc510) at /home/simark/src/binutils-gdb/gdb/dwarf2/loc.c:3889
        #10 0x000055555f5192e0 in read_frame_arg (fp_opts=..., sym=0x621007a99bd0, frame=0x621007acc510, argp=0x7fffffffbf20, entryargp=0x7fffffffbf60) at /home/simark/src/binutils-gdb/gdb/stack.c:559
        #11 0x000055555f51c352 in print_frame_args (fp_opts=..., func=0x621007a99ad0, frame=0x621007acc510, num=-1, stream=0x6030000bad90) at /home/simark/src/binutils-gdb/gdb/stack.c:887
        #12 0x000055555f521919 in print_frame (fp_opts=..., frame=0x621007acc510, print_level=1, print_what=LOCATION, print_args=1, sal=...) at /home/simark/src/binutils-gdb/gdb/stack.c:1390
        #13 0x000055555f51f22e in print_frame_info (fp_opts=..., frame=0x621007acc510, print_level=1, print_what=LOCATION, print_args=1, set_current_sal=0) at /home/simark/src/binutils-gdb/gdb/stack.c:1116
        #14 0x000055555f526c6d in backtrace_command_1 (fp_opts=..., bt_opts=..., count_exp=0x0, from_tty=0) at /home/simark/src/binutils-gdb/gdb/stack.c:2079
        #15 0x000055555f527ae5 in backtrace_command (arg=0x0, from_tty=0) at /home/simark/src/binutils-gdb/gdb/stack.c:2198
    
    The problem is that the type that gets passed down to
    dwarf_expr_context::fetch_result (the type of a variable of which we're
    trying to read the entry value) is a typedef whose size has never been
    computed yet (check_typedef has never been called on it).  As we get in
    the DWARF_VALUE_STACK case (line 1028 of dwarf2/expr.c), the `len`
    variable is therefore set to 0, instead of the actual type length.  We
    then call allocate_value on subobj_type, which does call check_typedef,
    so the length of the typedef gets filled in at that point.  We end up
    passing to the copy function a source array view of length 0 and a
    target array view of length 4, and the assertion fails.
    
    Fix this by calling check_typedef on both type and subobj_type at the
    beginning of fetch_result.
    
    I tried writing a test for this using the DWARF assembler, but I haven't
    succeeded.  It's possible that we need to get into this specific code
    path (value_of_dwarf_reg_entry and all) to manage to get to
    dwarf_expr_context::fetch_result with a typedef type that has never been
    resolved.  In all my attempts, the typedef would always be resolved
    already, so the bug wouldn't show up.
    
    As a fallback, I made a gdb.dwarf2 test with compiler-generated .S
    files.  I don't particularly like those, but I think it's better than no
    test.  The .cpp source code is the smallest reproducer I am able to make
    from the reproducer given in the bug (thanks to Pedro for suggestions on
    how to minimize it further than I had).  Since I tested on both amd64
    and aarch64, I added versions of the test for these two architectures.
    
    Change-Id: I182733ad08e34df40d8bcc47af72c482fabf4900
    Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=29374
Comment 7 Simon Marchi 2022-08-18 17:52:46 UTC
Fixed.