mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-10 19:16:34 +03:00
Fix to double free segfault in connection
This commit is contained in:
parent
5dab867db4
commit
5db66038fe
|
@ -1,5 +1,8 @@
|
||||||
2009-04-19 Federico Di Gregorio <fog@initd.org>
|
2009-04-19 Federico Di Gregorio <fog@initd.org>
|
||||||
|
|
||||||
|
* psycopg/connection_type.c: patch from Gangadharan to avoid double
|
||||||
|
free of the connection object when calling close() implicitly.
|
||||||
|
|
||||||
* psycopg/connection_type.c: patch from Marko Kreen to implement
|
* psycopg/connection_type.c: patch from Marko Kreen to implement
|
||||||
get_parameter_status().
|
get_parameter_status().
|
||||||
|
|
||||||
|
|
|
@ -498,6 +498,8 @@ static void
|
||||||
connection_dealloc(PyObject* obj)
|
connection_dealloc(PyObject* obj)
|
||||||
{
|
{
|
||||||
connectionObject *self = (connectionObject *)obj;
|
connectionObject *self = (connectionObject *)obj;
|
||||||
|
|
||||||
|
PyObject_GC_UnTrack(self);
|
||||||
|
|
||||||
if (self->closed == 0) conn_close(self);
|
if (self->closed == 0) conn_close(self);
|
||||||
|
|
||||||
|
|
61
sandbox/trigger_double_dealloc.py
Normal file
61
sandbox/trigger_double_dealloc.py
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import psycopg2, psycopg2.extensions
|
||||||
|
import threading
|
||||||
|
import gc
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# inherit psycopg2 connection class just so that
|
||||||
|
# garbage collector enters the tp_clear code path
|
||||||
|
# in delete_garbage()
|
||||||
|
|
||||||
|
class my_connection(psycopg2.extensions.connection):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class db_user(threading.Thread):
|
||||||
|
def run(self):
|
||||||
|
conn2 = psycopg2.connect(sys.argv[1], connection_factory=my_connection)
|
||||||
|
cursor = conn2.cursor()
|
||||||
|
cursor.execute("UPDATE test_psycopg2_dealloc SET a = 3", async=1)
|
||||||
|
|
||||||
|
# the conn2 desctructor will block indefinitely
|
||||||
|
# on the completion of the query
|
||||||
|
# (and it will not be holding the GIL during that time)
|
||||||
|
print >> sys.stderr, "begin conn2 del"
|
||||||
|
del cursor, conn2
|
||||||
|
print >> sys.stderr, "end conn2 del"
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# lock out a db row
|
||||||
|
conn1 = psycopg2.connect(sys.argv[1], connection_factory=my_connection)
|
||||||
|
cursor = conn1.cursor()
|
||||||
|
cursor.execute("DROP TABLE IF EXISTS test_psycopg2_dealloc")
|
||||||
|
cursor.execute("CREATE TABLE test_psycopg2_dealloc (a int)")
|
||||||
|
cursor.execute("INSERT INTO test_psycopg2_dealloc VALUES (1)")
|
||||||
|
conn1.commit()
|
||||||
|
cursor.execute("UPDATE test_psycopg2_dealloc SET a = 2", async=1)
|
||||||
|
|
||||||
|
# concurrent thread trying to access the locked row
|
||||||
|
db_user().start()
|
||||||
|
|
||||||
|
# eventually, a gc.collect run will happen
|
||||||
|
# while the conn2 is inside conn_close()
|
||||||
|
# but this second dealloc won't get blocked
|
||||||
|
# as it will avoid conn_close()
|
||||||
|
for i in range(10):
|
||||||
|
if gc.collect():
|
||||||
|
print >> sys.stderr, "garbage collection done"
|
||||||
|
break
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
# we now unlock the row by invoking
|
||||||
|
# the desctructor of conn1. This will permit the
|
||||||
|
# concurrent thread destructor of conn2 to
|
||||||
|
# continue and it will end up trying to free
|
||||||
|
# self->dsn a second time.
|
||||||
|
print >> sys.stderr, "begin conn1 del"
|
||||||
|
del cursor, conn1
|
||||||
|
print >> sys.stderr, "end conn1 del"
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
Loading…
Reference in New Issue
Block a user