binutils-gdb/gdb/python
Andrew Burgess cb6e6bb89d gdb/python: fix memory leak in python inferior code
When a user creates a gdb.Inferior object for the first time a new
Python object is created.  This object is then cached within GDB's
inferior object using the registry mechanism (see
inferior_to_inferior_object in py-inferior.c, specifically the calls
to inferior_data and set_inferior_data).

The Python Reference to the gdb.Inferior object held within the real
inferior object ensures that the reference count on the Python
gdb.Inferior object never reaches zero while the GDB inferior object
continues to exist.

At the same time, the gdb.Inferior object maintains a C++ pointer back
to GDB's real inferior object.  We therefore end up with a system that
looks like this:

                   Python Reference
                         |
                         |
    .----------.         |          .--------------.
    |          |------------------->|              |
    | inferior |                    | gdb.Inferior |
    |          |<-------------------|              |
    '----------'         |          '--------------'
                         |
                         |
                    C++ Pointer

When GDB's inferior object is deleted (say the inferior exits) then
py_free_inferior is called (thanks to the registry system), this
function looks up the Python gdb.Inferior object and sets the C++
pointer to nullptr and finally reduces the reference count on the
Python gdb.Inferior object.

If at this point the user still holds a reference to the Python
gdb.Inferior object then nothing happens.  However, the gdb.Inferior
object is now in the non-valid state (see infpy_is_valid in
py-inferior.c), but otherwise, everything is fine.

However, if there are no further references to the Python gdb.Inferior
object, or, once the user has given up all their references to the
gdb.Inferior object, then infpy_dealloc is called.

This function currently checks to see if the inferior pointer within
the gdb.Inferior object is nullptr or not.  If the pointer is nullptr
then infpy_dealloc immediately returns.

Only when the inferior point in the gdb.Inferior is not nullptr do
we (a) set the gdb.Inferior reference inside GDB's inferior to
nullptr, and (b) call the underlying Python tp_free function.

There are a number things wrong here:

  1.  The Python gdb.Inferior reference within GDB's inferior object
  holds a reference count, thus, setting this reference to nullptr
  without first decrementing the reference count would leak a
  reference, however...

  2. As GDB's inferior holds a reference then infpy_dealloc will never
  be called until GDB's inferior object is deleted.  Deleting a GDB
  inferior ohject calls py_free_inferior, and so gives up the
  reference.  At this point there is no longer a need to call
  set_inferior_data to set the field back to NULL, that field must
  have been cleared in order to get the reference count to zero, which
  means...

  3. If we know that py_free_inferior must be called before
  infpy_dealloc, then we know that the inferior pointer in
  gdb.Inferior will always be nullptr when infpy_dealloc is called,
  this means that the call to the underlying tp_free function will
  always be skipped.  Skipping this call will cause Python to leak the
  memory associated with the gdb.Inferior object, which is what we
  currently always do.

Given all of the above, I assert that the C++ pointer within
gdb.Inferior will always be nullptr when infpy_dealloc is called.
That's what this patch does.

I wrote a test for this issue making use of Pythons tracemalloc
module, which allows us to spot this memory leak.
2021-10-05 14:26:17 +01:00
..
lib/gdb gdb/python: fix a few flake8 warnings 2021-10-02 08:33:28 -04:00
py-all-events.def gdb/python: add a new gdb_exiting event 2021-10-05 10:05:40 +01:00
py-arch.c gdb: delay python initialisation until gdbpy_finish_initialization 2021-04-28 09:56:20 +01:00
py-auto-load.c gdb: remove unnecessary lookup_cmd when deprecating commands 2021-05-27 14:00:07 -04:00
py-block.c gdb/python: remove all uses of Py_TPFLAGS_HAVE_ITER 2021-09-09 09:50:38 +01:00
py-bpevent.c
py-breakpoint.c gdb/python: allow for catchpoint type breakpoints in python 2021-06-25 18:22:07 +01:00
py-cmd.c gdb: remove cmd_list_element::function::sfunc 2021-07-23 15:38:54 -04:00
py-continueevent.c
py-event-types.def gdb/python: add a new gdb_exiting event 2021-10-05 10:05:40 +01:00
py-event.c
py-event.h
py-events.h
py-evtregistry.c
py-evts.c
py-exitedevent.c
py-finishbreakpoint.c gdb: remove iterate_over_breakpoints function 2021-05-27 14:58:37 -04:00
py-frame.c gdb/python: add PendingFrame.level and Frame.level methods 2021-06-21 16:20:08 +01:00
py-framefilter.c Remove uses of fprintf_symbol_filtered 2021-08-02 10:48:29 -06:00
py-function.c
py-gdb-readline.c
py-inferior.c gdb/python: fix memory leak in python inferior code 2021-10-05 14:26:17 +01:00
py-infevents.c
py-infthread.c gdb: change thread_info::name to unique_xmalloc_ptr, add helper function 2021-09-24 17:25:55 -04:00
py-instruction.c
py-instruction.h
py-lazy-string.c
py-linetable.c gdb/python: remove all uses of Py_TPFLAGS_HAVE_ITER 2021-09-09 09:50:38 +01:00
py-newobjfileevent.c
py-objfile.c gdb: delay python initialisation until gdbpy_finish_initialization 2021-04-28 09:56:20 +01:00
py-param.c gdb: make string-like set show commands use std::string variable 2021-10-03 17:53:16 +01:00
py-prettyprint.c gdb: fix some indentation issues 2021-05-27 15:01:28 -04:00
py-progspace.c gdb: delay python initialisation until gdbpy_finish_initialization 2021-04-28 09:56:20 +01:00
py-record-btrace.c
py-record-btrace.h
py-record-full.c
py-record-full.h
py-record.c
py-record.h
py-ref.h
py-registers.c gdb/python: remove all uses of Py_TPFLAGS_HAVE_ITER 2021-09-09 09:50:38 +01:00
py-signalevent.c
py-stopevent.c
py-stopevent.h
py-symbol.c Restore gdb.SYMBOL_LABEL_DOMAIN constant 2021-06-03 14:56:55 +02:00
py-symtab.c gdb: delay python initialisation until gdbpy_finish_initialization 2021-04-28 09:56:20 +01:00
py-threadevent.c
py-tui.c Forward mouse click to python TUI window 2021-06-04 16:18:10 +02:00
py-type.c gdb: remove TYPE_FIELD_NAME and FIELD_NAME macros 2021-09-30 22:05:57 -04:00
py-unwind.c gdb: add names to unwinders, add debug messages when looking for unwinder 2021-06-29 12:05:03 -04:00
py-utils.c
py-value.c Change pointer_type to a method of struct type 2021-09-23 15:11:00 -06:00
py-varobj.c
py-xmethods.c
python-config.py gdb: re-format Python files using black 21.4b0 2021-05-07 10:56:20 -04:00
python-internal.h gdb: Introduce setting construct within cmd_list_element 2021-10-03 17:53:16 +01:00
python.c gdb/python: add a new gdb_exiting event 2021-10-05 10:05:40 +01:00
python.h