Skip to content

bpo-45126: Fix ref. leak in sqlite3.Connection.__init__ #28231

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions Modules/_sqlite/clinic/connection.c.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ preserve

static int
pysqlite_connection_init_impl(pysqlite_Connection *self,
PyObject *database_obj, double timeout,
const char *database, double timeout,
int detect_types, PyObject *isolation_level,
int check_same_thread, PyObject *factory,
int cached_statements, int uri);
Expand All @@ -19,7 +19,7 @@ pysqlite_connection_init(PyObject *self, PyObject *args, PyObject *kwargs)
PyObject * const *fastargs;
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1;
PyObject *database_obj;
const char *database = NULL;
double timeout = 5.0;
int detect_types = 0;
PyObject *isolation_level = NULL;
Expand All @@ -32,7 +32,7 @@ pysqlite_connection_init(PyObject *self, PyObject *args, PyObject *kwargs)
if (!fastargs) {
goto exit;
}
if (!PyUnicode_FSConverter(fastargs[0], &database_obj)) {
if (!clinic_fsconverter(fastargs[0], &database)) {
goto exit;
}
if (!noptargs) {
Expand Down Expand Up @@ -97,9 +97,12 @@ pysqlite_connection_init(PyObject *self, PyObject *args, PyObject *kwargs)
goto exit;
}
skip_optional_pos:
return_value = pysqlite_connection_init_impl((pysqlite_Connection *)self, database_obj, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements, uri);
return_value = pysqlite_connection_init_impl((pysqlite_Connection *)self, database, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements, uri);

exit:
/* Cleanup for database */
PyMem_Free((void *)database);

return return_value;
}

Expand Down Expand Up @@ -816,4 +819,4 @@ pysqlite_connection_exit(pysqlite_Connection *self, PyObject *const *args, Py_ss
#ifndef PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF
#define PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF
#endif /* !defined(PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF) */
/*[clinic end generated code: output=9c0dfc6c1ebf9039 input=a9049054013a1b77]*/
/*[clinic end generated code: output=5b7268875f33c016 input=a9049054013a1b77]*/
49 changes: 41 additions & 8 deletions Modules/_sqlite/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,32 @@
#define HAVE_TRACE_V2
#endif

static int
clinic_fsconverter(PyObject *pathlike, const char **result)
{
PyObject *bytes = NULL;
Py_ssize_t len;
char *str;

if (!PyUnicode_FSConverter(pathlike, &bytes)) {
goto error;
}
if (PyBytes_AsStringAndSize(bytes, &str, &len) < 0) {
goto error;
}
if ((*result = (const char *)PyMem_Malloc(len+1)) == NULL) {
goto error;
}

memcpy((void *)(*result), str, len+1);
Py_DECREF(bytes);
return 1;

error:
Py_XDECREF(bytes);
return 0;
}

#define clinic_state() (pysqlite_get_state(NULL))
#include "clinic/connection.c.h"
#undef clinic_state
Expand Down Expand Up @@ -81,10 +107,21 @@ new_statement_cache(pysqlite_Connection *self, int maxsize)
return res;
}

/*[python input]
class FSConverter_converter(CConverter):
type = "const char *"
converter = "clinic_fsconverter"
def converter_init(self):
self.c_default = "NULL"
def cleanup(self):
return f"PyMem_Free((void *){self.name});\n"
[python start generated code]*/
/*[python end generated code: output=da39a3ee5e6b4b0d input=7b3be538bc4058c0]*/

/*[clinic input]
_sqlite3.Connection.__init__ as pysqlite_connection_init

database as database_obj: object(converter='PyUnicode_FSConverter')
database: FSConverter
timeout: double = 5.0
detect_types: int = 0
isolation_level: object = NULL
Expand All @@ -96,23 +133,21 @@ _sqlite3.Connection.__init__ as pysqlite_connection_init

static int
pysqlite_connection_init_impl(pysqlite_Connection *self,
PyObject *database_obj, double timeout,
const char *database, double timeout,
int detect_types, PyObject *isolation_level,
int check_same_thread, PyObject *factory,
int cached_statements, int uri)
/*[clinic end generated code: output=dc19df1c0e2b7b77 input=aa1f21bf12fe907a]*/
/*[clinic end generated code: output=bc39e55eb0b68783 input=f8d1f7efc0d84104]*/
{
int rc;

if (PySys_Audit("sqlite3.connect", "O", database_obj) < 0) {
if (PySys_Audit("sqlite3.connect", "s", database) < 0) {
return -1;
}

pysqlite_state *state = pysqlite_get_state_by_type(Py_TYPE(self));
self->state = state;

const char *database = PyBytes_AsString(database_obj);

self->begin_statement = NULL;

Py_CLEAR(self->statement_cache);
Expand All @@ -130,8 +165,6 @@ pysqlite_connection_init_impl(pysqlite_Connection *self,
(uri ? SQLITE_OPEN_URI : 0), NULL);
Py_END_ALLOW_THREADS

Py_DECREF(database_obj); // needed bco. the AC FSConverter

if (rc != SQLITE_OK) {
_pysqlite_seterror(state, self->db);
return -1;
Expand Down