Skip to content

Fix reference leak and double-DECREF in pyodbc.drivers() (#1503)#1505

Open
apoorvdarshan wants to merge 1 commit into
mkleehammer:masterfrom
apoorvdarshan:fix-1503-drivers-refcount
Open

Fix reference leak and double-DECREF in pyodbc.drivers() (#1503)#1505
apoorvdarshan wants to merge 1 commit into
mkleehammer:masterfrom
apoorvdarshan:fix-1503-drivers-refcount

Conversation

@apoorvdarshan

Copy link
Copy Markdown

Summary

Fixes #1503. mod_drivers() (backing pyodbc.drivers()) had two reference-counting bugs around the Object RAII wrapper.

1. Leaked driver name after PyList_Append

Object name(PyUnicode_FromString((const char*)szDriverDesc));  // owns a new ref
if (PyList_Append(result, name.Get()) != 0)                    // keeps its own ref
    return 0;
name.Detach();   // clears the wrapper WITHOUT decref -> our ref leaks

PyList_Append increments the refcount, so the local reference should be released by ~Object(). Detach() prevents that, leaking one string per driver per call. Fix: drop the Detach().

2. Double-DECREF of result on the ODBC error path

Object result(PyList_New(0));
...
if (ret != SQL_NO_DATA) {
    Py_DECREF(result);   // decrefs the list but doesn't clear the wrapper
    return RaiseErrorFromHandle(...);   // ~Object() then decrefs it AGAIN
}

The manual Py_DECREF plus the destructor double-frees the list on the driver-manager error path. Fix: drop the manual Py_DECREF and let ~Object() release it once.

Verification

Leak (bug 1), built locally against unixODBC with three dummy drivers, using sys.getallocatedblocks():

build block growth over 20 000 drivers() calls
master 60001 (3 per call = one per driver)
this PR 1 (~0)

pyodbc.drivers() still returns the same list. Bug 2 is on the ODBC error path (hard to trigger without a broken driver manager), but the double-DECREF follows directly from the Object wrapper semantics — the destructor already releases result.

Disclosure: prepared with AI assistance; reviewed and verified locally.

mod_drivers() had two reference-counting bugs around the Object RAII wrapper:

1. Leak: after PyList_Append() (which keeps its own reference), the code called
   name.Detach(), which clears the wrapper without decrefing, leaking the string
   for every driver on every call. Drop the Detach() so the destructor releases it.

2. Double-DECREF: on the ODBC error path it did Py_DECREF(result) and then
   returned, after which ~Object() decref'd the same list again, double-freeing
   it. Drop the manual Py_DECREF and let the destructor release it once.

Verified the leak fix with sys.getallocatedblocks(): a 3-driver configuration
leaked 3 blocks per call before (60001 over 20000 calls) and ~0 after.

Fixes mkleehammer#1503.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reference leak and double-DECREF in pyodbc.drivers()

1 participant