Skip to content
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

gh-126937: ctypes: fix TypeError when a field's size is >65535 bytes #126938

Open
wants to merge 20 commits into
base: main
Choose a base branch
from

Conversation

Melissa0x1f992
Copy link

@Melissa0x1f992 Melissa0x1f992 commented Nov 17, 2024

Used the bit_size_obj as seems to be intended, based on its usage in Lib/ctypes/_layout.py

Tested the change locally informally. Didn't write any tests.

Copy link

cpython-cla-bot bot commented Nov 17, 2024

All commit authors signed the Contributor License Agreement.
CLA signed

@bedevere-app
Copy link

bedevere-app bot commented Nov 17, 2024

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

@Melissa0x1f992
Copy link
Author

Need some help understanding the lint error on this

@terryjreedy
Copy link
Member

terryjreedy commented Nov 17, 2024

By somewhat random clicking of the search box and the test stage line, I got the full log. There were 6 spaces after the final \n, thus trailing spaces failed. (Note that running patchcheck as suggested somewhere would have fixed this.)

I asked to rerun just the one test, but the edit triggered all.

@terryjreedy
Copy link
Member

I can't review the actual change. It might help someone else if you could connect the change to the change in 3.14 that caused the regression. To find the latter, I would first run git blame on cfield.c

@picnixz
Copy link
Contributor

picnixz commented Nov 18, 2024

cc @encukou

@Melissa0x1f992
Copy link
Author

Added a test. It doesn't have any assertions bc the failure case is an error getting raised. Not sure if this is the appropriate way to add such a test.

@Melissa0x1f992
Copy link
Author

For the Win32, I don't have that setup. Could I get some help determining what field size should pass the < (1ULL << (8*sizeof(Py_ssize_t)-1)) / 8) test on Win32? Am I off by one? I don't want to commit (2^31 - 1 - 1) unless there's a good reason to consider that one byte smaller the limit for Win32.

@Melissa0x1f992
Copy link
Author

Is there a way for the test to set up different field sizes based on the current platform, and is that desirable? I'd like to test the actual max size, regardless of platform, rather than the smallest of the different platform maxes.

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.

Some initial comments :)

@@ -0,0 +1 @@
Fixed TypeError when a Structure's field's size is >65535 bytes
Copy link
Member

@ZeroIntensity ZeroIntensity Nov 18, 2024

Choose a reason for hiding this comment

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

Suggested change
Fixed TypeError when a Structure's field's size is >65535 bytes
Fix :exc:`TypeError` when a :class:`ctypes.Structure` has a field size that doesn't fit into an unsigned 16-bit integer.

Py_ssize_t bit_size = NUM_BITS(size);
if (bit_size) {
if (bit_size_obj != Py_None) {

Copy link
Member

Choose a reason for hiding this comment

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

No need for a newline

Suggested change

Py_ssize_t bit_size;

if (PyLong_Check(bit_size_obj)) {
bit_size = PyLong_AsSsize_t(bit_size_obj);
Copy link
Member

Choose a reason for hiding this comment

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

PyLong_AsSsize_t can fail, you need to check for < 0 and then goto error.

if (bit_size) {
if (bit_size_obj != Py_None) {

Py_ssize_t bit_size;
Copy link
Member

Choose a reason for hiding this comment

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

Lint is complaining about this. I'm assuming this just needs to get moved to before the if

@@ -29,12 +29,12 @@ repos:
- id: black
name: Run Black on Tools/build/check_warnings.py
files: ^Tools/build/check_warnings.py
language_version: python3.12
language_version: python3
Copy link
Member

Choose a reason for hiding this comment

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

Let's revert these here, and deal with them in #126971:

Suggested change
language_version: python3
language_version: python3.12

args: [--line-length=79]
- id: black
name: Run Black on Tools/jit/
files: ^Tools/jit/
language_version: python3.12
language_version: python3
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
language_version: python3
language_version: python3.12

@vstinner vstinner changed the title gh-126937: Fix TypeError when a field's size is >65535 bytes gh-126937, ctypes: Fix TypeError when a field's size is >65535 bytes Nov 19, 2024
@encukou
Copy link
Member

encukou commented Nov 20, 2024

Thank you! That's the correct fix; there's a few details to iron out.
Here's how I'd write the start of the if (adding an explanation):

    if (bit_size_obj != Py_None) {
#ifdef Py_DEBUG
        Py_ssize_t bit_size = NUM_BITS(size);
        assert(bit_size > 0);
        assert(bit_size <= info->size * 8);
        // Currently, the bit size is specified redundantly
        // in NUM_BITS(size) and bit_size_obj.
        // Verify that they match.
        assert(PyLong_AsSsize_t(bit_size_obj) == bit_size);
#endif
        switch(info->ffi_type_pointer.type) {
            ...

(In Py_DEBUG builds, assert does nothing so a linter complains about unused bit_size. That #ifdef should fix this.)
(Putting each piece of data in one proper place is better left to another PR; I've started working on that.)

Is there a way for the test to set up different field sizes based on the current platform, and is that desirable?

Yes. sizeof(Py_ssize_t) in C is ctypes.sizeof(ctypes.c_size_t) in Python.

Added a test. It doesn't have any assertions bc the failure case is an error getting raised. Not sure if this is the appropriate way to add such a test.

It is; ideally with a comment like # this should succeed:. You could also add a with self.assertRaises block for the failing case.


@Melissa0x1f992, if at any time working on this PR stops being fun for you, let me know and I can finish it. I don't know if you're driven by curiosity or just want the bug fixed :)
Your perspective is certainly helpful.

@encukou
Copy link
Member

encukou commented Nov 29, 2024

Do you still want to work on this PR?
If not I can work on it next week.

@picnixz picnixz changed the title gh-126937, ctypes: Fix TypeError when a field's size is >65535 bytes gh-126937: ctypes: fix TypeError when a field's size is >65535 bytes Nov 29, 2024
@Melissa0x1f992
Copy link
Author

encukou, thank you! Yeah, life's gotten busy, so I won't be around to see this through.

It was a fun experience, and I hope to contribute on other things in the future 🙂

@encukou
Copy link
Member

encukou commented Dec 4, 2024

OK. Thank you for the fix!
I hope you don't mind me pushing to your branch -- this way I can keep changes in this PR, with the discussion.

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

Successfully merging this pull request may close these issues.

7 participants