FIX: ODBC Catalog method fetchone() issue #520
+157
−0
Azure Pipelines / MSSQL-Python-PR-Validation
succeeded
Apr 21, 2026 in 30m 16s
Build #pr-validation-pipeline had test failures
Details
- Failed: 1 (0.00%)
- Passed: 28,538 (97.26%)
- Other: 803 (2.74%)
- Total: 29,342
- 6672 of 8434 lines covered (79.11%)
Annotations
Check failure on line 1 in test_aggressive_dbc_segfault_reproduction
azure-pipelines / MSSQL-Python-PR-Validation
test_aggressive_dbc_segfault_reproduction
subprocess.TimeoutExpired: Command '['/Library/Frameworks/Python.framework/Versions/3.13/bin/python', '-c', '\nimport sys\nimport gc\nfrom mssql_python import connect\n\nprint("=== AGGRESSIVE DBC SEGFAULT TEST ===")\nprint("Creating many DBC handles and forcing shutdown...")\n\n# Create many connections without closing them\n# This maximizes the chance of DBC handles being finalized\n# AFTER the static ENV handle has destructed\nconnections = []\nfor i in range(5): # Reduced for faster execution\n conn = connect("Server=tcp:127.0.0.1,1433;Database=master;Uid=SA;Pwd=Azure@123!;TrustServerCertificate=yes")\n # Don\'t even create cursors - just DBC handles\n connections.append(conn)\n\nprint(f"Created {len(connections)} DBC handles")\nprint("Forcing GC to ensure objects are tracked...")\ngc.collect()\n\n# Delete the list but objects are still alive in GC\ndel connections\n\nprint("WARNING: About to exit with unclosed DBC handles")\nprint("If Type 2 (DBC) handles are not protected, this may SEGFAULT")\nprint("Stack trace will show: SQLFreeHandle -> SqlHandle::free() -> finalize_garbage")\n\n# Force immediate exit - this triggers finalize_garbage\nsys.exit(0)\n']' timed out after 5 seconds
Raw output
self = <test_013_SqlHandle_free_shutdown.TestHandleFreeShutdown object at 0x108f12210>
conn_str = 'Server=tcp:127.0.0.1,1433;Database=master;Uid=SA;Pwd=Azure@123!;TrustServerCertificate=yes'
def test_aggressive_dbc_segfault_reproduction(self, conn_str):
"""
AGGRESSIVE TEST: Try to reproduce DBC handle segfault during shutdown.
This test aggressively attempts to trigger the segfault described in the stack trace
by creating many DBC handles and forcing Python to shut down while they're still alive.
Current vulnerability: DBC handles (Type 2) are NOT protected during shutdown,
so they will call SQLFreeHandle during finalization, potentially accessing
the already-destructed static ENV handle.
Expected with CURRENT CODE: May segfault (this is the bug we're testing for)
Expected with FIXED CODE: No segfault
"""
script = textwrap.dedent(f"""
import sys
import gc
from mssql_python import connect
print("=== AGGRESSIVE DBC SEGFAULT TEST ===")
print("Creating many DBC handles and forcing shutdown...")
# Create many connections without closing them
# This maximizes the chance of DBC handles being finalized
# AFTER the static ENV handle has destructed
connections = []
for i in range(5): # Reduced for faster execution
conn = connect("{conn_str}")
# Don't even create cursors - just DBC handles
connections.append(conn)
print(f"Created {{len(connections)}} DBC handles")
print("Forcing GC to ensure objects are tracked...")
gc.collect()
# Delete the list but objects are still alive in GC
del connections
print("WARNING: About to exit with unclosed DBC handles")
print("If Type 2 (DBC) handles are not protected, this may SEGFAULT")
print("Stack trace will show: SQLFreeHandle -> SqlHandle::free() -> finalize_garbage")
# Force immediate exit - this triggers finalize_garbage
sys.exit(0)
""")
> result = subprocess.run(
[sys.executable, "-c", script], capture_output=True, text=True, timeout=5
)
tests/test_013_SqlHandle_free_shutdown.py:87:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/subprocess.py:556: in run
stdout, stderr = process.communicate(input, timeout=timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/subprocess.py:1222: in communicate
stdout, stderr = self._communicate(input, endtime, timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/subprocess.py:2155: in _communicate
self._check_timeout(endtime, orig_timeout, stdout, stderr)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <Popen: returncode: -9 args: ['/Library/Frameworks/Python.framework/Versions...>
endtime = 1995.637093599, orig_timeout = 5, stdout_seq = [], stderr_seq = []
skip_check_and_raise = False
def _check_timeout(self, endtime, orig_timeout, stdout_seq, stderr_seq,
skip_check_and_raise=False):
"""Convenience for checking if a timeout has expired."""
if endtime is None:
return
if skip_check_and_raise or _time() > endtime:
> raise TimeoutExpired(
self.args, orig_timeout,
output=b''.join(stdout_seq) if stdout_seq else None,
stderr=b''.join(stderr_seq) if stderr_seq else None)
E subprocess.TimeoutExpired: Command '['/Library/Frameworks/Python.framework/Versi
Loading