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.
This commit is contained in:
Zackery Spytz 2018-07-21 02:27:44 -06:00 committed by Serhiy Storchaka
parent c5bc6e477e
commit 3252205077
3 changed files with 69 additions and 36 deletions

View File

@ -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)
#----------------------------------------------------------------------

View File

@ -0,0 +1,2 @@
Fix potential heap corruption in the :mod:`bsddb` module. Patch by Zackery
Spytz.

View File

@ -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();
}
break;
}
PyBytes_AsStringAndSize(
PyList_GetItem(result, i),
&data, &size);
if (PyBytes_AsStringAndSize(PyList_GetItem(result, i),
&data, &size) < 0) {
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) {
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);
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();
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 {