python / python/cpython

`_pyio.BufferedReader.readinto()` can raise `ValueError` after partially filling the destination

Open
#155,074 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

stdlib topic-IO type-bug
Dominant language
Python
Stars
77.2k
Forks
35.9k
PR merge metrics
PR metrics pending

Description

Bug report

Bug description:

The pure Python implementation of BufferedReader.readinto() can raise ValueError for a valid writable buffer after partially modifying it.

import io
import _pyio

for module in (io, _pyio):
    reader = module.BufferedReader(module.BytesIO(b"abcd"), buffer_size=2)
    assert reader.read(1) == b"a"

    destination = bytearray(2)
    try:
        result = reader.readinto(destination)
    except ValueError as error:
        result = f"{type(error).__name__}: {error}"

    print(module.__name__, result, destination, reader.read())

Current output on main:

io 2 bytearray(b'bc') b'd'
_pyio ValueError: memoryview assignment: lvalue and rvalue have different structures bytearray(b'b\x00') b'cd'

_pyio should match io: return 2, fill the destination with b"bc", and leave b"d" unread. readinto() first partially fills the destination with data from its internal buffer, then refills that buffer with more data than fits in the remaining space. It then raises ValueError. Because the destination has already been modified and the stream has advanced when the exception is raised, the caller receives no byte count and cannot safely retry on a non-seekable stream.

_pyio.BufferedRandom.readinto() and _pyio.BufferedRWPair.readinto() are also affected because they use the same reader path.

I am working on a PR with a regression test shared by the C and pure Python implementations.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs
  • gh-155075

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the _pyio.BufferedReader.readinto() entry point and run the reproducer from the issue, comparing it with io.BufferedReader.readinto(). Done means the pure Python path returns 2, fills the destination with b"bc", leaves b"d" unread, and covers the shared regression behavior described for BufferedRandom and BufferedRWPair.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.