Skip to content

Fix #1070: bytes_to_read buffer size for Python 3.14#1074

Open
RachelXiaolan wants to merge 1 commit into
pyinvoke:mainfrom
RachelXiaolan:ai-2138-round-5-bytes-to-read
Open

Fix #1070: bytes_to_read buffer size for Python 3.14#1074
RachelXiaolan wants to merge 1 commit into
pyinvoke:mainfrom
RachelXiaolan:ai-2138-round-5-bytes-to-read

Conversation

@RachelXiaolan

Copy link
Copy Markdown

Fixes #1070.

Bug

On Python 3.14, fcntl.ioctl rejects undersized mutable buffers (cpython#144206). bytes_to_read() was passing a 2-byte buffer to FIONREAD, which writes a C int (typically 4 bytes). Same root cause as the bug fixed in #1038 for _pty_size, but bytes_to_read was missed.

Worked by accident on <=3.13 thanks to CPython's 1024-byte static copy buffer. On 3.14, Connection.run() from a TTY-attached process crashes with SystemError: buffer overflow in handle_stdin.

Fix

Pass a 4-byte buffer and unpack as a signed int:

fionread = fcntl.ioctl(input_, termios.FIONREAD, b"\x00\x00\x00\x00")
return int(struct.unpack("i", fionread)[0])

Tests

tests/terminals/test_bytes_to_read.py — 3 cases:

  • buffer passed to ioctl is 4 bytes
  • result is the int parsed from the 4-byte C int
  • FIONREAD returning 0 is correctly handled
  • non-TTY fallback still returns 1

Refs: AI-2138

Fixes pyinvoke#1070.

On Python 3.14, fcntl.ioctl rejects undersized mutable buffers
(cpython#144206). `bytes_to_read()` was passing a 2-byte buffer to
FIONREAD, which writes a C int (typically 4 bytes). The same bug was
fixed for `_pty_size` in pyinvoke#1038, but `bytes_to_read` was missed.

This worked by accident on <=3.13 thanks to CPython's 1024-byte static
copy buffer that masked the size mismatch. CPython 3.14 tightened the
check, so `Connection.run()` from a TTY-attached process on 3.14 now
crashes with SystemError: buffer overflow in `handle_stdin`.

Fix: pass a 4-byte buffer (`b"\x00\x00\x00\x00"`) and unpack as a
signed int (`struct.unpack('i', ...)`) instead of signed short
(`'h'`).

Tests: tests/terminals/test_bytes_to_read.py covers:
- buffer passed to ioctl is 4 bytes
- result is the int parsed from the 4-byte C int
- FIONREAD returning 0 is correctly handled
- non-TTY fallback still returns 1

Test runs:
- New tests: 3/3 pass
- Full non-pty, non-runner suite: 774 passed (no regressions)
- Pre-existing pty/runner failures in this sandbox are env-specific
  (no real TTY, no /dev/tty) and unrelated to this change

Fixes pyinvoke#1070
Refs: AI-2138
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bytes_to_read() raises SystemError: buffer overflow on Python 3.14 (FIONREAD undersized buffer)

2 participants