mirror of https://github.com/python/cpython
bpo-34052: Prevent SQLite functions from setting callbacks on exceptions. (GH-8113)
This commit is contained in:
parent
f2f4555d82
commit
5b25f1d031
|
@ -256,24 +256,6 @@ class RegressionTests(unittest.TestCase):
|
||||||
cur.execute("pragma page_size")
|
cur.execute("pragma page_size")
|
||||||
row = cur.fetchone()
|
row = cur.fetchone()
|
||||||
|
|
||||||
def CheckSetDict(self):
|
|
||||||
"""
|
|
||||||
See http://bugs.python.org/issue7478
|
|
||||||
|
|
||||||
It was possible to successfully register callbacks that could not be
|
|
||||||
hashed. Return codes of PyDict_SetItem were not checked properly.
|
|
||||||
"""
|
|
||||||
class NotHashable:
|
|
||||||
def __call__(self, *args, **kw):
|
|
||||||
pass
|
|
||||||
def __hash__(self):
|
|
||||||
raise TypeError()
|
|
||||||
var = NotHashable()
|
|
||||||
self.assertRaises(TypeError, self.con.create_function, var)
|
|
||||||
self.assertRaises(TypeError, self.con.create_aggregate, var)
|
|
||||||
self.assertRaises(TypeError, self.con.set_authorizer, var)
|
|
||||||
self.assertRaises(TypeError, self.con.set_progress_handler, var)
|
|
||||||
|
|
||||||
def CheckConnectionCall(self):
|
def CheckConnectionCall(self):
|
||||||
"""
|
"""
|
||||||
Call a connection with a non-string SQL request: check error handling
|
Call a connection with a non-string SQL request: check error handling
|
||||||
|
@ -398,9 +380,72 @@ class RegressionTests(unittest.TestCase):
|
||||||
support.gc_collect()
|
support.gc_collect()
|
||||||
|
|
||||||
|
|
||||||
|
class UnhashableFunc:
|
||||||
|
__hash__ = None
|
||||||
|
|
||||||
|
def __init__(self, return_value=None):
|
||||||
|
self.calls = 0
|
||||||
|
self.return_value = return_value
|
||||||
|
|
||||||
|
def __call__(self, *args, **kwargs):
|
||||||
|
self.calls += 1
|
||||||
|
return self.return_value
|
||||||
|
|
||||||
|
|
||||||
|
class UnhashableCallbacksTestCase(unittest.TestCase):
|
||||||
|
"""
|
||||||
|
https://bugs.python.org/issue34052
|
||||||
|
|
||||||
|
Registering unhashable callbacks raises TypeError, callbacks are not
|
||||||
|
registered in SQLite after such registration attempt.
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
self.con = sqlite.connect(':memory:')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.con.close()
|
||||||
|
|
||||||
|
def test_progress_handler(self):
|
||||||
|
f = UnhashableFunc(return_value=0)
|
||||||
|
with self.assertRaisesRegex(TypeError, 'unhashable type'):
|
||||||
|
self.con.set_progress_handler(f, 1)
|
||||||
|
self.con.execute('SELECT 1')
|
||||||
|
self.assertFalse(f.calls)
|
||||||
|
|
||||||
|
def test_func(self):
|
||||||
|
func_name = 'func_name'
|
||||||
|
f = UnhashableFunc()
|
||||||
|
with self.assertRaisesRegex(TypeError, 'unhashable type'):
|
||||||
|
self.con.create_function(func_name, 0, f)
|
||||||
|
msg = 'no such function: %s' % func_name
|
||||||
|
with self.assertRaisesRegex(sqlite.OperationalError, msg):
|
||||||
|
self.con.execute('SELECT %s()' % func_name)
|
||||||
|
self.assertFalse(f.calls)
|
||||||
|
|
||||||
|
def test_authorizer(self):
|
||||||
|
f = UnhashableFunc(return_value=sqlite.SQLITE_DENY)
|
||||||
|
with self.assertRaisesRegex(TypeError, 'unhashable type'):
|
||||||
|
self.con.set_authorizer(f)
|
||||||
|
self.con.execute('SELECT 1')
|
||||||
|
self.assertFalse(f.calls)
|
||||||
|
|
||||||
|
def test_aggr(self):
|
||||||
|
class UnhashableType(type):
|
||||||
|
__hash__ = None
|
||||||
|
aggr_name = 'aggr_name'
|
||||||
|
with self.assertRaisesRegex(TypeError, 'unhashable type'):
|
||||||
|
self.con.create_aggregate(aggr_name, 0, UnhashableType('Aggr', (), {}))
|
||||||
|
msg = 'no such function: %s' % aggr_name
|
||||||
|
with self.assertRaisesRegex(sqlite.OperationalError, msg):
|
||||||
|
self.con.execute('SELECT %s()' % aggr_name)
|
||||||
|
|
||||||
|
|
||||||
def suite():
|
def suite():
|
||||||
regression_suite = unittest.makeSuite(RegressionTests, "Check")
|
regression_suite = unittest.makeSuite(RegressionTests, "Check")
|
||||||
return unittest.TestSuite((regression_suite,))
|
return unittest.TestSuite((
|
||||||
|
regression_suite,
|
||||||
|
unittest.makeSuite(UnhashableCallbacksTestCase),
|
||||||
|
))
|
||||||
|
|
||||||
def test():
|
def test():
|
||||||
runner = unittest.TextTestRunner()
|
runner = unittest.TextTestRunner()
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
:meth:`sqlite3.Connection.create_aggregate`,
|
||||||
|
:meth:`sqlite3.Connection.create_function`,
|
||||||
|
:meth:`sqlite3.Connection.set_authorizer`,
|
||||||
|
:meth:`sqlite3.Connection.set_progress_handler` methods raises TypeError
|
||||||
|
when unhashable objects are passed as callable. These methods now don't pass
|
||||||
|
such objects to SQLite API. Previous behavior could lead to segfaults. Patch
|
||||||
|
by Sergey Fedoseev.
|
|
@ -843,7 +843,9 @@ PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObjec
|
||||||
flags |= SQLITE_DETERMINISTIC;
|
flags |= SQLITE_DETERMINISTIC;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
if (PyDict_SetItem(self->function_pinboard, func, Py_None) == -1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
rc = sqlite3_create_function(self->db,
|
rc = sqlite3_create_function(self->db,
|
||||||
name,
|
name,
|
||||||
narg,
|
narg,
|
||||||
|
@ -857,12 +859,8 @@ PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObjec
|
||||||
/* Workaround for SQLite bug: no error code or string is available here */
|
/* Workaround for SQLite bug: no error code or string is available here */
|
||||||
PyErr_SetString(pysqlite_OperationalError, "Error creating function");
|
PyErr_SetString(pysqlite_OperationalError, "Error creating function");
|
||||||
return NULL;
|
return NULL;
|
||||||
} else {
|
|
||||||
if (PyDict_SetItem(self->function_pinboard, func, Py_None) == -1)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
}
|
||||||
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject* pysqlite_connection_create_aggregate(pysqlite_Connection* self, PyObject* args, PyObject* kwargs)
|
PyObject* pysqlite_connection_create_aggregate(pysqlite_Connection* self, PyObject* args, PyObject* kwargs)
|
||||||
|
@ -883,17 +881,16 @@ PyObject* pysqlite_connection_create_aggregate(pysqlite_Connection* self, PyObje
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (PyDict_SetItem(self->function_pinboard, aggregate_class, Py_None) == -1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
rc = sqlite3_create_function(self->db, name, n_arg, SQLITE_UTF8, (void*)aggregate_class, 0, &_pysqlite_step_callback, &_pysqlite_final_callback);
|
rc = sqlite3_create_function(self->db, name, n_arg, SQLITE_UTF8, (void*)aggregate_class, 0, &_pysqlite_step_callback, &_pysqlite_final_callback);
|
||||||
if (rc != SQLITE_OK) {
|
if (rc != SQLITE_OK) {
|
||||||
/* Workaround for SQLite bug: no error code or string is available here */
|
/* Workaround for SQLite bug: no error code or string is available here */
|
||||||
PyErr_SetString(pysqlite_OperationalError, "Error creating aggregate");
|
PyErr_SetString(pysqlite_OperationalError, "Error creating aggregate");
|
||||||
return NULL;
|
return NULL;
|
||||||
} else {
|
|
||||||
if (PyDict_SetItem(self->function_pinboard, aggregate_class, Py_None) == -1)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
}
|
||||||
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _authorizer_callback(void* user_arg, int action, const char* arg1, const char* arg2 , const char* dbname, const char* access_attempt_source)
|
static int _authorizer_callback(void* user_arg, int action, const char* arg1, const char* arg2 , const char* dbname, const char* access_attempt_source)
|
||||||
|
@ -1006,17 +1003,15 @@ static PyObject* pysqlite_connection_set_authorizer(pysqlite_Connection* self, P
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (PyDict_SetItem(self->function_pinboard, authorizer_cb, Py_None) == -1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
rc = sqlite3_set_authorizer(self->db, _authorizer_callback, (void*)authorizer_cb);
|
rc = sqlite3_set_authorizer(self->db, _authorizer_callback, (void*)authorizer_cb);
|
||||||
|
|
||||||
if (rc != SQLITE_OK) {
|
if (rc != SQLITE_OK) {
|
||||||
PyErr_SetString(pysqlite_OperationalError, "Error setting authorizer callback");
|
PyErr_SetString(pysqlite_OperationalError, "Error setting authorizer callback");
|
||||||
return NULL;
|
return NULL;
|
||||||
} else {
|
|
||||||
if (PyDict_SetItem(self->function_pinboard, authorizer_cb, Py_None) == -1)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
}
|
||||||
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject* pysqlite_connection_set_progress_handler(pysqlite_Connection* self, PyObject* args, PyObject* kwargs)
|
static PyObject* pysqlite_connection_set_progress_handler(pysqlite_Connection* self, PyObject* args, PyObject* kwargs)
|
||||||
|
@ -1039,9 +1034,9 @@ static PyObject* pysqlite_connection_set_progress_handler(pysqlite_Connection* s
|
||||||
/* None clears the progress handler previously set */
|
/* None clears the progress handler previously set */
|
||||||
sqlite3_progress_handler(self->db, 0, 0, (void*)0);
|
sqlite3_progress_handler(self->db, 0, 0, (void*)0);
|
||||||
} else {
|
} else {
|
||||||
sqlite3_progress_handler(self->db, n, _progress_handler, progress_handler);
|
|
||||||
if (PyDict_SetItem(self->function_pinboard, progress_handler, Py_None) == -1)
|
if (PyDict_SetItem(self->function_pinboard, progress_handler, Py_None) == -1)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
sqlite3_progress_handler(self->db, n, _progress_handler, progress_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
|
|
Loading…
Reference in New Issue