Bug 28856 - Python pretty printer causes stack overflow when printing frame arguments
Summary: Python pretty printer causes stack overflow when printing frame arguments
Status: RESOLVED FIXED
Alias: None
Product: gdb
Classification: Unclassified
Component: python (show other bugs)
Version: HEAD
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
: 17437 24393 (view as bug list)
Depends on:
Blocks:
 
Reported: 2022-02-03 11:45 UTC by Jonathan Wakely
Modified: 2024-01-13 18:50 UTC (History)
7 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments
Test case (2.23 KB, application/x-compressed-tar)
2022-03-24 14:28 UTC, Simon Marchi
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Jonathan Wakely 2022-02-03 11:45:24 UTC
Compile one C++ source file, as follows:

struct category
{
  virtual const char* name() const = 0;
};

struct cat : category
{
  const char* name() const { return "miaow"; }
};

category* getcat() { static cat c; return &c; }

struct error_code
{
  error_code(int i = 0) : m_value(i), m_cat(getcat()) { }

  void assign(int i) { m_value = i; }
  int value() const { return m_value; }

  int m_value;
  category* m_cat;
};

int f(error_code& ec)
{
  ec.assign(1);
  return ec.value();
}

int g(error_code& ec)
{
  return f(ec);
}

int main()
{
  error_code ec;
  return g(ec);
}



Add one Python file:

class ErrorCodePrinter:
    "print an error_code"

    def __init__ (self, val):
        self.val = val

    @staticmethod
    def _category_name(cat):
        "Call the virtual function that overrides category::name()"
        gdb.set_convenience_variable('__cat', cat)
        return gdb.parse_and_eval('$__cat->name()').string()

    def to_string (self):
        value = self.val['m_value']
        category = self._category_name(self.val['m_cat'])
        return 'error_code = {"%s": %d}' % (category, value)

def ec_lookup_function(val):
    typ = val.type
    if typ.code == gdb.TYPE_CODE_REF:
        typ = typ.target()
    if str(typ) == 'error_code':
        return ErrorCodePrinter(val)
    return None

gdb.pretty_printers.append(ec_lookup_function)




Prepare an executable:

g++ -g ec.cc -o ec

And bake in the oven until it catches fire:

gdb -q -iex "add-auto-load-safe-path $PWD/ec-gdb.py" -ex start -ex n -ex 'p &ec' -ex step -ex step -ex n -ex up -ex up  ec


Reading symbols from ec...
Temporary breakpoint 1 at 0x40115e: file ec.C, line 37.
Starting program: /tmp/ec 

Temporary breakpoint 1, main () at ec.C:37
37        error_code ec;
38        return g(ec);
$1 = (error_code *) 0x7fffffffd790
g (ec=error_code = {"miaow": 0}) at ec.C:32
32        return f(ec);
f (ec=error_code = {"miaow": 0}) at ec.C:26
26        ec.assign(1);
27        return ec.value();
#1  0x0000000000401154 in g (ec=

Fatal signal: Segmentation fault
----- Backtrace -----
0x4bcd33 gdb_internal_backtrace_1
        /home/jwakely/src/binutils-gdb/gdb/bt-utils.c:121
0x4bcd33 _Z22gdb_internal_backtracev
        /home/jwakely/src/binutils-gdb/gdb/bt-utils.c:164
0x5b32b1 handle_fatal_signal
        /home/jwakely/src/binutils-gdb/gdb/event-top.c:896
0x5b33f4 handle_sigsegv
        /home/jwakely/src/binutils-gdb/gdb/event-top.c:969
0x7f8d6d648a1f ???
        ../sysdeps/unix/sysv/linux/sigaction.c:665
0x4b7ba5 _ZN22scoped_debug_start_endC2ERbPKcS2_S2_S2_S2_z
        /home/jwakely/src/binutils-gdb/gdb/../gdbsupport/common-debug.h:108
0x5cb846 _Z26frame_unwind_find_by_frameP10frame_infoPPv
        /home/jwakely/src/binutils-gdb/gdb/frame-unwind.c:181
0x5ce738 _Z17frame_unwind_archP10frame_info
        /home/jwakely/src/binutils-gdb/gdb/frame.c:2896
0x5cb879 _Z26frame_unwind_find_by_frameP10frame_infoPPv
        /home/jwakely/src/binutils-gdb/gdb/frame-unwind.c:184
0x5ce738 _Z17frame_unwind_archP10frame_info
        /home/jwakely/src/binutils-gdb/gdb/frame.c:2896
0x5cb879 _Z26frame_unwind_find_by_frameP10frame_infoPPv
        /home/jwakely/src/binutils-gdb/gdb/frame-unwind.c:184
0x5ce738 _Z17frame_unwind_archP10frame_info
        /home/jwakely/src/binutils-gdb/gdb/frame.c:2896
0x5cb879 _Z26frame_unwind_find_by_frameP10frame_infoPPv
        /home/jwakely/src/binutils-gdb/gdb/frame-unwind.c:184
0x5ce738 _Z17frame_unwind_archP10frame_info
        /home/jwakely/src/binutils-gdb/gdb/frame.c:2896
0x5cb879 _Z26frame_unwind_find_by_frameP10frame_infoPPv
        /home/jwakely/src/binutils-gdb/gdb/frame-unwind.c:184


[... several thousand more frames ...]
0x5ce738 _Z17frame_unwind_archP10frame_info
        /home/jwakely/src/binutils-gdb/gdb/frame.c:2896
0x5cb879 _Z26frame_unwind_find_by_frameP10frame_infoPPv
        /home/jwakely/src/binutils-gdb/gdb/frame-unwind.c:184
0x5ce738 _Z17frame_unwind_archP10frame_info
        /home/jwakely/src/binutils-gdb/gdb/frame.c:2896
0x5cb879 _Z26frame_unwind_find_by_frameP10frame_infoPPv
        /home/jwakely/src/binutils-gdb/gdb/frame-unwind.c:184
0x5ce738 _Z17frame_unwind_archP10frame_info
        /home/jwakely/src/binutils-gdb/gdb/frame.c:2896
0x5ce784 frame_unwind_pc
        /home/jwakely/src/binutils-gdb/gdb/frame.c:944
0x5ce8cb _Z12get_frame_pcP10frame_info
        /home/jwakely/src/binutils-gdb/gdb/frame.c:2571
0x5ce8cb _Z26get_frame_address_in_blockP10frame_info
        /home/jwakely/src/binutils-gdb/gdb/frame.c:2601
0x55727c dwarf2_frame_cache
        /home/jwakely/src/binutils-gdb/gdb/dwarf2/frame.c:903
0x558303 dwarf2_frame_prev_register
        /home/jwakely/src/binutils-gdb/gdb/dwarf2/frame.c:1136
0x5cee9b _Z27frame_unwind_register_valueP10frame_infoi
        /home/jwakely/src/binutils-gdb/gdb/frame.c:1233
0x5cf23a _Z21frame_register_unwindP10frame_infoiPiS1_P9lval_typePmS1_Ph
        /home/jwakely/src/binutils-gdb/gdb/frame.c:1143
0x5cf5c8 _Z21frame_unwind_registerP10frame_infoiPh
        /home/jwakely/src/binutils-gdb/gdb/frame.c:1199
0x5fe4e0 i386_unwind_pc
        /home/jwakely/src/binutils-gdb/gdb/i386-tdep.c:1970
0x5ce78f frame_unwind_pc
        /home/jwakely/src/binutils-gdb/gdb/frame.c:948
0x5ce869 _Z25get_frame_pc_if_availableP10frame_infoPm
        /home/jwakely/src/binutils-gdb/gdb/frame.c:2582
0x761b40 _Z16print_frame_infoRK19frame_print_optionsP10frame_infoi10print_whatii
        /home/jwakely/src/binutils-gdb/gdb/stack.c:1185
0x76246b _Z17print_stack_frameP10frame_infoi10print_whati
        /home/jwakely/src/binutils-gdb/gdb/stack.c:366
0x762502 _Z26print_stack_frame_to_uioutP6ui_outP10frame_infoi10print_whati
        /home/jwakely/src/binutils-gdb/gdb/stack.c:345
0x4eff7a cli_on_user_selected_context_changed
        /home/jwakely/src/binutils-gdb/gdb/cli/cli-interp.c:277
0x764463 _ZNKSt8functionIFv10enum_flagsI23user_selected_what_flagEEEclES2_
        /usr/include/c++/11/bits/std_function.h:560
0x764463 _ZNK3gdb9observers10observableIJ10enum_flagsI23user_selected_what_flagEEE6notifyES4_
        /home/jwakely/src/binutils-gdb/gdb/../gdbsupport/observable.h:150
0x764463 up_command
        /home/jwakely/src/binutils-gdb/gdb/stack.c:2690
0x4ee2a4 _Z8cmd_funcP16cmd_list_elementPKci
        /home/jwakely/src/binutils-gdb/gdb/cli/cli-decode.c:2459
0x7bdbe1 _Z15execute_commandPKci
        /home/jwakely/src/binutils-gdb/gdb/top.c:670
0x66dcd2 catch_command_errors
        /home/jwakely/src/binutils-gdb/gdb/main.c:523
0x66dd9f execute_cmdargs
        /home/jwakely/src/binutils-gdb/gdb/main.c:618
0x66f4b4 captured_main_1
        /home/jwakely/src/binutils-gdb/gdb/main.c:1317
0x66ff5a captured_main
        /home/jwakely/src/binutils-gdb/gdb/main.c:1338
0x66ff5a _Z8gdb_mainP18captured_main_args
        /home/jwakely/src/binutils-gdb/gdb/main.c:1363
0x431624 main
        /home/jwakely/src/binutils-gdb/gdb/gdb.c:32
---------------------
A fatal error internal to GDB has been detected, further
debugging is not possible.  GDB will now terminate.

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

Segmentation fault (core dumped)
Comment 1 Hannes Domani 2022-02-03 19:01:52 UTC
On Windows I get a use-after-free of a frame_info pointer.

It happens because the target function call gdb.parse_and_eval('$__cat->name()') leads to reinit_frame_cache(), and print_frame_info() continues on with that now stale 'frame' pointer.


Full stack traces:

> unhandled exception code: 0xC0000005 (ACCESS_VIOLATION)
>   exception on: '1 [4648]'
>     0x000000013F350000   C:\gdb\build64\gdb-git-python\gdb\gdb.exe
>       0x000000013F4A5687   C:\src\repos\binutils-gdb.git\gdb\frame.c:2545:3 [get_frame_pc_if_available(frame_info*, unsigned long long*)]
>       0x000000013F5F7E57   C:\src\repos\binutils-gdb.git\gdb\stack.c:1201:37 [print_frame_info(frame_print_options const&, frame_info*, int, print_what, int, int)]
>       0x000000013F5F887F   C:\src\repos\binutils-gdb.git\gdb\stack.c:366:24 [print_stack_frame(frame_info*, int, print_what, int)]
>       0x000000013F4F5A97   C:\src\repos\binutils-gdb.git\gdb\infrun.c:8420:23 [print_stop_location]
>                            C:\src\repos\binutils-gdb.git\gdb\infrun.c:8436:25 [print_stop_event(ui_out*, bool)]
>       0x000000013F6630BD   C:\src\repos\binutils-gdb.git\gdb\tui\tui-interp.c:99:19 [tui_on_normal_stop]
>       0x000000013F4F6F4F   c:\msys64\mingw64\x86_64-w64-mingw32\include\c++\11.2.0\bits\std_function.h:560:9 [std::function<void (bpstats*, int)>::operator()(bpstats*, int) const]
>                            c:\src\repos\binutils-gdb.git\gdbsupport\observable.h:150:9 [gdb::observers::observable<bpstats*, int>::notify(bpstats*, int) const]
>                            C:\src\repos\binutils-gdb.git\gdb\infrun.c:8705:40 [normal_stop()]
>       0x000000013F502B42   C:\src\repos\binutils-gdb.git\gdb\infrun.c:4157:29 [fetch_inferior_event()]
>       0x000000013F3885FA   C:\src\repos\binutils-gdb.git\gdb\async-event.c:335:31 [check_async_event_handlers()]
>       0x000000013F795901   C:\src\repos\binutils-gdb.git\gdbsupport\event-loop.cc:216:37 [gdb_do_one_event()]
>       0x000000013F64CD5B   C:\src\repos\binutils-gdb.git\gdb\top.c:529:27 [wait_sync_command_done()]
>       0x000000013F64D650   C:\src\repos\binutils-gdb.git\gdb\top.c:546:28 [maybe_wait_sync_command_done(int)]
>                            C:\src\repos\binutils-gdb.git\gdb\top.c:687:36 [execute_command(char const*, int)]
>       0x000000013F5213BB   C:\src\repos\binutils-gdb.git\gdb\main.c:523:15 [catch_command_errors]
>       0x000000013F5214E5   C:\src\repos\binutils-gdb.git\gdb\main.c:618:30 [execute_cmdargs]
>       0x000000013F524693   C:\src\repos\binutils-gdb.git\gdb\main.c:1322:19 [captured_main_1]
>       0x000000013F5250FC   C:\src\repos\binutils-gdb.git\gdb\main.c:1343:19 [captured_main]
>                            C:\src\repos\binutils-gdb.git\gdb\main.c:1368:21 [gdb_main(captured_main_args*)]
>       0x000000013FA3C146   C:\src\repos\binutils-gdb.git\gdb\gdb.c:32:19 [main]
>       0x000000013F351430   C:\gcc\src\mingw-w64-v8.0.2\mingw-w64-crt\crt\crtexe.c:345:15 [__tmainCRTStartup]
>       0x000000013F3515B5   C:\gcc\src\mingw-w64-v8.0.2\mingw-w64-crt\crt\crtexe.c:220:9 [mainCRTStartup]
>   read access violation at 0x000000039C0B0190
>   freed block 0x000000039C0B0020 (size 4064, offset +368)
>   allocated on: (#180516) '1 [4648]'
>                            [malloc]
>     0x000000013F350000   C:\gdb\build64\gdb-git-python\gdb\gdb.exe
>       0x000000013F37AEFB   C:\src\repos\binutils-gdb.git\gdb\alloc.c:60:16 [xmalloc]
>       0x000000013F7B8E34   C:\src\repos\binutils-gdb.git\libiberty\obstack.c:94:12 [call_chunkfun]
>                            C:\src\repos\binutils-gdb.git\libiberty\obstack.c:141:37 [_obstack_begin_worker]
>       0x000000013F4A504B   C:\src\repos\binutils-gdb.git\gdb\frame.c:2000:3 [reinit_frame_cache()]
>       0x000000013F4FF614   C:\src\repos\binutils-gdb.git\gdb\infrun.c:6021:18 [handle_signal_stop]
>       0x000000013F5012A0   C:\src\repos\binutils-gdb.git\gdb\infrun.c:4500:26 [handle_stop_requested]
>                            C:\src\repos\binutils-gdb.git\gdb\infrun.c:4494:1 [handle_stop_requested]
>                            C:\src\repos\binutils-gdb.git\gdb\infrun.c:5765:33 [handle_inferior_event]
>       0x000000013F502A44   C:\src\repos\binutils-gdb.git\gdb\infrun.c:4121:27 [fetch_inferior_event()]
>       0x000000013F3885FA   C:\src\repos\binutils-gdb.git\gdb\async-event.c:335:31 [check_async_event_handlers()]
>       0x000000013F795901   C:\src\repos\binutils-gdb.git\gdbsupport\event-loop.cc:216:37 [gdb_do_one_event()]
>       0x000000013F64CD5B   C:\src\repos\binutils-gdb.git\gdb\top.c:529:27 [wait_sync_command_done()]
>       0x000000013F64D650   C:\src\repos\binutils-gdb.git\gdb\top.c:546:28 [maybe_wait_sync_command_done(int)]
>                            C:\src\repos\binutils-gdb.git\gdb\top.c:687:36 [execute_command(char const*, int)]
>       0x000000013F5213BB   C:\src\repos\binutils-gdb.git\gdb\main.c:523:15 [catch_command_errors]
>       0x000000013F5214E5   C:\src\repos\binutils-gdb.git\gdb\main.c:618:30 [execute_cmdargs]
>       0x000000013F524693   C:\src\repos\binutils-gdb.git\gdb\main.c:1322:19 [captured_main_1]
>       0x000000013F5250FC   C:\src\repos\binutils-gdb.git\gdb\main.c:1343:19 [captured_main]
>                            C:\src\repos\binutils-gdb.git\gdb\main.c:1368:21 [gdb_main(captured_main_args*)]
>       0x000000013FA3C146   C:\src\repos\binutils-gdb.git\gdb\gdb.c:32:19 [main]
>       0x000000013F351430   C:\gcc\src\mingw-w64-v8.0.2\mingw-w64-crt\crt\crtexe.c:345:15 [__tmainCRTStartup]
>       0x000000013F3515B5   C:\gcc\src\mingw-w64-v8.0.2\mingw-w64-crt\crt\crtexe.c:220:9 [mainCRTStartup]
>   freed on: '1 [4648]'
>                            [free]
>     0x000000013F350000   C:\gdb\build64\gdb-git-python\gdb\gdb.exe
>       0x000000013F7B9051   C:\src\repos\binutils-gdb.git\libiberty\obstack.c:103:5 [call_freefun]
>                            C:\src\repos\binutils-gdb.git\libiberty\obstack.c:280:7 [_obstack_free]
>       0x000000013F4A502B   C:\src\repos\binutils-gdb.git\gdb\frame.c:1999:3 [reinit_frame_cache()]
>       0x000000013F4FAE4A   C:\src\repos\binutils-gdb.git\gdb\infrun.c:3130:25 [proceed(unsigned long long, gdb_signal)]
>       0x000000013F4E4C9B   C:\src\repos\binutils-gdb.git\gdb\infcall.c:611:15 [run_inferior_call]
>                            C:\src\repos\binutils-gdb.git\gdb\infcall.c:1277:27 [call_function_by_hand_dummy(value*, type*, gdb::array_view<value*>, void (*)(void*, int), void*)]
>       0x000000013F4E606A   C:\src\repos\binutils-gdb.git\gdb\infcall.c:742:38 [call_function_by_hand(value*, type*, gdb::array_view<value*>)]
>       0x000000013F489AE9   C:\src\repos\binutils-gdb.git\gdb\eval.c:674:36 [evaluate_subexp_do_call(expression*, noside, value*, gdb::array_view<value*>, char const*, type*)]
>       0x000000013F48CEFA   C:\src\repos\binutils-gdb.git\gdb\eval.c:966:34 [expr::structop_base_operation::evaluate_funcall(type*, expression*, noside, std::vector<std::unique_ptr<expr::operation, std::default_delete<expr::operation> >, std::allocator<std::unique_ptr<expr::operation, std::default_delete<expr::operation> > > > const&)]
>       0x000000013F920A29   C:\src\repos\binutils-gdb.git\gdb\expop.h:2178:54 [expr::funcall_operation::evaluate(type*, expression*, noside)]
>       0x000000013F48891B   C:\src\repos\binutils-gdb.git\gdb\eval.c:101:39 [expression::evaluate(type*, noside)]
>       0x000000013F488BB9   C:\src\repos\binutils-gdb.git\gdb\eval.c:115:24 [evaluate_expression(expression*, type*)]
>                            C:\src\repos\binutils-gdb.git\gdb\eval.c:74:30 [parse_and_eval(char const*)]
>       0x000000013F59B899   C:\src\repos\binutils-gdb.git\gdb\python\python.c:945:31 [gdbpy_parse_and_eval]
>     0x0000000069E90000   c:\gdb\gdb-libs64\Python27\python27.dll
>       0x0000000069F88EA8   [PyCFunction_Call]
>       0x0000000069FEDFA7   [PyEval_GetFuncDesc]
>       0x0000000069FEB49A   [PyEval_EvalFrameEx]
>       0x0000000069FEE177   [PyEval_GetFuncDesc]
>       0x0000000069FEE02E   [PyEval_GetFuncDesc]
>       0x0000000069FEB49A   [PyEval_EvalFrameEx]
>       0x0000000069FECA10   [PyEval_EvalCodeEx]
>       0x0000000069F77037   [PyFunction_SetClosure]
>       0x0000000069F42D22   [PyObject_Call]
>       0x0000000069F575D8   [PyMethod_New]
>       0x0000000069F42D22   [PyObject_Call]
>       0x0000000069F4359B   [PyObject_CallMethodObjArgs]
>     0x000000013F350000   C:\gdb\build64\gdb-git-python\gdb\gdb.exe
>       0x000000013F589B6E   C:\src\repos\binutils-gdb.git\gdb\python\py-prettyprint.c:200:17 [pretty_print_one_value]
>       0x000000013F589CB5   C:\src\repos\binutils-gdb.git\gdb\python\py-prettyprint.c:286:69 [print_string_repr]
>                            C:\src\repos\binutils-gdb.git\gdb\python\py-prettyprint.c:636:36 [gdbpy_apply_pretty_printer]
>       0x000000013F58A934   C:\src\repos\binutils-gdb.git\gdb\python\py-prettyprint.c:620:36 [gdbpy_apply_val_pretty_printer(extension_language_defn const*, value*, ui_file*, int, value_print_options const*, language_defn const*)]
>       0x000000013F493319   C:\src\repos\binutils-gdb.git\gdb\extension.c:488:51 [apply_ext_lang_val_pretty_printer(value*, ui_file*, int, value_print_options const*, language_defn const*)]
>       0x000000013F68B755   C:\src\repos\binutils-gdb.git\gdb\valprint.c:1028:47 [do_val_print]
>       0x000000013F5F5A06   C:\src\repos\binutils-gdb.git\gdb\stack.c:489:33 [print_frame_arg]
>       0x000000013F5F680C   C:\src\repos\binutils-gdb.git\gdb\stack.c:893:22 [print_frame_args]
>       0x000000013F5F82E0   C:\src\repos\binutils-gdb.git\gdb\stack.c:1407:25 [print_frame]
>                            C:\src\repos\binutils-gdb.git\gdb\stack.c:1124:17 [print_frame_info(frame_print_options const&, frame_info*, int, print_what, int, int)]
>       0x000000013F5F887F   C:\src\repos\binutils-gdb.git\gdb\stack.c:366:24 [print_stack_frame(frame_info*, int, print_what, int)]
>       0x000000013F4F5A97   C:\src\repos\binutils-gdb.git\gdb\infrun.c:8420:23 [print_stop_location]
>                            C:\src\repos\binutils-gdb.git\gdb\infrun.c:8436:25 [print_stop_event(ui_out*, bool)]
>       0x000000013F6630BD   C:\src\repos\binutils-gdb.git\gdb\tui\tui-interp.c:99:19 [tui_on_normal_stop]
>       0x000000013F4F6F4F   c:\msys64\mingw64\x86_64-w64-mingw32\include\c++\11.2.0\bits\std_function.h:560:9 [std::function<void (bpstats*, int)>::operator()(bpstats*, int) const]
>                            c:\src\repos\binutils-gdb.git\gdbsupport\observable.h:150:9 [gdb::observers::observable<bpstats*, int>::notify(bpstats*, int) const]
>                            C:\src\repos\binutils-gdb.git\gdb\infrun.c:8705:40 [normal_stop()]
>       0x000000013F502B42   C:\src\repos\binutils-gdb.git\gdb\infrun.c:4157:29 [fetch_inferior_event()]
>       0x000000013F3885FA   C:\src\repos\binutils-gdb.git\gdb\async-event.c:335:31 [check_async_event_handlers()]
>       0x000000013F795901   C:\src\repos\binutils-gdb.git\gdbsupport\event-loop.cc:216:37 [gdb_do_one_event()]
>       0x000000013F64CD5B   C:\src\repos\binutils-gdb.git\gdb\top.c:529:27 [wait_sync_command_done()]
>       0x000000013F64D650   C:\src\repos\binutils-gdb.git\gdb\top.c:546:28 [maybe_wait_sync_command_done(int)]
>                            C:\src\repos\binutils-gdb.git\gdb\top.c:687:36 [execute_command(char const*, int)]
>       0x000000013F5213BB   C:\src\repos\binutils-gdb.git\gdb\main.c:523:15 [catch_command_errors]
>       0x000000013F5214E5   C:\src\repos\binutils-gdb.git\gdb\main.c:618:30 [execute_cmdargs]
>       0x000000013F524693   C:\src\repos\binutils-gdb.git\gdb\main.c:1322:19 [captured_main_1]
>       0x000000013F5250FC   C:\src\repos\binutils-gdb.git\gdb\main.c:1343:19 [captured_main]
>                            C:\src\repos\binutils-gdb.git\gdb\main.c:1368:21 [gdb_main(captured_main_args*)]
>       0x000000013FA3C146   C:\src\repos\binutils-gdb.git\gdb\gdb.c:32:19 [main]
>       0x000000013F351430   C:\gcc\src\mingw-w64-v8.0.2\mingw-w64-crt\crt\crtexe.c:345:15 [__tmainCRTStartup]
>       0x000000013F3515B5   C:\gcc\src\mingw-w64-v8.0.2\mingw-w64-crt\crt\crtexe.c:220:9 [mainCRTStartup]
Comment 2 Jonathan Wakely 2022-02-03 20:50:31 UTC
Should I not be even attempting to use gdb.parse_and_eval('$__cat->name()') like that in a printer? Is there a better way to call a virtual member function given a pointer to a polymorphic object?
Comment 3 Hannes Domani 2022-02-03 21:15:26 UTC
(In reply to Jonathan Wakely from comment #2)
> Should I not be even attempting to use gdb.parse_and_eval('$__cat->name()')
> like that in a printer? Is there a better way to call a virtual member
> function given a pointer to a polymorphic object?

If you need to call it, then I don't think there is a better way.

I would only use a function call in a pretty printer if there is no way to avoid it, even more so because it wouldn't work for core files.
Comment 4 Simon Marchi 2022-02-03 21:29:48 UTC
(In reply to Jonathan Wakely from comment #2)
> Should I not be even attempting to use gdb.parse_and_eval('$__cat->name()')
> like that in a printer? Is there a better way to call a virtual member
> function given a pointer to a polymorphic object?

I agree with Hannes: you should ideally not call a function in the pretty printer so that it works with core files.  But it's still a GDB bug, if someone chooses to do it, it should work.
Comment 5 Jonathan Wakely 2022-02-03 22:19:16 UTC
OK, thanks. I suppose I could just use the class name instead of calling the name() member. I think my preference would be to try to call the function, and if that returns a char* then use that, and if it fails (e.g. because there's no inferior process, just a core) then use the class name. But of course to gracefully find out if calling the function is possible, it needs to not crash :-)

For the std:: error categories I can actually avoid calling the name() function, because GDB can look up the address of the private symbol and compare the _M_cat pointer to that.

gdb.parse_and_eval('_ZN12_GLOBAL__N_124system_category_instanceE.obj').address

But for user-defined custom categories, I'd like to be able to print the name.

I'll see what I can do to avoid calling the function. Thanks for the guidance.
Comment 6 Guinevere Larsen 2022-02-07 17:51:38 UTC
I have managed to reproduce the crash without using the pretty printer. In fact, all I need to reproduce is:

gdb -q ec -ex start -ex n -ex s -ex bt

and it crashes using backtrace, up or finish. What I found while trying to debug this is that there is a corruption in the linked list, creating the following entry:

1: *this_frame = {level = 0, pspace = 0x0, aspace = 0x26b6d00, prologue_cache = 0x0, unwind = 0x0, prev_arch = {p = false, arch = 0x0}, prev_pc = {status = CC_UNKNOWN, masked = false, value = 0x0}, prev_func = {addr = 0x40113c, status = CC_VALUE}, this_id = {p = frame_id_status::COMPUTED, value = {stack_addr = 0x0, code_addr = 0x0, special_addr = 0x0, stack_status = FID_STACK_SENTINEL, code_addr_p = 0, special_addr_p = 1, artificial_depth = 0}}, base = 0x0, base_cache = 0x0, next = 0x288fee0, prev_p = true, prev = 0x288ffb0, stop_reason = UNWIND_NO_REASON, stop_string = 0x0}

(top-gdb) p this_frame
$2 = (frame_info *) 0x288fee0

I haven't tracked down where this is happening, but my guess is somewhere during the step process.
Comment 7 Sourceware Commits 2022-03-21 13:23:08 UTC
The master branch has been updated by Bruno Larsen <blarsen@sourceware.org>:

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

commit daaf7acf47a12d10459060dca5500b63273cd683
Author: Bruno Larsen <blarsen@redhat.com>
Date:   Tue Feb 22 11:44:44 2022 -0300

    [gdb/testsuite] test a function call by hand from pretty printer
    
    The test case added here is testing the bug gdb/28856, where calling a
    function by hand from a pretty printer makes GDB crash. There are 6
    mechanisms to trigger this crash in the current test, using the commands
    backtrace, up, down, finish, step and continue. Since the failure happens
    because of use-after-free (more details below) the tests will always
    have a chance of passing through sheer luck, but anecdotally they seem
    to fail all of the time.
    
    The reason GDB is crashing is a use-after-free problem. The above
    mentioned functions save a pointer to the current frame's information,
    then calls the pretty printer, and uses the saved pointer for different
    reasons, depending on the function. The issue happens because
    call_function_by_hand needs to reset the obstack to get the current
    frame, invalidating the saved pointer.
Comment 8 Simon Marchi 2022-03-24 14:28:57 UTC
Created attachment 14034 [details]
Test case

This test case used to be in the tree (with some setup_kfails), but it still causes problems when GDB is built with ASan.  I removed it from the tree and am attaching it here, so it can be merged back when we have a corresponding fix.
Comment 9 Sourceware Commits 2022-10-10 10:14:04 UTC
The master branch has been updated by Bruno Larsen <blarsen@sourceware.org>:

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

commit c29a6445a981cee5e8eebe3617ef5c049fd3409f
Author: Bruno Larsen <blarsen@redhat.com>
Date:   Mon Jul 25 14:06:37 2022 -0300

    gdb/frame: Add reinflation method for frame_info_ptr
    
    Currently, despite having a smart pointer for frame_infos, GDB may
    attempt to use an invalidated frame_info_ptr, which would cause internal
    errors to happen.  One such example has been documented as PR
    python/28856, that happened when printing frame arguments calls an
    inferior function.
    
    To avoid failures, the smart wrapper was changed to also cache the frame
    id, so the pointer can be reinflated later.  For this to work, the
    frame-id stuff had to be moved to their own .h file, which is included
    by frame-info.h.
    
    Frame_id caching is done explicitly using the prepare_reinflate method.
    Caching is done manually so that only the pointers that need to be saved
    will be, and reinflating has to be done manually using the reinflate
    method because the get method and the -> operator must not change
    the internals of the class.  Finally, attempting to reinflate when the
    pointer is being invalidated causes the following assertion errors:
    
    check_ptrace_stopped_lwp_gone: assertion `lp->stopped` failed.
    get_frame_pc: Assertion `frame->next != NULL` failed.
    
    As for performance concerns, my personal testing with `time make
    chec-perf GDB_PERFTEST_MODE=run` showed an actual reduction of around
    10% of time running.
    
    This commit also adds a testcase that exercises the python/28856 bug with
    7 different triggers, run, continue, step, backtrace, finish, up and down.
    Some of them can seem to be testing the same thing twice, but since this
    test relies on stale pointers, there is always a chance that GDB got lucky
    when testing, so better to test extra.
    
    Regression tested on x86_64, using both gcc and clang.
    
    Approved-by: Tom Tomey <tom@tromey.com>
Comment 10 Guinevere Larsen 2022-10-10 10:23:14 UTC
The commit mentioned by cvs-commit fixes this issue.
Comment 11 Tom Tromey 2023-08-03 15:59:13 UTC
*** Bug 24393 has been marked as a duplicate of this bug. ***
Comment 12 Hannes Domani 2024-01-13 18:50:38 UTC
*** Bug 17437 has been marked as a duplicate of this bug. ***