[Python-checkins] bpo-25943: Fix potential heap corruption in bsddb's _db_associateCallback() (GH-8337)

Serhiy Storchaka webhook-mailer at python.org
Sat Jul 21 04:27:49 EDT 2018


https://github.com/python/cpython/commit/32522050773c257a5c3c0c8929ba5c64123b53ed
commit: 32522050773c257a5c3c0c8929ba5c64123b53ed
branch: 2.7
author: Zackery Spytz <zspytz at gmail.com>
committer: Serhiy Storchaka <storchaka at gmail.com>
date: 2018-07-21T11:27:44+03:00
summary:

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.

files:
A Misc/NEWS.d/next/Core and Builtins/2018-07-18-23-40-32.bpo-25943.Zgf99y.rst
M Lib/bsddb/test/test_associate.py
M Modules/_bsddb.c

diff --git a/Lib/bsddb/test/test_associate.py b/Lib/bsddb/test/test_associate.py
index f0eadaaf66f1..4a8d2aed990b 100644
--- a/Lib/bsddb/test/test_associate.py
+++ b/Lib/bsddb/test/test_associate.py
@@ -114,6 +114,22 @@ def f(a,b): return a+b
             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 000000000000..264e65c65517
--- /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 9c81ec5217f3..a8867942b1fe 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<listlen; i++)
-            {
-                if (!PyBytes_Check(PyList_GetItem(result, i)))
-                {
-                    PyErr_SetString(
-                       PyExc_TypeError,
+            if (listlen > 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 {



More information about the Python-checkins mailing list