Skip to content

fix(libkernel): return errno from fstat, flock and write instead of throwing on a bad argument - #4929

Closed
xXPBSXx wants to merge 2 commits into
boykopovar:mainfrom
xXPBSXx:fix/descriptor-calls-throw
Closed

xXPBSXx wants to merge 2 commits into
boykopovar:mainfrom
xXPBSXx:fix/descriptor-calls-throw

Conversation

@xXPBSXx

@xXPBSXx xXPBSXx commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

What

Three calls in libkernel Stdio.cpp threw a C++ exception on an argument FreeBSD answers with an errno. A throw out of a guest call ends the process, where the title would see -1 and handle it:

Call Argument Was FreeBSD 11.4
fstat null struct stat throws EFAULT (14) (the copyout fails)
flock closed descriptor or -1 throws flock failed ... error=6 EBADF (9), sys_flock takes the descriptor with fget first
write negative length (a size above LONG_MAX; the test covers -1 only) throws EINVAL (22), sys_write fails nbyte > IOSIZE_MAX (sys_write in sys/kern/sys_generic.c begins with if (uap->nbyte > IOSIZE_MAX) return (EINVAL); sys/sys/systm.h lines 165-169 define IOSIZE_MAX as iosize_max() on LP64 and SSIZE_MAX otherwise, and iosize_max() in sys_generic.c returns SSIZE_MAX, or INT_MAX for a 32-bit process or with the debug.iosize_max_clamp sysctl set (default 0); all read in release/11.4.0; a negative length is above it)

(fstat on a closed descriptor or -1 threw as well; #4391 fixed that on main, and the test keeps checking it.) flock keeps its throw for host errors it does not know; only ERROR_INVALID_HANDLE (Windows) and EBADF (Linux) become EBADF. The negative-length check of write comes before the guest-socket branch, so it applies to a guest socket as well; the test covers a file descriptor only.

Found by calling these functions with invalid arguments, one process per call, on main; the other calls tried (fsync, fdatasync, fchmod, futimes, ftruncate, lseek, readv, writev, preadv, pwritev, pwrite, ioctl, dup, dup2, fcntl, read with a null buffer) already return EBADF or EFAULT. pread throws too; the open #4719 changes that (git merge-tree of the two heads is clean).

No title evidence: I did not observe a title that calls one of these on a bad descriptor. The reason to fix them is that they end the process instead of failing the call, not a conformance detail.

Overlap: #4903 (fstat on a guest socket) is open and the overlap bot lists it for core/libs/prx/libkernel/File/src/Stdio.cpp L645-648, the fstat head next to the null check rewritten here; whichever lands second needs a small rebase. #5403 contains #4903's commit and conflicts here for that reason: it replaces the line after the null check, this PR rewrites the null check itself. Both changes apply together (sb == nullptr returns EFAULT, then a guest socket reports its status). Otherwise git merge-tree of the head d3e58a1ce against main bf8dc610e is clean (checked, not rebuilt); the test registration sits before guest_socket_descriptors in core/libs/prx/libkernel/CMakeLists.txt, away from the other new tests. #4818 (fail write with EINVAL for a size above SSIZE_MAX, open, older) contains the same one-line change in write (if (size < 0) return PosixFailure(GUEST_EINVAL);); git merge-tree of the two is clean, whichever lands second drops the line.

Tested

Windows 10 (19045), MinGW-w64 GCC 15.2.0 (WinLibs r7), Release, on main c2565f014; moved since to a newer main (git merge-tree of the head against main bf8dc610e is clean; not rebuilt there) with its test registration at its own place:

  • guest_descriptor_bad_arguments (new) passes: all three cases and the fstat check on a closed descriptor, plus fstat and flock on an open file still succeed.
  • It fails without the change to Stdio.cpp (fstat_nid_disambig1_nid_postfix: invalid argument, abort). The test stops at its first failing check, so that run names only the first case (fstat with a null buffer); the write and flock cases were not run one by one without the change (the old code throws in each of them, see the diff).
  • The guest_file*, guest_dup, guest_socket*, guest_poll, guest_select and guest_filesystem tests pass.
  • python tools/check_conventions.py --base upstream/main passes.
  • Re-run on 14 October 2026 (Windows 10 19045, MinGW-w64 GCC 15.2.0, Release, -j4), the head d2e7f675f merged onto main 7a0c838ca: guest_descriptor_bad_arguments passes (0.62 s, ctest); with Stdio.cpp taken back to main and the new test kept it fails (uncaught invalid_argument, exit code 0xc0000409). Second run, head merged onto main d1670009f, the targets of the group built as well: on main alone guest_filesystem, guest_file_vector, guest_memory, guest_kernel_sys, guest_signals and guest_directory_buffer pass (6 of 6); with the head and its test 7 of 7 pass, 0 failed. This replaces the earlier combined-build figures. Not run: the socket tests, Linux, a console, a title.
  • Second commit d2e7f675f (test only): guest_descriptor_bad_arguments removes a leftover test file at its start and closes its descriptor and removes the file before a failing check ends the process. Rebuilt and run on the new head (base 1e7f4967e, Windows 10, MinGW GCC 15.2.0, Release, -j4): guest_descriptor_bad_arguments passes; with Stdio.cpp taken back to the base it fails (the old code throws invalid_argument, exit code 0xc0000409); python tools/check_conventions.py --base upstream/main passes. A run that ends by such a throw still leaves the file behind (the process does not reach the test's own cleanup); the next run removes it first.
  • Not tested: Linux (the flock branch for EBADF and the fstat change build from one source, only Windows was built), a console, a title run.

Checklist

AI-assisted (Claude Code).

🤖 Generated with Claude Code

…hrowing on a bad argument

fstat with a null buffer, flock on a closed descriptor and write with a negative length threw, which ends the guest process. FreeBSD 11.4 fails them with EFAULT, EBADF and EINVAL (copyout, sys_flock fget, sys_write nbyte > IOSIZE_MAX).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@xXPBSXx
xXPBSXx force-pushed the fix/descriptor-calls-throw branch from 73022dc to d3e58a1 Compare October 13, 2026 14:21
…uments

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@oneandonlydean

Copy link
Copy Markdown
Collaborator

Closing: no title is known to reach this. With the queue at 700+ open PRs, please hold off on new PRs that don't name a title needing the change until it's down. If a title stops on this on current main, reopen 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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants