Skip to content

fix(libc): read /dev/random and /dev/urandom through fopen - #6541

Merged
adriadam10 merged 1 commit into
boykopovar:mainfrom
wachi35676:fix/libc-fopen-random-device
Oct 19, 2026
Merged

adriadam10 merged 1 commit into
boykopovar:mainfrom
wachi35676:fix/libc-fopen-random-device

Conversation

@wachi35676

Copy link
Copy Markdown
Contributor

What

fopen("/dev/urandom", "rb") (and /dev/random) failed with ENOENT: fopen_nid_postfix resolved the path to a host file that does not exist, while libkernel's open and sceKernelOpen already serve both paths from host entropy (RandomDevice.cpp, #1777, #4200).

A read-only fopen of exactly /dev/random or /dev/urandom now returns a stream over the host null device, marked as a random device, and fread on it fills the buffer with HostEntropy_nid_no_patch, as the libkernel descriptors do (FreeBSD random(4): reads never block and never hit end of file).

  • setvbuf, feof, ferror, clearerr, fclose and the stream locks (flockfile, ftrylockfile, funlockfile, _Lockfilelock, _Unlockfilelock) work on it. fflush returns 0, as FreeBSD's __sflush does for a stream that is not writing.
  • Every other stream function throws through GetNativeStream, so nothing silently reads the null device. Opening the paths for writing throws, as in libkernel.
  • freopen clears the mark; freopen onto these paths still fails with ENOENT (listed in the libc debt file with the rest).

Title: ProsperoTV (PPSA99003, 01.000.070, the author's GPL-3.0 release) reads its phone-pairing code with fopen("/dev/urandom", "rb") + fread (src/iptv_remote.cpp, Random()). On main, Settings → Pair a phone starts the remote server but always shows "Code expired or unavailable" (3 of 3 runs). With this change it shows a 6-digit code and a countdown (3 of 3 runs).

Tested

  • guest_filesystem gets a check (open, setvbuf, two freads that differ, feof/ferror, the locks, fflush, an unsupported call throws, fclose). On main it fails at the fopen (null stream, guest errno 2); with this change it passes.
    • Windows 11, WinLibs GCC 15.2.0: 25 related stream, file and random tests pass (guest_filesystem, file_streams, guest_standard_streams, guest_host_streams, guest_file_lock_*, guest_formatting, guest_random*, …).
    • Linux (WSL Ubuntu 24.04, GCC 13.3): guest_filesystem passes; full tree build passes, including NID patching of every library.
  • Rebased on main 44df21632, after fix(libc): fail stream calls on a stream that a failed freopen closed #5632 (closed-stream checks), which this keeps in front of the new code. python tools/check_conventions.py --base origin/main passes.
  • Not tested: macOS; pairing a phone end to end.

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
  • Unblocks: ProsperoTV (PPSA99003) phone pairing
  • GPUs tested: n/a (libc stream change; the title runs used an NVIDIA RTX 5070 Ti, Windows 11)
  • AI-assisted: yes

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Oct 15, 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 6819 docs/dev/debt/libc.md L83-84

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 3987 docs/dev/debt/libc.md
PR 5021 core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/RuntimeSupport.cpp
core/libs/prx/libc/src/StandardStreams.cpp
docs/dev/debt/libc.md
PR 5303 docs/dev/debt/libc.md
PR 6044 core/libs/tests/GuestFilesystem.cpp
PR 6226 docs/dev/debt/libc.md
PR 6469 docs/dev/debt/libc.md
PR 6897 core/libs/tests/GuestFilesystem.cpp
PR 7039 docs/dev/debt/libc.md
PR 7081 core/libs/prx/libc/src/FileAndHeap.cpp
docs/dev/debt/libc.md
PR 7201 core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/FileAndHeap.cpp
core/libs/prx/libc/src/RuntimeSupport.cpp
core/libs/prx/libc/src/StandardStreams.cpp
PR 7332 core/libs/prx/libc/include/FileStream.hpp
core/libs/prx/libc/src/FileAndHeap.cpp
core/libs/prx/libc/src/RuntimeSupport.cpp
core/libs/prx/libc/src/StandardStreams.cpp
docs/dev/debt/libc.md
PR 7473 docs/dev/debt/libc.md
PR 7545 docs/dev/debt/libc.md
PR 7715 docs/dev/debt/libc.md

@screamm

screamm commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Cross-validation on Windows 11 Pro 26220, NVIDIA RTX 4070 (596.21), MinGW GCC 15.2, PR head 48e967497 merged on main 5d9dcc42d (clean merge):

  • Full build and libs: pass, apart from the known MinGW 64 KiB alignas AGC test object that fails on main too (test(agc): align the indirect arguments block at run time #6452/fix(agc): allocate indirect arguments test block dynamically on Windows #6478).
  • guest_filesystem, --repeat until-fail:3: 3 of 3 pass. 14 related stream, file-lock and random tests also pass.
  • Negative control: with the four libc sources reverted to main and the new test kept, guest_filesystem fails at the new Require(stream != nullptr) after fopen("/dev/urandom", "rb") (guest errno 2); with the sources restored it passes again.
  • ctest -j8 --timeout 120 -E '^agc_driver_|^video_out_flip': 829 of 837 pass, 2 skipped, 6 timeouts on a heavily loaded host. On serial reruns conventions_function_bodies, tls_function_coverage_register_address_loads and two conventions shards pass; conventions_function_bodies_shard_2 still hits its 30 s limit under load but its script takes about the same time on this branch and on main when timed directly (15-17 s vs 14 s); hardware_oracle_compare also hits its 20 s limit on main. None of them is about libc streams.
  • tools/check_conventions.py --base origin/main and git diff --check: pass.
  • ProsperoTV PPSA99003 (my local copy is 01.000.050, not 01.000.070), relinked, 130 s per run, scripted pad input (R1 x4 to Settings, Down x5 to "Pair a phone", Cross), dialog captured at 125 s:
    • main: 3 of 3 runs that reached the dialog show "Code expired or unavailable / Press X for a new code" (a fourth run lost an R1 press and ended on Sources). No stop line; the title keeps running and is killed at the limit.
    • This PR: 2 of 2 runs show a 6-digit code (a different one each run) and "Expires in 71 seconds". No stop line, killed at the limit.
    • Frames presented at the kill: 4911, 3839, 3624 on main, 5489 and 3908 with the PR (20-46 FPS because the host was busy).

Not checked: pairing an actual phone end to end, Linux/macOS, freopen onto these paths.

Written with Claude Code assistance (validation by a subagent, reviewed in this session).

fopen resolved the random device paths to a host file that does not
exist and failed with ENOENT, while open and sceKernelOpen already serve
them from host entropy. A read-only fopen of either path now returns a
stream whose fread fills the buffer with host entropy. setvbuf, feof,
ferror, clearerr, the stream locks and fclose work on it, fflush returns
0 as on a FreeBSD stream that is not writing, other stream functions
throw, and opening the paths for writing throws as in libkernel.

ProsperoTV (PPSA99003) reads its phone pairing code this way and showed
"Code expired or unavailable".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@wachi35676
wachi35676 force-pushed the fix/libc-fopen-random-device branch from 48e9674 to b4e80e8 Compare October 18, 2026 22:31
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 18, 2026
@wachi35676

Copy link
Copy Markdown
Contributor Author

Validated against a second title: VLC (PPSA85300, ZiZc3/VLC v0.4.6), relinked with --windows --windows-fixed-base 0x400000, on main 8e7904b20 with and without this PR, 3 runs each.

  • On main, every run logs net: no /dev/urandom: HTTPS won't work.
  • With this PR, every run logs net: /dev/urandom ok.

Windows 11 26300, RTX 5070 Ti (32.0.16.1742), WinLibs GCC 15.2.0, Release.

AI-assisted: yes (Claude Code); results above were run on this machine.

@adriadam10 adriadam10 self-assigned this Oct 19, 2026
@adriadam10
adriadam10 merged commit dc54701 into boykopovar:main Oct 19, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs libc unblocks title Names a title ID on the Unblocks line

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants