Skip to content

Conversation

picnixz
Copy link
Member

@picnixz picnixz commented Sep 24, 2025

Using the legacy PyMemerDef API is problematic for writable fields:

  • If the field is tagged as Py_T_UINT or any other unsigned type, using obj.attr = -1 does NOT raise a ValueError for legacy purposes (part of the very large commit 89f507f).
  • Instead, I'm using plain getter/setters functions so that the "batch" size for each fetchmany() is stored as an uint32_t instead. Previously it was stored as an int.
  • To prevent possible counting overflows, I'm restricting maxrows in fetchmany() to be a uint32_t which I can decrement instead of having an additional counter.
  • I don't think we'll ever need more than 2^32 rows per fetchmany() call.

📚 Documentation preview 📚: https://cpython-previews--139296.org.readthedocs.build/

@picnixz picnixz force-pushed the fix/sqlite3/fetchmany-139283 branch from b86282b to be39d9b Compare September 24, 2025 12:31
Copy link
Member

@ZeroIntensity ZeroIntensity left a comment

Choose a reason for hiding this comment

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

If the issue is that Py_T_UINT doesn't do the proper validation, I'd imagine that this is an issue in other places too. Have you considered adding a private _Py_T_UINT32 that does do the validation, so we can use it elsewhere?

}

/*[clinic input]
@critical_section
Copy link
Member

Choose a reason for hiding this comment

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

A critical section won't help us much, because nothing else in Cursor uses a critical section at the moment. All other readers will race with the setter function. We also shouldn't use a critical section here -- a relaxed atomic for uint32 will be faster, but let's do that in a different PR where we fix any thread-safety issues of _sqlite entirely.

Copy link
Member Author

@picnixz picnixz Sep 29, 2025

Choose a reason for hiding this comment

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

We also shouldn't use a critical section here -- a relaxed atomic for uint32 will be faster

Well, it was faster to write the critical section. I'll remove the critical sections from this PR for now if you want to address other thread safeties separately.

@picnixz
Copy link
Member Author

picnixz commented Sep 29, 2025

If the issue is that Py_T_UINT doesn't do the proper validation, I'd imagine that this is an issue in other places too. Have you considered adding a private _Py_T_UINT32 that does do the validation, so we can use it elsewhere?

There's an open issue for that and the API is not decided (see capi-workgroup/decisions#63). So I won't bother for that yet.

@picnixz picnixz force-pushed the fix/sqlite3/fetchmany-139283 branch from 7bb5b7a to 248400d Compare September 29, 2025 13:02
@ZeroIntensity
Copy link
Member

There's an open issue for that and the API is not decided (see capi-workgroup/decisions#63). So I won't bother for that yet.

The C API WG decides on public C API. We can add private APIs all we want. If we add _Py_T_UINT32 everywhere and then the WG decides they want to make that public, it's pretty easy to switch it.

@picnixz
Copy link
Member Author

picnixz commented Sep 29, 2025

The problem is that even with a private API, it would become too annoying in the end. I don't want to create a new private API that could later be changed to accomodate the public one. In particular, I don't want to design something that would be eventually changed. See #117031 for an initial work as well. So here, I really want to focus on fixing fetchmany() and I don't want to introduce a separate API in this PR.

@picnixz
Copy link
Member Author

picnixz commented Sep 29, 2025

@ZeroIntensity Do you have other comments on this specific fix apart the extra API that I will not add here? (even a new private API will need tests and other things that are too much for this PR in particular, however considering the next release is soon, I want this to be fixed ASAP).

Copy link
Member

@ZeroIntensity ZeroIntensity left a comment

Choose a reason for hiding this comment

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

No, this seems fine otherwise. I really think we should look into adding a private API for this afterward, especially if this problem is present in other places.

@picnixz picnixz merged commit bc172ee into python:main Sep 30, 2025
49 checks passed
@picnixz picnixz deleted the fix/sqlite3/fetchmany-139283 branch September 30, 2025 09:18
@miss-islington-app
Copy link

Thanks @picnixz for the PR 🌮🎉.. I'm working now to backport this PR to: 3.13, 3.14.
🐍🍒⛏🤖

miss-islington pushed a commit to miss-islington/cpython that referenced this pull request Sep 30, 2025
pythonGH-139296)

Passing a negative or zero size to `cursor.fetchmany()` made it fetch all rows
instead of none.

While this could be considered a security vulnerability, it was decided to treat
this issue as a regular bug as passing a non-sanitized *size* value in the first
place is not recommended.
(cherry picked from commit bc172ee)

Co-authored-by: Bénédikt Tran <[email protected]>
@miss-islington-app
Copy link

Sorry, @picnixz, I could not cleanly backport this to 3.13 due to a conflict.
Please backport using cherry_picker on command line.

cherry_picker bc172ee8307431caf4c89612e9e454081635191f 3.13

@bedevere-app
Copy link

bedevere-app bot commented Sep 30, 2025

GH-139441 is a backport of this pull request to the 3.14 branch.

@bedevere-app bedevere-app bot removed the needs backport to 3.14 bugs and security fixes label Sep 30, 2025
picnixz added a commit to picnixz/cpython that referenced this pull request Sep 30, 2025
…hmany()` (pythonGH-139296)

Passing a negative or zero size to `cursor.fetchmany()` made it fetch all rows
instead of none.

While this could be considered a security vulnerability, it was decided to treat
this issue as a regular bug as passing a non-sanitized *size* value in the first
place is not recommended.
(cherry picked from commit bc172ee)

Co-authored-by: Bénédikt Tran <[email protected]>
@bedevere-app
Copy link

bedevere-app bot commented Sep 30, 2025

GH-139444 is a backport of this pull request to the 3.13 branch.

@bedevere-app bedevere-app bot removed the needs backport to 3.13 bugs and security fixes label Sep 30, 2025
picnixz added a commit to picnixz/cpython that referenced this pull request Sep 30, 2025
…hmany()` (pythonGH-139296)

Passing a negative or zero size to `cursor.fetchmany()` made it fetch all rows
instead of none.

While this could be considered a security vulnerability, it was decided to treat
this issue as a regular bug as passing a non-sanitized *size* value in the first
place is not recommended.
(cherry picked from commit bc172ee)

Co-authored-by: Bénédikt Tran <[email protected]>
picnixz added a commit to picnixz/cpython that referenced this pull request Sep 30, 2025
…hmany()` (pythonGH-139296)

Passing a negative or zero size to `cursor.fetchmany()` made it fetch all rows
instead of none.

While this could be considered a security vulnerability, it was decided to treat
this issue as a regular bug as passing a non-sanitized *size* value in the first
place is not recommended.
(cherry picked from commit bc172ee)

Co-authored-by: Bénédikt Tran <[email protected]>
@bedevere-app
Copy link

bedevere-app bot commented Sep 30, 2025

GH-139444 is a backport of this pull request to the 3.13 branch.

1 similar comment
@bedevere-app
Copy link

bedevere-app bot commented Sep 30, 2025

GH-139444 is a backport of this pull request to the 3.13 branch.

picnixz added a commit to picnixz/cpython that referenced this pull request Sep 30, 2025
…hmany()` (pythonGH-139296)

Passing a negative or zero size to `cursor.fetchmany()` made it fetch all rows
instead of none.

While this could be considered a security vulnerability, it was decided to treat
this issue as a regular bug as passing a non-sanitized *size* value in the first
place is not recommended.
(cherry picked from commit bc172ee)

Co-authored-by: Bénédikt Tran <[email protected]>
picnixz added a commit to picnixz/cpython that referenced this pull request Sep 30, 2025
…hmany()` (pythonGH-139296)

Passing a negative or zero size to `cursor.fetchmany()` made it fetch all rows
instead of none.

While this could be considered a security vulnerability, it was decided to treat
this issue as a regular bug as passing a non-sanitized *size* value in the first
place is not recommended.
(cherry picked from commit bc172ee)

Co-authored-by: Bénédikt Tran <[email protected]>
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.

2 participants