GeezerVison is showing github.com — 1.10.x: segfault when garbage-collecting an unclosed connection (#69 not fixed on release-1.x) · Issue #77 · FirebirdSQL/python3-driver
1.10.x: segfault when garbage-collecting an unclosed connection (#69 not fixed on release-1.x) #77DescriptionSummary
On the 1.x line (tested with firebird-driver 1.10.11 / firebird-base 1.8.0), letting the garbage collector finalize a Connection that was never closed segfaults the Python process. This is the same crash as #69. #72 fixed it on master on 2026-05-21, but that fix is not on release-1.x, and it is also not in any 2.x release yet (2.0.3 was published 2026-04-20).
On 1.x, iAttachment.detach() (the v5 override in interfaces.py) calls the native detach without the if self._refcnt: guard that iAttachment_v4.detach() has. During cyclic garbage collection the interface can be released first, and Connection.__del__ then detaches freed memory.
Minimal reproduction
Firebird 5.0.3 server, CPython 3.11.15, Linux x86_64, firebird-driver 1.10.11:
import gc, os, sys
from firebird.driver import connect, driver_config
driver_config.fb_client_library.value = os.environ["FB_CLIENT_LIBRARY"]
DSN, PASSWORD = os.environ["FB_DSN"], os.environ["FB_PASSWORD"]
def abandon():
con = connect(DSN, user="SYSDBA", password=PASSWORD)
cur = con.cursor()
cur.execute("select count(*) from rdb$relations")
cur.fetchall()
# no con.close(): the connection is left to the garbage collector
for i in range(100):
abandon()
gc.collect()
print("completed without crash")
Output (python -X faulthandler repro.py), 3 of 3 runs, exit code 139:
Fatal Python error: Segmentation fault
File ".../firebird/driver/interfaces.py", line 1236 in detach
File ".../firebird/driver/core.py", line 1672 in __del__
File "repro.py", line 18 in <module>
With the #69 guard applied to release-1.x (if self._refcnt: around the native detach in the v5 iAttachment.detach()), the same script completes 100 iterations without a crash in 3 of 3 runs.
Impact
Any unclosed connection can crash the process when it is garbage collected. Examples include a connection pool that drops a connection it failed to close (a SQLAlchemy dialect whose terminate hook fails on invalidation), or application code that forgets close(). Applications that cannot move to 2.x yet (2.x requires firebird-base~=2.0 → protobuf~=5.29) are stuck on 1.10.11.
Request
- Backport the segfault in garbage collecting #69 guard to
release-1.x and publish a 1.10.x release. A backport PR follows.
- If possible, publish a 2.x release that includes Fix segfault in garbage collection of abandoned connection (#69) #72.
Activityaminghadersohi commented on Sep 26, 2026 Follow-up: the detach() guard from #72 / #78 fixes Connection.__del__ → iAttachment.detach(), but other finalization orders in the same cycle still crash on 1.10.11.
When the attachment's own iReferenceCounted.__del__ releases it first, the remaining finalizers still call into its children. For example, Cursor.__del__ → Cursor.close() → iResultSet.close() calls into a result set whose attachment is already gone. Guarding on the wrapper's own _refcnt doesn't catch this, because the result set wrapper still holds its reference.
Minimal reproduction (Firebird 5.0.3, CPython 3.11, firebird-driver 1.10.11 with the #78 change applied):
import gc
from firebird.driver import connect
def abandon():
con = connect(DSN, user="SYSDBA", password=PASSWORD)
cur = con.cursor()
cur.execute("select rdb$relation_id from rdb$relations")
cur.fetchone() # result set left open
con.cycle = [con, cur] # force collection by the cyclic GC
cur.cycle = con
for _ in range(100):
abandon()
gc.collect()
This segfaults in interfaces.py iResultSet.close (via Cursor.__del__). An open BlobReader, or an active TransactionManager with a prepared statement, crashes the same way.
A fix that covers all orders: while a garbage collection is running, defer every native call that releases an interface (release, detach, commit, rollback, close, free, cancel). Then replay those calls right after the collection, children first: result sets and blobs, then statements, then transactions, then attachments. This can hook gc.callbacks. With that change, the scenarios above run 100 iterations without a crash on Firebird 3.0.14, 4.0.7 and 5.0.3, and leave no server attachments behind. added a commit that references this issue on Oct 7, 2026This issue will close once commit 375055e is merged into the 'master' branch.375055e
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|