gh-139327: fix some reference leaks in sqlite3 error branches (#139328)

This commit is contained in:
Bénédikt Tran 2025-10-01 11:10:12 +02:00 committed by GitHub
parent d936dbeb1f
commit d0a3eff9d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 42 additions and 32 deletions

View file

@ -144,7 +144,7 @@ class _sqlite3.Connection "pysqlite_Connection *" "clinic_state()->ConnectionTyp
[clinic start generated code]*/ [clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=67369db2faf80891]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=67369db2faf80891]*/
static void _pysqlite_drop_unused_cursor_references(pysqlite_Connection* self); static int _pysqlite_drop_unused_cursor_references(pysqlite_Connection* self);
static void free_callback_context(callback_context *ctx); static void free_callback_context(callback_context *ctx);
static void set_callback_context(callback_context **ctx_pp, static void set_callback_context(callback_context **ctx_pp,
callback_context *ctx); callback_context *ctx);
@ -561,7 +561,10 @@ pysqlite_connection_cursor_impl(pysqlite_Connection *self, PyObject *factory)
return NULL; return NULL;
} }
_pysqlite_drop_unused_cursor_references(self); if (_pysqlite_drop_unused_cursor_references(self) < 0) {
Py_DECREF(cursor);
return NULL;
}
if (cursor && self->row_factory != Py_None) { if (cursor && self->row_factory != Py_None) {
Py_INCREF(self->row_factory); Py_INCREF(self->row_factory);
@ -1060,32 +1063,36 @@ final_callback(sqlite3_context *context)
PyGILState_Release(threadstate); PyGILState_Release(threadstate);
} }
static void _pysqlite_drop_unused_cursor_references(pysqlite_Connection* self) static int
_pysqlite_drop_unused_cursor_references(pysqlite_Connection* self)
{ {
/* we only need to do this once in a while */ /* we only need to do this once in a while */
if (self->created_cursors++ < 200) { if (self->created_cursors++ < 200) {
return; return 0;
} }
self->created_cursors = 0; self->created_cursors = 0;
PyObject* new_list = PyList_New(0); PyObject* new_list = PyList_New(0);
if (!new_list) { if (!new_list) {
return; return -1;
} }
for (Py_ssize_t i = 0; i < PyList_Size(self->cursors); i++) { assert(PyList_CheckExact(self->cursors));
PyObject* weakref = PyList_GetItem(self->cursors, i); Py_ssize_t imax = PyList_GET_SIZE(self->cursors);
for (Py_ssize_t i = 0; i < imax; i++) {
PyObject* weakref = PyList_GET_ITEM(self->cursors, i);
if (_PyWeakref_IsDead(weakref)) { if (_PyWeakref_IsDead(weakref)) {
continue; continue;
} }
if (PyList_Append(new_list, weakref) != 0) { if (PyList_Append(new_list, weakref) != 0) {
Py_DECREF(new_list); Py_DECREF(new_list);
return; return -1;
} }
} }
Py_SETREF(self->cursors, new_list); Py_SETREF(self->cursors, new_list);
return 0;
} }
/* Allocate a UDF/callback context structure. In order to ensure that the state /* Allocate a UDF/callback context structure. In order to ensure that the state

View file

@ -471,6 +471,9 @@ static int check_cursor(pysqlite_Cursor* cur)
return 0; return 0;
} }
assert(cur->connection != NULL);
assert(cur->connection->state != NULL);
if (cur->closed) { if (cur->closed) {
PyErr_SetString(cur->connection->state->ProgrammingError, PyErr_SetString(cur->connection->state->ProgrammingError,
"Cannot operate on a closed cursor."); "Cannot operate on a closed cursor.");
@ -567,43 +570,40 @@ bind_param(pysqlite_state *state, pysqlite_Statement *self, int pos,
switch (paramtype) { switch (paramtype) {
case TYPE_LONG: { case TYPE_LONG: {
sqlite_int64 value = _pysqlite_long_as_int64(parameter); sqlite_int64 value = _pysqlite_long_as_int64(parameter);
if (value == -1 && PyErr_Occurred()) rc = (value == -1 && PyErr_Occurred())
rc = -1; ? SQLITE_ERROR
else : sqlite3_bind_int64(self->st, pos, value);
rc = sqlite3_bind_int64(self->st, pos, value);
break; break;
} }
case TYPE_FLOAT: { case TYPE_FLOAT: {
double value = PyFloat_AsDouble(parameter); double value = PyFloat_AsDouble(parameter);
if (value == -1 && PyErr_Occurred()) { rc = (value == -1 && PyErr_Occurred())
rc = -1; ? SQLITE_ERROR
} : sqlite3_bind_double(self->st, pos, value);
else {
rc = sqlite3_bind_double(self->st, pos, value);
}
break; break;
} }
case TYPE_UNICODE: case TYPE_UNICODE:
string = PyUnicode_AsUTF8AndSize(parameter, &buflen); string = PyUnicode_AsUTF8AndSize(parameter, &buflen);
if (string == NULL) if (string == NULL) {
return -1; return SQLITE_ERROR;
}
if (buflen > INT_MAX) { if (buflen > INT_MAX) {
PyErr_SetString(PyExc_OverflowError, PyErr_SetString(PyExc_OverflowError,
"string longer than INT_MAX bytes"); "string longer than INT_MAX bytes");
return -1; return SQLITE_ERROR;
} }
rc = sqlite3_bind_text(self->st, pos, string, (int)buflen, SQLITE_TRANSIENT); rc = sqlite3_bind_text(self->st, pos, string, (int)buflen, SQLITE_TRANSIENT);
break; break;
case TYPE_BUFFER: { case TYPE_BUFFER: {
Py_buffer view; Py_buffer view;
if (PyObject_GetBuffer(parameter, &view, PyBUF_SIMPLE) != 0) { if (PyObject_GetBuffer(parameter, &view, PyBUF_SIMPLE) != 0) {
return -1; return SQLITE_ERROR;
} }
if (view.len > INT_MAX) { if (view.len > INT_MAX) {
PyErr_SetString(PyExc_OverflowError, PyErr_SetString(PyExc_OverflowError,
"BLOB longer than INT_MAX bytes"); "BLOB longer than INT_MAX bytes");
PyBuffer_Release(&view); PyBuffer_Release(&view);
return -1; return SQLITE_ERROR;
} }
rc = sqlite3_bind_blob(self->st, pos, view.buf, (int)view.len, SQLITE_TRANSIENT); rc = sqlite3_bind_blob(self->st, pos, view.buf, (int)view.len, SQLITE_TRANSIENT);
PyBuffer_Release(&view); PyBuffer_Release(&view);
@ -613,7 +613,7 @@ bind_param(pysqlite_state *state, pysqlite_Statement *self, int pos,
PyErr_Format(state->ProgrammingError, PyErr_Format(state->ProgrammingError,
"Error binding parameter %d: type '%s' is not supported", "Error binding parameter %d: type '%s' is not supported",
pos, Py_TYPE(parameter)->tp_name); pos, Py_TYPE(parameter)->tp_name);
rc = -1; rc = SQLITE_ERROR;
} }
final: final:
@ -733,14 +733,17 @@ bind_parameters(pysqlite_state *state, pysqlite_Statement *self,
} }
binding_name++; /* skip first char (the colon) */ binding_name++; /* skip first char (the colon) */
PyObject *current_param; PyObject *current_param = NULL;
(void)PyMapping_GetOptionalItemString(parameters, binding_name, &current_param); int found = PyMapping_GetOptionalItemString(parameters,
if (!current_param) { binding_name,
if (!PyErr_Occurred() || PyErr_ExceptionMatches(PyExc_LookupError)) { &current_param);
PyErr_Format(state->ProgrammingError, if (found == -1) {
"You did not supply a value for binding " return;
"parameter :%s.", binding_name); }
} else if (found == 0) {
PyErr_Format(state->ProgrammingError,
"You did not supply a value for binding "
"parameter :%s.", binding_name);
return; return;
} }