Skip to content

libkernel: writing to a pipe whose reader is closed returns EINVAL on Windows instead of EPIPE; fix branch ready #5246

Closed as not planned

Description

@husseinkad

On Windows, write, writev, sceKernelWrite and sceKernelWritev to a pipe whose read end has been closed fail with EINVAL instead of EPIPE. The host C runtime maps ERROR_NO_DATA (232) to EINVAL, and FreeBSD 11.4 pipe_write returns EPIPE. A guest that treats EPIPE as "the reader went away" (the usual way a writer ends cleanly) sees a generic invalid-argument error instead.

Fix branch: https://github.com/husseinkad/AnyPS5/tree/fix/libkernel-pipe-write-epipe (one commit on a recent main: File/src/Open.cpp NativeWrite, the TransferIovecs error path in File/src/Stdio.cpp, and the new test tests/GuestPipeClosedWrite.cpp, registered as guest_pipe_closed_write). ERROR_NO_DATA is now remapped to EPIPE in both places. NativeTransfer is left alone because #4885 changes it.

Checks:

  • The new test fails on main (abort) and passes with the change; guest_file_vector, guest_kernel_fcntl and guest_dup pass.
  • Probed through the guest exports against main: a write of 1, 4096, 4097 bytes and 1 MiB, and writev with 1 to 4 iovecs (including zero-length ones), now give EPIPE (32) for blocking and nonblocking pipes, and sceKernelWrite/sceKernelWritev return 0x80020020. A 1 MiB write blocked on a 4096-byte pipe wakes with EPIPE when the reader closes (with two blocked writers main returned EPIPE for one and EINVAL for the other; now both get EPIPE), with no hangs. Unchanged against main: a full pipe (EAGAIN or blocking), a write to the read end and to a closed or invalid fd (EBADF), and writes to a regular file, a read-only file and a socket. A direct probe through both ucrtbase.dll and msvcrt.dll shows the same EINVAL with _doserrno 232, which matters because local toolchains and CI use different C runtimes.
  • Two independent reviews found nothing wrong with the change. check_conventions.py is clean and pr_overlap.py finds no overlap; fix(libkernel): return EAGAIN for nonblocking vector pipe I/O on Windows #4885 edits NativeTransfer nearby, so a small rebase of the Stdio.cpp hunk may be needed.

Not covered: no SIGPIPE is raised on Windows (already recorded in docs/dev/debt/libkernel.md); a write that is part done when the reader closes returns EPIPE instead of the partial count through the single-buffer path (the iovec path does return the count); the check reads _doserrno without clearing it first. A zero-length write and a zero-length writev on a broken pipe return 0 where FreeBSD returns EPIPE, and a nonblocking writev on a full pipe gives ENOSPC rather than EAGAIN; both are the same on main. Only Windows was built and run.

AI-assisted: yes

Activity

  1. MarcZX10 commented on Oct 13, 2026

    @MarcZX10
    Contributor

    Implemented in #5265. I built guest_pipe_closed_write_tests on Linux and ran guest_pipe_closed_write; the test passed (1/1). The Windows-specific EPIPE mapping was not executable in my Linux environment. The implementation also guards the Windows-only error check so it does not affect Linux compilation.

  2. oneandonlydean commented on Oct 14, 2026

    @oneandonlydean
    Collaborator

    Closing: no title is known to reach this, and functions are only changed when a title needs them. #5265 is closed for the same reason. If a title stops on or misbehaves because of it on current main, comment here with the title ID and the log line.

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