Bug 28131

Summary: Segfault in gdbpy_enter_varobj::gdbpy_enter_varobj when debugging in VS Code
Product: gdb Reporter: Andrey Sotnikov <usaonmonday>
Component: varobjAssignee: Tom Tromey <tromey>
Status: RESOLVED FIXED    
Severity: normal CC: simark, ssbssa, tromey
Priority: P2    
Version: HEAD   
Target Milestone: 11.1   
Host: Target:
Build: Last reconfirmed: 2021-07-30 00:00:00

Description Andrey Sotnikov 2021-07-24 20:01:22 UTC
Anything I debug in VS Code ends up in gdb exiting on segfault, if I try stepping through the code.

I compiled a debug version of gdb and got a core dump. Here is the backtrace:
#0  0x00007fca50787d22 in raise () from /usr/lib/libc.so.6
#1  0x00007fca50771862 in abort () from /usr/lib/libc.so.6
#2  0x000000000069a943 in handle_sigsegv (sig=11) at ../../binutils-gdb/gdb/event-top.c:893
#3  <signal handler called>
#4  gdbpy_enter_varobj::gdbpy_enter_varobj (this=0x7fffaef03e00, var=0x63f00b0) at ../../binutils-gdb/gdb/varobj.c:209
#5  0x00000000008a3687 in py_varobj_iter::~py_varobj_iter (this=0x64cb140, __in_chrg=<optimized out>) at ../../binutils-gdb/gdb/python/py-varobj.c:51
#6  0x00000000008a36ca in py_varobj_iter::~py_varobj_iter (this=0x64cb140, __in_chrg=<optimized out>) at ../../binutils-gdb/gdb/python/py-varobj.c:53
#7  0x0000000000a7f7c8 in std::default_delete<varobj_iter>::operator() (this=0x6839078, __ptr=0x64cb140)
    at /home/user/gcc-env/include/c++/11/bits/unique_ptr.h:85
#8  0x0000000000a7ea84 in std::unique_ptr<varobj_iter, std::default_delete<varobj_iter> >::~unique_ptr (this=0x6839078, __in_chrg=<optimized out>)
    at /home/user/gcc-env/include/c++/11/bits/unique_ptr.h:361
#9  0x0000000000a7e7b0 in varobj_dynamic::~varobj_dynamic (this=0x6839060, __in_chrg=<optimized out>) at ../../binutils-gdb/gdb/varobj.c:108
#10 0x0000000000a7d00b in varobj::~varobj (this=0x63f00b0, __in_chrg=<optimized out>) at ../../binutils-gdb/gdb/varobj.c:1850
#11 0x0000000000a7c91c in delete_variable_1 (delcountp=0x7fffaef03fec, var=0x63f00b0, only_children_p=false, remove_from_parent_p=true)
    at ../../binutils-gdb/gdb/varobj.c:1729
#12 0x0000000000a7c7a3 in delete_variable (var=0x63f00b0, only_children_p=false) at ../../binutils-gdb/gdb/varobj.c:1678
#13 0x0000000000a79a9e in varobj_delete (var=0x63f00b0, only_children=false) at ../../binutils-gdb/gdb/varobj.c:449
#14 0x00000000007e84b5 in mi_cmd_var_delete (command=0x6675d50 "var-delete", argv=0x6640520, argc=1) at ../../binutils-gdb/gdb/mi/mi-cmd-var.c:182
#15 0x000000000080319a in mi_cmd_execute (parse=0x63eb4d0) at ../../binutils-gdb/gdb/mi/mi-main.c:2083
#16 0x0000000000802664 in captured_mi_execute_command (uiout=0x25c8eb0, context=0x63eb4d0) at ../../binutils-gdb/gdb/mi/mi-main.c:1794
#17 0x0000000000802c3c in mi_execute_command (cmd=0x65c7200 "1221-var-delete var76", from_tty=1) at ../../binutils-gdb/gdb/mi/mi-main.c:1953
#18 0x00000000007ebd76 in mi_execute_command_wrapper (cmd=0x65c7200 "1221-var-delete var76") at ../../binutils-gdb/gdb/mi/mi-interp.c:275
#19 0x00000000007ebdff in mi_execute_command_input_handler (cmd=...) at ../../binutils-gdb/gdb/mi/mi-interp.c:304
#20 0x000000000069a88c in gdb_readline_no_editing_callback (client_data=0x242eb70) at ../../binutils-gdb/gdb/event-top.c:848
#21 0x000000000069a10d in stdin_event_handler (error=0, client_data=0x242eb70) at ../../binutils-gdb/gdb/event-top.c:515
#22 0x0000000000bde882 in handle_file_event (file_ptr=0x25a2580, ready_mask=1) at ../../binutils-gdb/gdbsupport/event-loop.cc:575
#23 0x0000000000bdee1d in gdb_wait_for_event (block=1) at ../../binutils-gdb/gdbsupport/event-loop.cc:701
#24 0x0000000000bddddb in gdb_do_one_event () at ../../binutils-gdb/gdbsupport/event-loop.cc:237
#25 0x00000000007c3745 in start_event_loop () at ../../binutils-gdb/gdb/main.c:421
#26 0x00000000007c3864 in captured_command_loop () at ../../binutils-gdb/gdb/main.c:481
#27 0x00000000007c50e3 in captured_main (data=0x7fffaef04520) at ../../binutils-gdb/gdb/main.c:1353
#28 0x00000000007c5149 in gdb_main (args=0x7fffaef04520) at ../../binutils-gdb/gdb/main.c:1368
#29 0x00000000004188bd in main (argc=3, argv=0x7fffaef04638) at ../../binutils-gdb/gdb/gdb.c:32

It seems like here
gdbpy_enter_varobj::gdbpy_enter_varobj (const struct varobj *var)
: gdbpy_enter (var->root->exp->gdbarch, var->root->exp->language_defn)
{
}

exp has already been deleted, which causes the segfault.
Comment 1 Andrey Sotnikov 2021-07-24 23:53:52 UTC
I ran a quick binary search and found the culprit commit:

SHA-1: 24fd95b4cd40f0108299c02f31f2d2a129f83769

* Change varobj_dynamic::child_iter to unique_ptr

This changes varobj_dynamic::child_iter to be a unique_ptr, removing
some manual management.

gdb/ChangeLog
2020-12-11  Tom Tromey  <tom@tromey.com>

	* varobj.c (struct varobj_dynamic) <child_iter>: Now unique_ptr.
	(varobj_get_iterator): Return unique_ptr.
	(update_dynamic_varobj_children, install_visualizer)
	(varobj::~varobj): Update.
	* python/python-internal.h (py_varobj_get_iterator): Return
	unique_ptr.
	* python/py-varobj.c (py_varobj_get_iterator): Return unique_ptr.


Adding Tom to CC.
Comment 2 Simon Marchi 2021-07-29 19:28:45 UTC
Does VSCode give you a way to see the MI traffic between it and GDB?  If so, could you share that?  It would make it easier to reproduce.  If you can give the source of the debugged program, even better.
Comment 3 Tom Tromey 2021-07-30 17:08:06 UTC
It took a little while but I figured out how to reproduce this today.
Comment 5 Andrey Sotnikov 2021-08-02 03:06:37 UTC
Thanks for taking care of it!
Comment 6 Sourceware Commits 2021-08-02 15:46:27 UTC
The master branch has been updated by Tom Tromey <tromey@sourceware.org>:

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

commit 4d0754c5f572b01cf2fe6c8ab292adba83331cbc
Author: Tom Tromey <tromey@adacore.com>
Date:   Fri Jul 30 11:18:36 2021 -0600

    Avoid crash in varobj deletion
    
    PR varobj/28131 points out a crash in the varobj deletion code.  It
    took a while to reproduce this, but essentially what happens is that a
    top-level varobj deletes its root object, then deletes the "dynamic"
    object.  However, deletion of the dynamic object may cause
    ~py_varobj_iter to run, which in turn uses gdbpy_enter_varobj:
    
    gdbpy_enter_varobj::gdbpy_enter_varobj (const struct varobj *var)
    : gdbpy_enter (var->root->exp->gdbarch, var->root->exp->language_defn)
    {
    }
    
    However, because var->root has already been destroyed, this is
    invalid.
    
    I've added a new test case.  This doesn't reliably crash, but the
    problem can easily be seen under valgrind (and, I presume, with ASAN,
    though I did not try this).
    
    Tested on x86-64 Fedora 32.  I also propose putting this on the GDB 11
    branch, with a suitable ChangeLog entry of course.
    
    Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28131
Comment 7 Sourceware Commits 2021-08-02 16:06:10 UTC
The gdb-11-branch branch has been updated by Tom Tromey <tromey@sourceware.org>:

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

commit 3e5ec878a43ac06624a2cc816a329d9b22b95cac
Author: Tom Tromey <tromey@adacore.com>
Date:   Fri Jul 30 11:18:36 2021 -0600

    Avoid crash in varobj deletion
    
    PR varobj/28131 points out a crash in the varobj deletion code.  It
    took a while to reproduce this, but essentially what happens is that a
    top-level varobj deletes its root object, then deletes the "dynamic"
    object.  However, deletion of the dynamic object may cause
    ~py_varobj_iter to run, which in turn uses gdbpy_enter_varobj:
    
    gdbpy_enter_varobj::gdbpy_enter_varobj (const struct varobj *var)
    : gdbpy_enter (var->root->exp->gdbarch, var->root->exp->language_defn)
    {
    }
    
    However, because var->root has already been destroyed, this is
    invalid.
    
    I've added a new test case.  This doesn't reliably crash, but the
    problem can easily be seen under valgrind (and, I presume, with ASAN,
    though I did not try this).
    
    Tested on x86-64 Fedora 32.  I also propose putting this on the GDB 11
    branch, with a suitable ChangeLog entry of course.
    
    Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28131
    
    (cherry picked from commit 4d0754c5f572b01cf2fe6c8ab292adba83331cbc)
    
    gdb/ChangeLog
    2021-08-02  Tom Tromey  <tromey@adacore.com>
    
            PR varobj/28131
            * varobj.c (~varobj): Delete 'dynamic' before 'root'.
    
    gdb/testsuite/ChangeLog
    2021-08-02  Tom Tromey  <tromey@adacore.com>
    
            PR varobj/28131
            * gdb.python/py-mi-var-info-path-expression.exp: Add regression
            test.
Comment 8 Tom Tromey 2021-08-02 16:06:54 UTC
Fixed.