Repository navigation
fix(libc): open closed standard descriptors on /dev/null when libc loads - #6107
Conversation
|
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.
|
c4c90bd to
a7ef31e
Compare
|
Rebased onto current main (head a7ef31e). The only conflict was in docs/dev/debt/libc.md, where main had expanded the neighbouring Checks are green and it merges cleanly. Could you take a look when you have a moment, and merge if it looks right? |
|
Windows check of head
Written with Claude Code assistance (validation by a subagent, reviewed in this session). |
|
Checked the initializer in |
|
@adriadam10 thanks for checking it. It's still open; it merges cleanly onto current |
What
A converted title started with its standard input closed (a launcher or a shell that closes it,
./app.elf <&-) gets one of AnyPS5's own descriptors as its standard input. The first descriptor the libraries open takes the lowest free number, and on Linux that is the write watch'suserfaultfd:/proc/<pid>/fd/0readsanon_inode:[userfaultfd]about 100 ms in. Yamagi Quake II (PPSA99007) callsfcntlon descriptor 0 at start-up and stops onThe same happens to descriptors 1 and 2: a guest write to a closed stdout would go to whatever AnyPS5 opened in its place.
StandardStreams.cppopens/dev/nullon each of descriptors 0, 1 and 2 that is closed when libc loads, read-only for 0 and write-only for 1 and 2, the way glibc'scheck_fds.cdoes for set-user-ID programs. libc is loaded and initialized before the libraries that depend on it, so this runs before any of them opens a descriptor. Descriptors that are open are not touched. Linux and macOS only.guest_closed_standard_descriptors: the test runs itself again with stdin, stdout, or all three closed, and the child checks that each closed descriptor is/dev/nullwith the expected access mode by the timemainruns, and that the guestfcntlon descriptor 0 succeeds. If a libc initializer opened a descriptor before this one, it would take descriptor 0 and the check would fail.Title. Yamagi Quake II (PPSA99007, catalog release) with
./app.elf <&-: onmainit aborts on thefcntlthrow above in each of 3 runs. With this PR it no longer aborts and runs the same as when started with</dev/null(in both cases it then leaves on its own after start-up and tears down cleanly with status 0).Tested
Linux (Bazzite 44, GCC 16.2.1, Ninja, Release,
-DBUILD_TESTING=ON), RX 7700 XT (RADV), based onmain922abe6a4:ctest: 1187 of 1211 pass and 21 are skipped (display tests).agc_driver_tessellationandapplication_heap_lazysegfault, andagc_host_import_sparsefails, on unchangedmainhere too (test(agc): drop a shared range's backing when the range goes away #5759 fixes the last one).guest_closed_standard_descriptorspasses 5 of 5 (--repeat until-fail:5). With the previousStandardStreams.cppit fails on its first case (child exited with 74: descriptor 0 is not/dev/null).main4808d9c20, hidden display, no controller): as above.tools/check_conventions.py --base upstream/mainpasses.tools/pr_overlap.pyreports feat(libc): implement wide and unlocked stream I/O #5816 indocs/dev/debt/libc.mdonly (a neighbouring line). Windows and macOS were not built here.Checklist
main; no other open PR implements the same functions.mdfiles or images added to the repository