mkleehammer / pyodbc

Python ODBC bridge
https://github.com/mkleehammer/pyodbc/wiki
MIT No Attribution
2.92k stars 561 forks source link

Fix segfault for empty iterators #1274

Closed ndmlny-qs closed 11 months ago

ndmlny-qs commented 1 year ago

This commit returns false if the first cell in GetTableInfo is NULL, and relies on Python's exception handling of the error instead of issuing a segfault.

You can reproduce the segfault by doing the following with pyodbc 5.0.0b.

container

Create a container running MS SQL 2017.

# Pull the server image provided by Microsoft
podman pull mcr.microsoft.com/mssql/server:2017-latest
# Create and start a container from the image we just pulled.
podman run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=StrongPassword2017" -p 1401:1433 \
    --name mssql2017 --hostname mssql2017 -d mcr.microsoft.com/mssql/server:2017-latest
# Log into the container so we can create a test database.
podman exec -it mssql2017 "bash"
# Once in the container, run `sqlcmd` with the user and password supplied in the GitHub actions file.
/opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P "StrongPassword2017"

To create the test database in sqlcmd, enter each line below and press enter.

CREATE DATABASE test
GO
QUIT
# Exit out of the running pod
exit

python

Start a python session and run the following.

from collections.abc import Sequence
import pyodbc

class MySequence(Sequence):
    def __getitem__(self, index):
        raise Exception
    def __len__(self):
        return 1

connection = pyodbc.connect("DRIVER={ODBC Driver 17 for SQL Server};SERVER=127.0.0.1,1401;UID=sa;PWD=StrongPassword2017;DATABASE=test")
connection.execute("SELECT ?, ?", 123, MySequence()).fetchone()

This results in Segmentation fault (core dumped). With the applied changes in the PR, the exception in the MySequence class is raised leading to the following error in the Python interpreter.

----> 1 connection.execute("SELECT ?, ?", 123, MySequence()).fetchone()

Cell In[3], line 3, in MySequence.__getitem__(self, index)
      2 def __getitem__(self, index):
----> 3     raise Exception

Exception:
mkleehammer commented 11 months ago

Thanks for this.