From 91d78aff95a9bc5dc82efd935abfe9885a67564a Mon Sep 17 00:00:00 2001 From: VanshAgarwal24036 Date: Tue, 20 Jan 2026 19:10:09 +0530 Subject: [PATCH 1/3] gh-144069: Fix memory leak in _dbm.open() on failure --- Lib/test/test_dbm.py | 4 ++++ Modules/_dbmmodule.c | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_dbm.py b/Lib/test/test_dbm.py index ae9faabd536a6c..75dbcef15d82e5 100644 --- a/Lib/test/test_dbm.py +++ b/Lib/test/test_dbm.py @@ -244,6 +244,10 @@ def setUp(self): self.addCleanup(cleaunup_test_dir) setup_test_dir() + def test_open_nonexistent_directory(self): + missing_dir = os.path.join(dirname + "_does_not_exist", "test.db") + with self.assertRaises(OSError): + dbm.open(missing_dir, "c") class WhichDBTestCase(unittest.TestCase): def test_whichdb(self): diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c index f88861fa24423b..76d9e359e75619 100644 --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -85,11 +85,16 @@ newdbmobject(_dbm_state *state, const char *file, int flags, int mode) } dp->di_size = -1; dp->flags = flags; + dp->di_dbm = NULL; PyObject_GC_Track(dp); /* See issue #19296 */ - if ( (dp->di_dbm = dbm_open((char *)file, flags, mode)) == 0 ) { + if ( (dp->di_dbm = dbm_open((char *)file, flags, mode)) == NULL ) { PyErr_SetFromErrnoWithFilename(state->dbm_error, file); + if (dp->di_dbm != NULL) { + dbm_close(dp->di_dbm); + dp->di_dbm = NULL; + } Py_DECREF(dp); return NULL; } From 8bdd53c22f419ff2ed492f61462e771b0da82025 Mon Sep 17 00:00:00 2001 From: VanshAgarwal24036 Date: Tue, 20 Jan 2026 20:17:48 +0530 Subject: [PATCH 2/3] News Added --- .../next/C_API/2026-01-20-20-17-16.gh-issue-144069.IE5fug.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/C_API/2026-01-20-20-17-16.gh-issue-144069.IE5fug.rst diff --git a/Misc/NEWS.d/next/C_API/2026-01-20-20-17-16.gh-issue-144069.IE5fug.rst b/Misc/NEWS.d/next/C_API/2026-01-20-20-17-16.gh-issue-144069.IE5fug.rst new file mode 100644 index 00000000000000..6167c1d362b02b --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-01-20-20-17-16.gh-issue-144069.IE5fug.rst @@ -0,0 +1,2 @@ +Fix a memory leak in :func:`dbm.open` when database creation fails, such as +when the target directory does not exist. From 32c8ccaa5b5f539e24a5cc4321b16e12bc8f4159 Mon Sep 17 00:00:00 2001 From: VanshAgarwal24036 Date: Wed, 21 Jan 2026 10:01:22 +0530 Subject: [PATCH 3/3] gh-144069: Fix cleanup in _dbm deallocation --- Lib/test/test_dbm.py | 9 +++++---- Modules/_dbmmodule.c | 5 +---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_dbm.py b/Lib/test/test_dbm.py index 75dbcef15d82e5..5db2e658f45585 100644 --- a/Lib/test/test_dbm.py +++ b/Lib/test/test_dbm.py @@ -71,6 +71,11 @@ def test_error(self): def test_anydbm_not_existing(self): self.assertRaises(dbm.error, dbm.open, _fname) + def test_open_nonexistent_directory(self): + missing_dir = os.path.join(dirname + "_does_not_exist", "test.db") + with self.assertRaises(OSError): + dbm.open(missing_dir, "c") + def test_anydbm_creation(self): f = dbm.open(_fname, 'c') self.assertEqual(list(f.keys()), []) @@ -244,10 +249,6 @@ def setUp(self): self.addCleanup(cleaunup_test_dir) setup_test_dir() - def test_open_nonexistent_directory(self): - missing_dir = os.path.join(dirname + "_does_not_exist", "test.db") - with self.assertRaises(OSError): - dbm.open(missing_dir, "c") class WhichDBTestCase(unittest.TestCase): def test_whichdb(self): diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c index 76d9e359e75619..12ee08e01820f8 100644 --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -91,10 +91,6 @@ newdbmobject(_dbm_state *state, const char *file, int flags, int mode) /* See issue #19296 */ if ( (dp->di_dbm = dbm_open((char *)file, flags, mode)) == NULL ) { PyErr_SetFromErrnoWithFilename(state->dbm_error, file); - if (dp->di_dbm != NULL) { - dbm_close(dp->di_dbm); - dp->di_dbm = NULL; - } Py_DECREF(dp); return NULL; } @@ -110,6 +106,7 @@ dbm_dealloc(PyObject *self) PyObject_GC_UnTrack(dp); if (dp->di_dbm) { dbm_close(dp->di_dbm); + dp->di_dbm = NULL; } PyTypeObject *tp = Py_TYPE(dp); tp->tp_free(dp);