Skip to content

fix(libkernel): answer F_GETFD and F_SETFD on a guest socket - #5020

Merged
adriadam10 merged 2 commits into
boykopovar:mainfrom
xXPBSXx:fix/socket-fd-flags
Oct 14, 2026
Merged

adriadam10 merged 2 commits into
boykopovar:mainfrom
xXPBSXx:fix/socket-fd-flags

Conversation

@xXPBSXx

@xXPBSXx xXPBSXx commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

What

fcntl on a guest socket answered F_GETFL and F_SETFL and failed every other command with EINVAL. libcurl calls fcntl(fd, F_SETFD, FD_CLOEXEC) on every new socket, and it failed:

  • Title evidence: PPSA99001 (ProsperoRadio, bundled libcurl 8.18.0 and OpenSSL 3.5.2 over libkernel sockets), traced by screamm on main in this comment on #4796: fcntl(F_SETFD) on a socket returns EINVAL (the comment lists it among the "smaller mismatches curl tolerates, for later"); libcurl issues fcntl(fd, F_SETFD, FD_CLOEXEC) on every new socket. The same group of titles (PSXS5, SwanStation, ProsperoTV, ProsperoRadio) uses that libcurl. No title fails because of this answer: curl ignores the error, so the change replaces a wrong answer in the trace of these titles and does not unblock one.

F_GETFD and F_SETFD are now answered for a guest socket as FreeBSD 11.4's kern_fcntl does (FD_CLOEXEC, value 1):

  • F_SETFD sets or clears the flag, F_GETFD reads it back (a new socket starts at 0);
  • SOCK_CLOEXEC in the type of socket sets it at creation;
  • dup and dup2 give the copy no flag (FreeBSD's kern_dup clears it) and leave the original's; close forgets it, so the next socket on the same number starts at 0.

The flag is only recorded: there is no exec in the guest. Not changed, and in the TechnicalDebt entry for libkernel: the other commands on a socket still fail with EINVAL (F_DUPFD, F_DUPFD_CLOEXEC, F_GETOWN, F_SETOWN), and an accepted socket starts without the flag.

Overlap: this branch was merged with main 00f4b3351 (conflict in docs/dev/debt/libkernel.md next to the ioctl entry, resolved by keeping both entries; git merge-tree against main is clean). #4954 (fcntl and O_APPEND), #5186 (FIOCLEX and FIONCLEX) and #5569 (ENOTTY for malformed ioctl requests) touch the same part of docs/dev/debt/libkernel.md; whichever lands second rebases its entry.

Tested

Windows 10 (19045), MinGW-w64 GCC 15.2.0 (WinLibs r7), Release, head 863e506b2 (this branch merged with main 00f4b3351):

  • The socket group passes, 25 of 25 (guest_socket*, including the twelve guest_socket_host_write_*, guest_sce_net, guest_udp, guest_tcp, guest_poll, guest_dup, guest_kernel_fcntl), ctest -V, serial, after building the test executables of exactly these tests (log run_5020_fix2.log). guest_socket_descriptor_flags (new) is one of them: F_GETFD on a new socket, F_SETFD and read-back, dup and dup2 give the copy no flag and keep the original's, F_SETFD 0, SOCK_CLOEXEC, and the next socket after a closed one starts at 0.
  • Without the change to Udp.cpp (taken from main, test kept; measured on the previous head b09eccdc4, whose Udp.cpp is the same) it fails: a new socket must not be close-on-exec (F_GETFD 0) (errno 22) (log run_5020.log).
  • A first run on that previous head showed 12 failures in the guest_socket_host_write_* tests; the cause was an outdated test executable (not rebuilt). After rebuilding it they pass, here and on main.
  • python tools/check_conventions.py --base upstream/main passes.
  • Not tested: Linux (one source for both hosts, only Windows was built), a console, a title run with this change (the evidence is screamm's trace).

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 in TechnicalDebt (docs/dev/debt/libkernel.md)
  • No notes, investigation .md files or images added to the repository
  • New third-party code is a submodule built from source (none added)
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Unblocks: PPSA99001 (ProsperoRadio) and the titles of its group: a clean fcntl trace on their sockets; no bring-up discussion exists
  • AI-assisted: yes

AI-assisted (Claude Code).

🤖 Generated with Claude Code

xXPBSXx and others added 2 commits October 14, 2026 19:28
fcntl on a guest socket handled F_GETFL and F_SETFL only and failed every other command with EINVAL, so libcurl's fcntl(fd, F_SETFD, FD_CLOEXEC) on a new socket failed (ProsperoRadio PPSA99001, traced by screamm on boykopovar#4796). Record FD_CLOEXEC per socket descriptor: F_SETFD sets or clears it, F_GETFD reads it, SOCK_CLOEXEC sets it at creation, and dup, dup2 and close start without it, as FreeBSD's kern_fcntl and kern_dup do.

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

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

Copy link
Copy Markdown
Collaborator

Checked on current main (merged and rebased): build + ctest green (169 tests in core/libs and libkernel), and guest_socket_descriptor_flags fails without the Udp.cpp change. F_GETFD/F_SETFD, SOCK_CLOEXEC, dup/dup2 and close match FreeBSD's kern_fcntl/kern_dup.

Added on your branch: rebased onto main (conflict in docs/dev/debt/libkernel.md, kept both entries) and updated the fcntl entry, which still said F_GETFD/F_SETFD on a socket fail with EINVAL. Merging once the full Build is green.

@adriadam10
adriadam10 merged commit 5922a72 into boykopovar:main Oct 14, 2026
8 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.

2 participants