)]}'
{
  "commit": "cb6e6bb89d5bfff3651b1e2a4e2d856d16b006db",
  "tree": "db4134de5b0db4d0684bf077a537d069a06f8c5e",
  "parents": [
    "a5ea23036d8a85d2ef133458a4c54a339857c152"
  ],
  "author": {
    "name": "Andrew Burgess",
    "email": "andrew.burgess@embecosm.com",
    "time": "Fri Sep 03 09:23:35 2021 +0100"
  },
  "committer": {
    "name": "Andrew Burgess",
    "email": "andrew.burgess@embecosm.com",
    "time": "Tue Oct 05 14:26:17 2021 +0100"
  },
  "message": "gdb/python: fix memory leak in python inferior code\n\nWhen a user creates a gdb.Inferior object for the first time a new\nPython object is created.  This object is then cached within GDB\u0027s\ninferior object using the registry mechanism (see\ninferior_to_inferior_object in py-inferior.c, specifically the calls\nto inferior_data and set_inferior_data).\n\nThe Python Reference to the gdb.Inferior object held within the real\ninferior object ensures that the reference count on the Python\ngdb.Inferior object never reaches zero while the GDB inferior object\ncontinues to exist.\n\nAt the same time, the gdb.Inferior object maintains a C++ pointer back\nto GDB\u0027s real inferior object.  We therefore end up with a system that\nlooks like this:\n\n                   Python Reference\n                         |\n                         |\n    .----------.         |          .--------------.\n    |          |-------------------\u003e|              |\n    | inferior |                    | gdb.Inferior |\n    |          |\u003c-------------------|              |\n    \u0027----------\u0027         |          \u0027--------------\u0027\n                         |\n                         |\n                    C++ Pointer\n\nWhen GDB\u0027s inferior object is deleted (say the inferior exits) then\npy_free_inferior is called (thanks to the registry system), this\nfunction looks up the Python gdb.Inferior object and sets the C++\npointer to nullptr and finally reduces the reference count on the\nPython gdb.Inferior object.\n\nIf at this point the user still holds a reference to the Python\ngdb.Inferior object then nothing happens.  However, the gdb.Inferior\nobject is now in the non-valid state (see infpy_is_valid in\npy-inferior.c), but otherwise, everything is fine.\n\nHowever, if there are no further references to the Python gdb.Inferior\nobject, or, once the user has given up all their references to the\ngdb.Inferior object, then infpy_dealloc is called.\n\nThis function currently checks to see if the inferior pointer within\nthe gdb.Inferior object is nullptr or not.  If the pointer is nullptr\nthen infpy_dealloc immediately returns.\n\nOnly when the inferior point in the gdb.Inferior is not nullptr do\nwe (a) set the gdb.Inferior reference inside GDB\u0027s inferior to\nnullptr, and (b) call the underlying Python tp_free function.\n\nThere are a number things wrong here:\n\n  1.  The Python gdb.Inferior reference within GDB\u0027s inferior object\n  holds a reference count, thus, setting this reference to nullptr\n  without first decrementing the reference count would leak a\n  reference, however...\n\n  2. As GDB\u0027s inferior holds a reference then infpy_dealloc will never\n  be called until GDB\u0027s inferior object is deleted.  Deleting a GDB\n  inferior ohject calls py_free_inferior, and so gives up the\n  reference.  At this point there is no longer a need to call\n  set_inferior_data to set the field back to NULL, that field must\n  have been cleared in order to get the reference count to zero, which\n  means...\n\n  3. If we know that py_free_inferior must be called before\n  infpy_dealloc, then we know that the inferior pointer in\n  gdb.Inferior will always be nullptr when infpy_dealloc is called,\n  this means that the call to the underlying tp_free function will\n  always be skipped.  Skipping this call will cause Python to leak the\n  memory associated with the gdb.Inferior object, which is what we\n  currently always do.\n\nGiven all of the above, I assert that the C++ pointer within\ngdb.Inferior will always be nullptr when infpy_dealloc is called.\nThat\u0027s what this patch does.\n\nI wrote a test for this issue making use of Pythons tracemalloc\nmodule, which allows us to spot this memory leak.\n",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "0659c28ea9c5da4b03252bd1648c3a9fa8846497",
      "old_mode": 33188,
      "old_path": "gdb/python/py-inferior.c",
      "new_id": "c8de41dd009c144bae743bb42ed16e1cf515896f",
      "new_mode": 33188,
      "new_path": "gdb/python/py-inferior.c"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "bfe52c018d47899c142e2c03b70c11a73db0eac6",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.python/py-inferior-leak.c"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "9cd1ebf243333e068316f32b52bc19b339242428",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.python/py-inferior-leak.exp"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "914fb3ecc08411148a702ce9aa707cb57b145dc3",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.python/py-inferior-leak.py"
    }
  ]
}
