Skip to content

fix(libc): fail stream calls on a stream that a failed freopen closed - #5632

Merged
adriadam10 merged 1 commit into
boykopovar:mainfrom
babyhuey:fix/libc-closed-stream-writes
Oct 15, 2026
Merged

adriadam10 merged 1 commit into
boykopovar:mainfrom
babyhuey:fix/libc-closed-stream-writes

Conversation

@babyhuey

@babyhuey babyhuey commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

What

A freopen that fails closes the stream, on FreeBSD as on the host. Later calls on that stream fail with EBADF on FreeBSD: a write returns EOF and fflush returns EOF. In AnyPS5 every libc call on such a FileStream reached GetHandle() and threw FileStream: closed stream. On Windows, printf's PrintWindows wrote to the closed host stdout and threw Formatted output write failed.

  • FileStream::IsOpen() reports whether the host handle is still there.
  • On a closed stream these now set errno to 9 (EBADF) and return their failure value instead of throwing:
    • writes: fputs, fputc, putc_unlocked, fwrite (0), vfprintf/fprintf, and the stdout writers printf, libc_printf, vprintf and puts;
    • reads: fread (0), fgetc, getc_unlocked, ungetc, fgets (null), fscanf;
    • positioning and the rest: fseeko, ftello, fflush, fileno, setvbuf, fclose.
  • SyncStatus, SyncUnlockedStatus and ClearError do nothing on a closed stream, so feof, ferror and clearerr no longer throw. Close() has already cleared its flags.
  • fclose on a closed dynamic stream still frees it.
  • The lock calls (flockfile, funlockfile, ftrylockfile) still throw on a closed stream, as CheckTryLock expects.
  • FileStreams.cpp expected fflush on a closed stream to throw. It now expects -1 with errno 9, as FreeBSD's fflush returns for a stream that is neither reading nor writing.

The wide writers (fputwc, fputws) already catch the throw and return -1.

Tested

On the PR head 292752f (rebased onto main 58d98de after #4904 and #4728; the test file merge keeps both sides), Linux (Ubuntu 24.04, GCC 13) and Windows 11 (MinGW-w64 GCC 15.2 from WinLibs), Release:

  • guest_standard_streams, guest_standard_streams_closed_stdout (new: it reopens the guest stdout to a missing directory and checks printf, vprintf, puts and fputs) and file_streams each pass 5/5 (--repeat until-fail:5) on both. The new closed-stream checks fail on main. The Windows run of --closed-stdout shows no UCRT invalid-parameter report.
  • No warnings in the files this PR touches.

PS5LM Plus (PPSA99581, catalog release 0.8.3), with #5605 merged (its stop on main is the absent sceKernelGetSocPowerConsumption). At start-up it calls freopen("/data/PS5LM/app.log", "w", stdout) and freopen(..., "a", stderr). /data/PS5LM doesn't exist, so both fail and it goes on printing.

  • Without this PR it stops at once: on Linux fputs throws FileStream: closed stream; on Windows printf throws Formatted output write failed, then fprintf and fflush throw too as each is reached.
  • With this PR it keeps running. On Windows it draws its UI ("PS5LM Plus v0.8.3", "NO MODEL", "GENERATION / Not serving") and starts its HTTP server on :8081. It then stops for user data: no models; copy a .gguf to /data/PS5LM/models. On Linux it gets to the headless DisplayWindow throw.

A separate problem remains, not changed here: on Windows the failed freopen also closes the host process's native stdout. The libraries' own logging then trips the UCRT invalid-parameter handler on every line (about 740 reports in 60 s for PS5LM Plus), which slows it to about one frame. That needs the standard streams handled so a failed guest freopen doesn't close the host's, and is left for a follow-up.

The fflush change touches the same function as #4904 (which returns flush failures instead of throwing), in a different place.

check_conventions.py passes.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none. feat(libkernel): export sceKernelGetSocPowerConsumption #5605 is merged, and PS5LM Plus reaches this on main 63fb0cd: it now stops at FileStream: closed stream 0.5 s in on Linux and at Formatted output write failed (exit 0xC0000409) about 1 s in on Windows.
  • Unblocks: PPSA99581 PS5LM Plus (PPSA99581) #5601
  • AI-assisted: yes

@babyhuey
babyhuey force-pushed the fix/libc-closed-stream-writes branch from d862be2 to 292752f Compare October 14, 2026 06:39
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 14, 2026
@babyhuey

Copy link
Copy Markdown
Contributor Author

#5605 is merged, so PS5LM Plus (PPSA99581) now stops at this PR's fix on main 63fb0cd without any other open pull request:

  • Linux: FileStream: closed stream thrown from fputs, 0.5 s in.
  • Windows 11 (RTX 3080, WinLibs GCC 15.2, Release, --windows --registry): three UCRT invalid parameter reports, then Formatted output write failed and exit 0xC0000409 about 1 s in.

These are the same stops described above for main with #5605 merged locally. I updated Depends on to say nothing open.

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 6018 core/libs/tests/GuestStandardStreams.cpp L502-503

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 3891 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/StandardStreams.cpp
PR 3965 core/libs/prx/libc/CMakeLists.txt
PR 4156 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/StandardStreams.cpp
PR 4396 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/StandardStreams.cpp
PR 4397 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/StandardStreams.cpp
PR 4422 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/StandardStreams.cpp
PR 4423 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/StandardStreams.cpp
PR 4424 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/StandardStreams.cpp
PR 4520 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/StandardStreams.cpp
PR 4521 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/StandardStreams.cpp
PR 4522 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/StandardStreams.cpp
PR 4523 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/StandardStreams.cpp
PR 4566 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/StandardStreams.cpp
PR 4599 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/StandardStreams.cpp
PR 4718 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/StandardStreams.cpp
PR 4740 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/StandardStreams.cpp
PR 4749 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/StandardStreams.cpp
PR 4760 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/StandardStreams.cpp
PR 4813 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/StandardStreams.cpp
PR 5021 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/StandardStreams.cpp
PR 5835 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libc/src/FileAndHeap.cpp
PR 6044 core/libs/prx/libc/CMakeLists.txt
core/libs/tests/FileStreams.cpp
core/libs/tests/GuestStandardStreams.cpp
PR 6107 core/libs/prx/libc/src/StandardStreams.cpp
PR 6222 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/StandardStreams.cpp

@YossiYad

Copy link
Copy Markdown
Contributor

The Linux part with a display, since the run above stopped at the headless DisplayWindow throw. PS5LM Plus (PPSA99581, 0.8.3), main 4808d9c20 with this PR's head 292752ff5 merged, nothing else open, Bazzite 44 (GCC 16.2.1, Release), RX 7700 XT on RADV, /data/PS5LM absent:

  • Without the PR: what(): FileStream: closed stream from fputs, at start-up, in 2 of 2 runs. The stack at the throw holds the line it was printing, [UHI] program batch2d built in 15 ms.
  • With the PR: it keeps running and presents at 59.94 fps for the whole 40 s run.
  • With /data/PS5LM created by hand and no PR, it also runs and writes app.log and models/, which confirms the stop is the failed freopen and not something later.

@adriadam10

Copy link
Copy Markdown
Collaborator

Checked on the PR merged onto main: file_streams, guest_standard_streams and the new guest_standard_streams_closed_stdout pass, and all three fail with the library changes reverted. Behaviour matches FreeBSD (EBADF, EOF/-1/0/NULL) for the touched calls; the lock calls still throw as CheckTryLock expects. Title runs confirmed by @YossiYad (Linux/RADV) and the author (Windows).

Conventions was red on the Depends on line; I changed it to none. in the PR body (the #5605 note is kept). Merging once the full Build is green.

@adriadam10
adriadam10 merged commit 21ca964 into boykopovar:main Oct 15, 2026
13 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants