Skip to content

send_all can silently truncate typed memoryviews after partial writes #3543

Open

Description

@AryanRG

SocketStream.send_all() and POSIX FdStream.send_all() use byte counts returned by send() / os.write() as memoryview element offsets. For a contiguous memoryview with itemsize > 1, a partial write can skip data and return successfully without sending the full buffer.

Reproduced on macOS with Python 3.12.15 at main 4630d02872c0aa5778ce0afd702c5d532a2ccf60. This uses real pipes without patching the write operation:

import os
import trio

async def main():
    read_fd, write_fd = os.pipe()
    payload = bytes(range(256)) * 4096
    received = bytearray()
    async with trio.lowlevel.FdStream(write_fd) as sender, trio.lowlevel.FdStream(read_fd) as receiver:
        async def send():
            with memoryview(payload).cast("H") as data:
                await sender.send_all(data)
            await sender.aclose()

        async with trio.open_nursery() as nursery:
            nursery.start_soon(send)
            async for chunk in receiver:
                received.extend(chunk)

    print(len(payload), len(received), received == payload)

trio.run(main)

Expected: 1048576 1048576 True. Observed: 1048576 524288 False. Exact truncation depends on the OS write sizes. The socket retry loop has the same mismatch; tests limiting real socket sends to three bytes also reproduce it. Multidimensional views have the same problem. Scalar views additionally fail when the implementation asks for their length on Python 3.12+.

A local fix casts nonempty contiguous views to a one-dimensional byte view before slicing. It preserves empty strided-view handling. Six focused regression cases fail against unchanged main and pass with the fix; two empty-view compatibility cases pass in both versions.

AI disclosure: this report, the proposed fix, and its tests were produced by an autonomous OpenAI Codex agent at the account owner's request. Validation and independent review were performed by AI agents; no human review is claimed.

Activity

  1. A5rocks commented on Oct 16, 2026

    @A5rocks
    Contributor

    I think we should probably disallow ndim != 1 buffers! or... do people actually remember what their stride is? (also it just occurs to me that these methods actually support collections.abc.Buffer rather than bytes | bytearray | memoryview.)

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions