Skip to content

Conversation

@picnixz
Copy link
Member

@picnixz picnixz commented Dec 27, 2025

FTR, the reason why execute is actually not affected is that the iterator we are taking is built from a list we create ourselves so it's not possible to have "bad" side effects.

@picnixz picnixz added needs backport to 3.13 bugs and security fixes needs backport to 3.14 bugs and security fixes labels Dec 27, 2025
@picnixz picnixz changed the title gh-143198: fix SIGSEGV in sqlite3.execute[many] with re-entrant parameter iterator gh-143198: fix SIGSEGV in sqlite3.execute[many] with concurrent mutations Dec 27, 2025
@picnixz picnixz marked this pull request as draft December 27, 2025 15:19
@picnixz picnixz changed the title gh-143198: fix SIGSEGV in sqlite3.execute[many] with concurrent mutations gh-143198: fix SIGSEGV in sqlite3.executemany with concurrent mutations Dec 27, 2025
@picnixz picnixz marked this pull request as ready for review December 27, 2025 16:42
@picnixz
Copy link
Member Author

picnixz commented Dec 29, 2025

There were more UAFs that I could find...

@picnixz picnixz changed the title gh-143198: fix SIGSEGV in sqlite3.executemany with concurrent mutations gh-143198: fix some crashes in sqlite3.executemany with concurrent mutations Dec 29, 2025
@picnixz picnixz force-pushed the fix/sqlite/uaf-in-cursor-143198 branch from 15f7823 to 6056bb2 Compare December 31, 2025 13:58
@picnixz picnixz force-pushed the fix/sqlite/uaf-in-cursor-143198 branch from 6056bb2 to 75b2a0c Compare December 31, 2025 14:00
@picnixz picnixz marked this pull request as draft December 31, 2025 14:04
@picnixz picnixz marked this pull request as ready for review December 31, 2025 14:04
@picnixz
Copy link
Member Author

picnixz commented Dec 31, 2025

Ok, let's avoid force-pushing more. I force pushed because I wanted a single commit for the follow-up PR

@picnixz
Copy link
Member Author

picnixz commented Dec 31, 2025

If possible, I would prefer to add checks closer to the code that can be affected by closing the connection then to the code that can close the connection.

This is not always possible and it's more prone to errors in the future. The connection may be closed but we would check it way later. I tried to only add checks when the code in question never uses the sqlite3 API (e.g., pysqlite_microprotocols_adapt is purely using the Python C API so it doesn't matter that the connection is alive or not).

I can't make the diff smaller though. It's the minimal diff on the C code I can do.

const char* binding_name;
int i;
int rc;
int rc; /* sqlite3 error code */
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a reason why I added this comment. I was myself confused when I used the same variable (one for internal C API and one for sqlite API call). Later I will rename that variable when I will refactor the functions.

Copy link
Member

@serhiy-storchaka serhiy-storchaka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for minimizing the diff, @picnixz.

It seems that most new checks are not covered by tests. At least tests were passed when I commented out them one by one. Maybe more tests are needed?

It seems that neither bind_param(), nor bind_parameters() use self->connection->db, so the check can be added only after the call of bind_parameters(). Maybe much later -- immediately before sqlite3_changes() and sqlite3_last_insert_rowid().

I wonder, what happens when we close the connection in one of numerous callbacks? Then self->connection->db can be set NULL after calling some SQLite C API, not only after calling some Python C API.

num_params = PySequence_Size(parameters);
if (num_params == -1) {
return;
if (num_params == -1 && PyErr_Occurred()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't PyErr_Occurred redundant?

Copy link
Member Author

@picnixz picnixz Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what I thought but, in release mode the check is not performed. You can have a __len__ that returns -1 without setting an exception:

    PySequenceMethods *m = Py_TYPE(s)->tp_as_sequence;
    if (m && m->sq_length) {
        Py_ssize_t len = m->sq_length(s);
        assert(_Py_CheckSlotResult(s, "__len__", len >= 0));
        return len;
    }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, you're right. I thought we directly called __len__ but it's warpped as follows:

    PyObject *res = vectorcall_method(&_Py_ID(__len__), stack, 1);
    Py_ssize_t len;

    if (res == NULL)
        return -1;

    Py_SETREF(res, _PyNumber_Index(res));
    if (res == NULL)
        return -1;

So returning -1 always implies that there is an exception set here. I'll remove that check.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can still get -1 for the extension class. But this is a bug in the extension.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want me to re-add the check though?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because on DEBUG builds there's some assert that would be crashing if the extension class does something bad

@picnixz
Copy link
Member Author

picnixz commented Jan 10, 2026

@serhiy-storchaka I've extensively added test cases for the adpater's part. I didn't check all __conform__ or __adapt__ methods because these calls are wrapped in pysqlite_microprotocols_adapt so I don't really need to care about it (as soon as I check after the call whether it's fine or not should be sufficient).

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants