From 32522050773c257a5c3c0c8929ba5c64123b53ed Mon Sep 17 00:00:00 2001 From: Zackery Spytz Date: Sat, 21 Jul 2018 02:27:44 -0600 Subject: bpo-25943: Fix potential heap corruption in bsddb's _db_associateCallback() (GH-8337) There was a missing check for integer overflow, several function calls were not checked for failure, and allocated memory was not freed if an error occurred. --- Lib/bsddb/test/test_associate.py | 16 ++++ .../2018-07-18-23-40-32.bpo-25943.Zgf99y.rst | 2 + Modules/_bsddb.c | 93 +++++++++++++--------- 3 files changed, 72 insertions(+), 39 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2018-07-18-23-40-32.bpo-25943.Zgf99y.rst diff --git a/Lib/bsddb/test/test_associate.py b/Lib/bsddb/test/test_associate.py index f0eadaa..4a8d2ae 100644 --- a/Lib/bsddb/test/test_associate.py +++ b/Lib/bsddb/test/test_associate.py @@ -114,6 +114,22 @@ class AssociateErrorTestCase(unittest.TestCase): dupDB.close() self.fail("DBError exception was expected") + @unittest.skipUnless(db.version() >= (4, 6), 'Needs 4.6+') + def test_associateListError(self): + db1 = db.DB(self.env) + db1.open('bad.db', "a.db", db.DB_BTREE, db.DB_CREATE) + db2 = db.DB(self.env) + db2.open('bad.db', "b.db", db.DB_BTREE, db.DB_CREATE) + + db1.associate(db2, lambda a, b: [0]) + + msg = "TypeError: The list returned by DB->associate callback" \ + " should be a list of strings." + with test_support.captured_output("stderr") as s: + db1.put("0", "1") + db1.close() + db2.close() + self.assertEquals(s.getvalue().strip(), msg) #---------------------------------------------------------------------- diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-07-18-23-40-32.bpo-25943.Zgf99y.rst b/Misc/NEWS.d/next/Core and Builtins/2018-07-18-23-40-32.bpo-25943.Zgf99y.rst new file mode 100644 index 0000000..264e65c --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-07-18-23-40-32.bpo-25943.Zgf99y.rst @@ -0,0 +1,2 @@ +Fix potential heap corruption in the :mod:`bsddb` module. Patch by Zackery +Spytz. diff --git a/Modules/_bsddb.c b/Modules/_bsddb.c index 9c81ec5..a886794 100644 --- a/Modules/_bsddb.c +++ b/Modules/_bsddb.c @@ -1503,56 +1503,71 @@ _db_associateCallback(DB* db, const DBT* priKey, const DBT* priData, else if (PyList_Check(result)) { char* data; - Py_ssize_t size; - int i, listlen; + Py_ssize_t size, listlen, i; DBT* dbts; listlen = PyList_Size(result); - dbts = (DBT *)malloc(sizeof(DBT) * listlen); - - for (i=0; i PY_SIZE_MAX / sizeof(DBT)) { + PyErr_NoMemory(); + PyErr_Print(); + } + else { + dbts = (DBT *)malloc(sizeof(DBT) * listlen); + if (dbts == NULL) { + PyErr_NoMemory(); + PyErr_Print(); + } + else { + for (i = 0; i < listlen; i++) { + if (!PyBytes_Check(PyList_GetItem(result, i))) { + PyErr_SetString(PyExc_TypeError, #if (PY_VERSION_HEX < 0x03000000) "The list returned by DB->associate callback should be a list of strings."); #else "The list returned by DB->associate callback should be a list of bytes."); #endif - PyErr_Print(); - } - - PyBytes_AsStringAndSize( - PyList_GetItem(result, i), - &data, &size); - - CLEAR_DBT(dbts[i]); - dbts[i].data = malloc(size); /* TODO, check this */ - - if (dbts[i].data) - { - memcpy(dbts[i].data, data, size); - dbts[i].size = size; - dbts[i].ulen = dbts[i].size; - dbts[i].flags = DB_DBT_APPMALLOC; /* DB will free */ - } - else - { - PyErr_SetString(PyExc_MemoryError, - "malloc failed in _db_associateCallback (list)"); - PyErr_Print(); + break; + } + + if (PyBytes_AsStringAndSize(PyList_GetItem(result, i), + &data, &size) < 0) { + break; + } + + CLEAR_DBT(dbts[i]); + dbts[i].data = malloc(size); + if (dbts[i].data) { + memcpy(dbts[i].data, data, size); + dbts[i].size = size; + dbts[i].ulen = dbts[i].size; + /* DB will free. */ + dbts[i].flags = DB_DBT_APPMALLOC; + } + else { + PyErr_SetString(PyExc_MemoryError, + "malloc failed in " + "_db_associateCallback (list)"); + break; + } + } + if (PyErr_Occurred()) { + PyErr_Print(); + while (i--) { + free(dbts[i].data); + } + free(dbts); + } + else { + CLEAR_DBT(*secKey); + + secKey->data = dbts; + secKey->size = listlen; + secKey->flags = DB_DBT_APPMALLOC | DB_DBT_MULTIPLE; + retval = 0; + } } } - - CLEAR_DBT(*secKey); - - secKey->data = dbts; - secKey->size = listlen; - secKey->flags = DB_DBT_APPMALLOC | DB_DBT_MULTIPLE; - retval = 0; } #endif else { -- cgit v0.12