From 5f7f389a8a784ec750013f4f8f23765229795aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Grenotton?= Date: Sat, 7 Nov 2020 00:29:31 +0100 Subject: [PATCH] Fix PyUnknownFields memory leak (#7928) Properly release internal data structure on deallocation. Fix #7301 --- .../protobuf/internal/unknown_fields_test.py | 24 +++++++++++++++++++ .../google/protobuf/pyext/unknown_fields.cc | 1 + 2 files changed, 25 insertions(+) diff --git a/python/google/protobuf/internal/unknown_fields_test.py b/python/google/protobuf/internal/unknown_fields_test.py index 277603d95235..a60b942cdfb0 100755 --- a/python/google/protobuf/internal/unknown_fields_test.py +++ b/python/google/protobuf/internal/unknown_fields_test.py @@ -39,6 +39,12 @@ import unittest2 as unittest #PY26 except ImportError: import unittest +import sys +try: + import tracemalloc +except ImportError: + # Requires python 3.4+ + pass from google.protobuf import map_unittest_pb2 from google.protobuf import unittest_mset_pb2 from google.protobuf import unittest_pb2 @@ -312,6 +318,24 @@ def testClear(self): self.assertIn('UnknownFields does not exist.', str(context.exception)) + @unittest.skipIf((sys.version_info.major, sys.version_info.minor) < (3, 4), + 'tracemalloc requires python 3.4+') + def testUnknownFieldsNoMemoryLeak(self): + # Call to UnknownFields must not leak memory + nb_leaks = 1234 + def leaking_function(): + for _ in range(nb_leaks): + self.empty_message.UnknownFields() + tracemalloc.start() + snapshot1 = tracemalloc.take_snapshot() + leaking_function() + snapshot2 = tracemalloc.take_snapshot() + top_stats = snapshot2.compare_to(snapshot1, 'lineno') + tracemalloc.stop() + # There's no easy way to look for a precise leak source. + # Rely on a "marker" count value while checking allocated memory. + self.assertEqual([], [x for x in top_stats if x.count_diff == nb_leaks]) + def testSubUnknownFields(self): message = unittest_pb2.TestAllTypes() message.optionalgroup.a = 123 diff --git a/python/google/protobuf/pyext/unknown_fields.cc b/python/google/protobuf/pyext/unknown_fields.cc index 8509213b18dc..deb86e691682 100644 --- a/python/google/protobuf/pyext/unknown_fields.cc +++ b/python/google/protobuf/pyext/unknown_fields.cc @@ -142,6 +142,7 @@ static void Dealloc(PyObject* pself) { } Py_CLEAR(self->parent); self->~PyUnknownFields(); + Py_TYPE(pself)->tp_free(pself); } static PySequenceMethods SqMethods = {