]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.14] gh-139327: fix some reference leaks in `sqlite3` error branches (GH-139328...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Tue, 7 Oct 2025 18:22:20 +0000 (20:22 +0200)
committerGitHub <noreply@github.com>
Tue, 7 Oct 2025 18:22:20 +0000 (20:22 +0200)
(cherry picked from commit d0a3eff9d6e72e209da0236fb1ea1f8c18938eaa)

Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Modules/_sqlite/connection.c
Modules/_sqlite/cursor.c

index 2a184f787542ec01e1aeb92312cfbbdfaa1e28fe..be4ad89c6b98aa3ae4a70a379f5fa80521713f0c 100644 (file)
@@ -144,7 +144,7 @@ class _sqlite3.Connection "pysqlite_Connection *" "clinic_state()->ConnectionTyp
 [clinic start generated code]*/
 /*[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 set_callback_context(callback_context **ctx_pp,
                                  callback_context *ctx);
@@ -562,7 +562,10 @@ pysqlite_connection_cursor_impl(pysqlite_Connection *self, PyObject *factory)
         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) {
         Py_INCREF(self->row_factory);
@@ -1061,32 +1064,36 @@ error:
     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 */
     if (self->created_cursors++ < 200) {
-        return;
+        return 0;
     }
 
     self->created_cursors = 0;
 
     PyObject* new_list = PyList_New(0);
     if (!new_list) {
-        return;
+        return -1;
     }
 
-    for (Py_ssize_t i = 0; i < PyList_Size(self->cursors); i++) {
-        PyObject* weakref = PyList_GetItem(self->cursors, i);
+    assert(PyList_CheckExact(self->cursors));
+    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)) {
             continue;
         }
         if (PyList_Append(new_list, weakref) != 0) {
             Py_DECREF(new_list);
-            return;
+            return -1;
         }
     }
 
     Py_SETREF(self->cursors, new_list);
+    return 0;
 }
 
 /* Allocate a UDF/callback context structure. In order to ensure that the state
index 0c3f43d0e50b43590aa96ba1afe772b8d0d63ac1..a38bf534d1b334e1972f4a9b1cded5c40ae161b0 100644 (file)
@@ -471,6 +471,9 @@ static int check_cursor(pysqlite_Cursor* cur)
         return 0;
     }
 
+    assert(cur->connection != NULL);
+    assert(cur->connection->state != NULL);
+
     if (cur->closed) {
         PyErr_SetString(cur->connection->state->ProgrammingError,
                         "Cannot operate on a closed cursor.");
@@ -567,43 +570,40 @@ bind_param(pysqlite_state *state, pysqlite_Statement *self, int pos,
     switch (paramtype) {
         case TYPE_LONG: {
             sqlite_int64 value = _pysqlite_long_as_int64(parameter);
-            if (value == -1 && PyErr_Occurred())
-                rc = -1;
-            else
-                rc = sqlite3_bind_int64(self->st, pos, value);
+            rc = (value == -1 && PyErr_Occurred())
+                ? SQLITE_ERROR
+                : sqlite3_bind_int64(self->st, pos, value);
             break;
         }
         case TYPE_FLOAT: {
             double value = PyFloat_AsDouble(parameter);
-            if (value == -1 && PyErr_Occurred()) {
-                rc = -1;
-            }
-            else {
-                rc = sqlite3_bind_double(self->st, pos, value);
-            }
+            rc = (value == -1 && PyErr_Occurred())
+                ? SQLITE_ERROR
+                : sqlite3_bind_double(self->st, pos, value);
             break;
         }
         case TYPE_UNICODE:
             string = PyUnicode_AsUTF8AndSize(parameter, &buflen);
-            if (string == NULL)
-                return -1;
+            if (string == NULL) {
+                return SQLITE_ERROR;
+            }
             if (buflen > INT_MAX) {
                 PyErr_SetString(PyExc_OverflowError,
                                 "string longer than INT_MAX bytes");
-                return -1;
+                return SQLITE_ERROR;
             }
             rc = sqlite3_bind_text(self->st, pos, string, (int)buflen, SQLITE_TRANSIENT);
             break;
         case TYPE_BUFFER: {
             Py_buffer view;
             if (PyObject_GetBuffer(parameter, &view, PyBUF_SIMPLE) != 0) {
-                return -1;
+                return SQLITE_ERROR;
             }
             if (view.len > INT_MAX) {
                 PyErr_SetString(PyExc_OverflowError,
                                 "BLOB longer than INT_MAX bytes");
                 PyBuffer_Release(&view);
-                return -1;
+                return SQLITE_ERROR;
             }
             rc = sqlite3_bind_blob(self->st, pos, view.buf, (int)view.len, SQLITE_TRANSIENT);
             PyBuffer_Release(&view);
@@ -613,7 +613,7 @@ bind_param(pysqlite_state *state, pysqlite_Statement *self, int pos,
             PyErr_Format(state->ProgrammingError,
                     "Error binding parameter %d: type '%s' is not supported",
                     pos, Py_TYPE(parameter)->tp_name);
-            rc = -1;
+            rc = SQLITE_ERROR;
     }
 
 final:
@@ -733,14 +733,17 @@ bind_parameters(pysqlite_state *state, pysqlite_Statement *self,
             }
 
             binding_name++; /* skip first char (the colon) */
-            PyObject *current_param;
-            (void)PyMapping_GetOptionalItemString(parameters, binding_name, &current_param);
-            if (!current_param) {
-                if (!PyErr_Occurred() || PyErr_ExceptionMatches(PyExc_LookupError)) {
-                    PyErr_Format(state->ProgrammingError,
-                                 "You did not supply a value for binding "
-                                 "parameter :%s.", binding_name);
-                }
+            PyObject *current_param = NULL;
+            int found = PyMapping_GetOptionalItemString(parameters,
+                                                        binding_name,
+                                                        &current_param);
+            if (found == -1) {
+                return;
+            }
+            else if (found == 0) {
+                PyErr_Format(state->ProgrammingError,
+                             "You did not supply a value for binding "
+                             "parameter :%s.", binding_name);
                 return;
             }