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.
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 @@
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 {