Skip to content

ctypes infinite pointer cache #100926

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
earonesty opened this issue Jan 10, 2023 · 7 comments
Closed

ctypes infinite pointer cache #100926

earonesty opened this issue Jan 10, 2023 · 7 comments
Labels
extension-modules C modules in the Modules dir topic-ctypes type-bug An unexpected behavior, bug, or error

Comments

@earonesty
Copy link

earonesty commented Jan 10, 2023

Bug(ish?) report

The following function has a cache. If you are using a factory to call ctypes.POINTER in a loop, the memory usage is unbounded and unable to be reclaimed.

https://docs.python.org/3/library/ctypes.html#ctypes.POINTER

The documentation should mention that this is unbounded and should not be called in a loop, or the cache should be changed to a configurable, bounded LRU cache.

Example of a variable length type factory used by windows, and a bad func that cannot be called in a loop:

def shitemid_factory(size: int) -> Type[ctypes.Structure]:
    class SHITEMID_Var(ctypes.Structure):
        _fields_ = (
            ("cb", USHORT),
            ("abID", BYTE * size),
        )

    return SHITEMID_Var

def bad_func():
    SHITEMID_Var = shitemid_factory(sz - ctypes.sizeof(USHORT))
    item_var = ctypes.cast(item_ptr, ctypes.POINTER(SHITEMID_Var))

Linked PRs

@mara004
Copy link
Contributor

mara004 commented Mar 9, 2025

We've run into a similar issue with pypdfium2: pypdfium2-team/pypdfium2#346
An unlimited cache for pointer types seems problematic. Also, the docs aren't very clear about the peril of using POINTER() to get the data of variable-size arrays, or what alternatives there are.

@picnixz picnixz added the extension-modules C modules in the Modules dir label Mar 9, 2025
@picnixz
Copy link
Member

picnixz commented Mar 9, 2025

cc @encukou

@encukou
Copy link
Member

encukou commented Mar 11, 2025

I'm aware of the issue, can bump it up my list of priorities. Still not sure I'll be able to get a fix in 3.14.
If anyone wants to take a stab at it before I get to it, I can review/discuss.

IMO, the POINTER should be cached in stginfo, rather than a global cache, so when a type goes away so does the pointer to it (there'd probably be a ref cycle for the GC to resolve, but that's better than an unbounded cache).
I've only peeked down this rabbit hole enough to see that it's not straightforward, but should be solvable.

@mara004
Copy link
Contributor

mara004 commented Mar 11, 2025

Thanks.
In the meantime, is (type * size).from_address(addressof(first_ptr.contents)) OK to work around this issue and replace our previous use of cast(first_ptr, POINTER(type * size)).contents ?

@sergey-miryanov
Copy link
Contributor

I'm aware of the issue, can bump it up my list of priorities. Still not sure I'll be able to get a fix in 3.14. If anyone wants to take a stab at it before I get to it, I can review/discuss.

I would like to try, if you don't mind.

@sergey-miryanov
Copy link
Contributor

@encukou I have made a PR, please take a look.

sergey-miryanov added a commit to sergey-miryanov/cpython that referenced this issue Mar 15, 2025
sergey-miryanov added a commit to sergey-miryanov/cpython that referenced this issue Mar 21, 2025
sergey-miryanov added a commit to sergey-miryanov/cpython that referenced this issue Mar 24, 2025
sergey-miryanov added a commit to sergey-miryanov/cpython that referenced this issue Mar 26, 2025
sergey-miryanov added a commit to sergey-miryanov/cpython that referenced this issue Mar 26, 2025
sergey-miryanov added a commit to sergey-miryanov/cpython that referenced this issue Mar 30, 2025
sergey-miryanov added a commit to sergey-miryanov/cpython that referenced this issue Mar 30, 2025
sergey-miryanov added a commit to sergey-miryanov/cpython that referenced this issue Apr 2, 2025
sergey-miryanov added a commit to sergey-miryanov/cpython that referenced this issue Apr 26, 2025
sergey-miryanov added a commit to sergey-miryanov/cpython that referenced this issue May 1, 2025
sergey-miryanov added a commit to sergey-miryanov/cpython that referenced this issue May 2, 2025
encukou added a commit to sergey-miryanov/cpython that referenced this issue May 2, 2025
encukou added a commit that referenced this issue May 2, 2025
…gInfo (GH-131282)

Deprecate _pointer_type_cache and calling POINTER on a string.

Co-authored-by: neonene <[email protected]>
Co-authored-by: Jun Komoda <[email protected]>
Co-authored-by: Petr Viktorin <[email protected]>
@encukou
Copy link
Member

encukou commented May 2, 2025

Thanks @earonesty for the report, @sergey-miryanov for the PR, @neonene for input & reviews, @mara004 and @picnixz for pings!

@junkmd, you'll want something like this to avoid deprecation warnings:

-        from ctypes import _pointer_type_cache  # type: ignore
-        _pointer_type_cache[self] = p
+        if sys.version_info >= (3, 14):
+            self.__pointer_type__ = p
+        else:
+            from ctypes import _pointer_type_cache  # type: ignore
+            _pointer_type_cache[self] = p

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
extension-modules C modules in the Modules dir topic-ctypes type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

6 participants