Commit 32522050 authored by Zackery Spytz's avatar Zackery Spytz Committed by Serhiy Storchaka

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.
parent c5bc6e47
...@@ -114,6 +114,22 @@ class AssociateErrorTestCase(unittest.TestCase): ...@@ -114,6 +114,22 @@ class AssociateErrorTestCase(unittest.TestCase):
dupDB.close() dupDB.close()
self.fail("DBError exception was expected") 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)
#---------------------------------------------------------------------- #----------------------------------------------------------------------
......
Fix potential heap corruption in the :mod:`bsddb` module. Patch by Zackery
Spytz.
...@@ -1503,56 +1503,71 @@ _db_associateCallback(DB* db, const DBT* priKey, const DBT* priData, ...@@ -1503,56 +1503,71 @@ _db_associateCallback(DB* db, const DBT* priKey, const DBT* priData,
else if (PyList_Check(result)) else if (PyList_Check(result))
{ {
char* data; char* data;
Py_ssize_t size; Py_ssize_t size, listlen, i;
int i, listlen;
DBT* dbts; DBT* dbts;
listlen = PyList_Size(result); listlen = PyList_Size(result);
dbts = (DBT *)malloc(sizeof(DBT) * listlen); if (listlen > PY_SIZE_MAX / sizeof(DBT)) {
PyErr_NoMemory();
for (i=0; i<listlen; i++) PyErr_Print();
{ }
if (!PyBytes_Check(PyList_GetItem(result, i))) else {
{ dbts = (DBT *)malloc(sizeof(DBT) * listlen);
PyErr_SetString( if (dbts == NULL) {
PyExc_TypeError, 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) #if (PY_VERSION_HEX < 0x03000000)
"The list returned by DB->associate callback should be a list of strings."); "The list returned by DB->associate callback should be a list of strings.");
#else #else
"The list returned by DB->associate callback should be a list of bytes."); "The list returned by DB->associate callback should be a list of bytes.");
#endif #endif
PyErr_Print(); break;
} }
PyBytes_AsStringAndSize( if (PyBytes_AsStringAndSize(PyList_GetItem(result, i),
PyList_GetItem(result, i), &data, &size) < 0) {
&data, &size); break;
}
CLEAR_DBT(dbts[i]);
dbts[i].data = malloc(size); /* TODO, check this */ CLEAR_DBT(dbts[i]);
dbts[i].data = malloc(size);
if (dbts[i].data) if (dbts[i].data) {
{ memcpy(dbts[i].data, data, size);
memcpy(dbts[i].data, data, size); dbts[i].size = size;
dbts[i].size = size; dbts[i].ulen = dbts[i].size;
dbts[i].ulen = dbts[i].size; /* DB will free. */
dbts[i].flags = DB_DBT_APPMALLOC; /* DB will free */ dbts[i].flags = DB_DBT_APPMALLOC;
} }
else else {
{ PyErr_SetString(PyExc_MemoryError,
PyErr_SetString(PyExc_MemoryError, "malloc failed in "
"malloc failed in _db_associateCallback (list)"); "_db_associateCallback (list)");
PyErr_Print(); 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 #endif
else { else {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment