Skip to content

struct.Struct.iter_unpack crashes when __buffer__ reinitializes the Struct to a zero-sized format #158695

Description

@augusto-rehfeldt

Bug report

Bug description:

Documented behaviour: The struct documentation, under struct.iter_unpack, states: “The buffer’s size in bytes must be a multiple of the size required by the format.” A zero-sized format cannot define the fixed-size chunks described there: “This function returns an iterator which will read equally-sized chunks from the buffer until all its contents have been consumed.”

Expected: Raise struct.error after detecting the zero-sized format, without crashing.

Actual: The subprocess exits with status 3221225620 (0xC0000094, integer division by zero).

import struct, subprocess, sys

case = {'initial': 'B', 'replacement': '0s', 'buffer': b'', 'token': None}
if sys.version_info < (3, 12):
    print('REFUTATION REJECTED:', 'Python 3.12+ is required for __buffer__')
else:
    size = struct.calcsize(case['initial'])
    valid = size > 0 and len(case['buffer']) % size == 0
    expected = 'raises struct.error' if struct.calcsize(case['replacement']) == 0 else None
    if not valid or expected is None:
        print('REFUTATION REJECTED:', 'input or zero-sized-format premise is invalid')
    else:
        code = """
import struct
S = struct.Struct('B')
class Exporter:
    def __buffer__(self, flags):
        S.__init__('0s')
        return memoryview(b'')
try:
    S.iter_unpack(Exporter())
except struct.error:
    print('raises struct.error')
except BaseException as e:
    print(type(e).__name__, str(e))
else:
    print('returned iterator')
"""
        p = subprocess.run([sys.executable, '-I', '-c', code],
                           capture_output=True, text=True)
        actual = p.stdout.strip() if p.returncode == 0 else ('process exited', p.returncode)
        if actual != expected:
            print('REFUTATION CONFIRMED:', case, 'actual =', actual, 'expected =', expected)
        else:
            print('REFUTATION REJECTED:', 'the call raised struct.error as expected')

Output on Python 3.14.6 (Windows-11-10.0.26220-SP0), standard library struct:

REFUTATION CONFIRMED: {'initial': 'B', 'replacement': '0s', 'buffer': b'', 'token': None} actual = ('process exited', 3221225620) expected = raises struct.error

This report was found and written by an automated property-testing tool I run (bugforge). The reproducer above was executed and its output is pasted unedited; no person reviewed the report before it was filed. The search script is in https://github.com/augusto-rehfeldt/bugforge-results/tree/main/struct-20261003-232219-c4

CPython versions tested on:

3.14

Operating systems tested on:

Windows

Activity

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions