Skip to content

ctypes: bitfield lost data with union on linux platform #95496

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
px528 opened this issue Jul 31, 2022 · 4 comments
Closed

ctypes: bitfield lost data with union on linux platform #95496

px528 opened this issue Jul 31, 2022 · 4 comments
Labels
topic-ctypes type-bug An unexpected behavior, bug, or error

Comments

@px528
Copy link

px528 commented Jul 31, 2022

below example is simple and can be reproduced.

from ctypes import Structure,Union
from ctypes import alignment,sizeof
from ctypes import c_int8,c_int16,c_int32,c_int64
from ctypes import c_uint8,c_uint16,c_uint32,c_uint64

class sub_type(Structure):
    _fields_ = [
    ('a',c_uint8,4),
    ('b',c_uint8,4),
    ('c',c_uint16,10),
    ('d',c_uint16,10),
    ('e',c_uint8,1),
    ('f',c_uint8,1),
    ('g',c_uint8,1),
    ]

class main_type(Union):
    byte_length = sizeof(sub_type)
    _fields_ = [
                ("data",    sub_type),
                ("asbytes",  c_uint8*byte_length),
    ]

a=main_type(data=sub_type(e=1,f=1,g=1))
print(list(a.asbytes))

#on windows, print value is correct
#[0, 0, 0, 0, 0, 0, 7, 0]

#on linux, the value is wrong at all. the length is correct, but value is wrong.
#[0, 0, 0, 0, 0, 0]
@korjaa
Copy link

korjaa commented Sep 1, 2022

I get similar error without unions. Below is a test snippet that passes on Windows but fails on Ubuntu WSL2.

import ctypes

for field_width in range(32, 1, -1):
    class TestStruct(ctypes.Structure):
        _fields_ = [
            ("Field1", ctypes.c_uint32, field_width),
            ("Field2", ctypes.c_uint8, 8)
        ]

    cmd = TestStruct()
    cmd.Field2 = 1
    if cmd.Field2 != 1:
        raise RuntimeError(f"{field_width=}, {cmd.Field2=} != 1")

print("All good")

I get following output in WSL2 Ubuntu

$ python --version
Python 3.8.10

$ python -c 'import ctypes; print(ctypes.__version__)'
1.1.0

$ python ctypes_test.py
Traceback (most recent call last):
  File "ctypes_test.py", line 13, in <module>
    raise RuntimeError(f"{field_width=}, {cmd.Field2=} != 1")
RuntimeError: field_width=24, cmd.Field2=0 != 1

I tried Python 3.9 on WSL2 Ubuntu too with same result.

@px528
Copy link
Author

px528 commented Sep 1, 2022 via email

@encukou
Copy link
Member

encukou commented Jan 17, 2025

This is fixed in #97702 and related fixes. Should be released in Python 3.14; unfortunately the fix is backwards-incompatible so I don't plan to backport it.

Note that bitfields use a different layout on Linux (gcc/sysV): the output will be [0, 0, 0, 0, 0, 28]. But the values are in the proper place:

for name in 'abcdefg':
    print(name, getattr(a.data, name))
# → zeros for abce, ones for efg

To get the Windows behaviour you can add _layout_ = 'ms' to TestStruct. (The mechanism might still change before the 3.14.0 final release; see Structure docs for current docs.)

@encukou encukou closed this as completed Jan 17, 2025
@px528
Copy link
Author

px528 commented Jan 17, 2025 via email

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

No branches or pull requests

5 participants