Skip to content

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

Description

@px528

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]

Activity

  1. korjaa commented on Sep 1, 2022

    @korjaa

    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.

  2. px528 commented on Sep 1, 2022

    @px528
    Author
  3. encukou commented on Jan 17, 2025

    @encukou
    Member

    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.)

  4. px528 commented on Jan 17, 2025

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions