Skip to content

fix(libkernel): set and clear close-on-exec with FIOCLEX and FIONCLEX on a socket - #13695

Open
Retr0G28 wants to merge 2 commits into
boykopovar:mainfrom
Retr0G28:fix/ioctl-fioclex-socket
Open

Retr0G28 wants to merge 2 commits into
boykopovar:mainfrom
Retr0G28:fix/ioctl-fioclex-socket

Conversation

@Retr0G28

Copy link
Copy Markdown
Contributor

What

On a guest socket, ioctl(fd, FIOCLEX) and ioctl(fd, FIONCLEX) failed: with a null argument, the usual call for these _IO requests, they returned EFAULT, and with a pointer ENOTTY. The socket path of ioctl checks the argument and then accepts only FIONBIO and FIONREAD. On files and pipes, File::Ioctl already handles both requests.

FreeBSD 11.4's kern_ioctl handles FIOCLEX and FIONCLEX for every descriptor before calling the file's fo_ioctl. It sets or clears UF_EXCLOSE and returns 0, so soo_ioctl never sees them, and F_GETFD then reports the flag.

The socket path of ioctl_nid_postfix now handles both requests after the descriptor lookup and before the argument check. They insert into or erase from closeOnExecDescriptors, the set that the socket fcntl F_GETFD and F_SETFD already read and write. The request is compared on its low 32 bits, as File::Ioctl does. The debt entry now lists the two requests with FIONBIO and FIONREAD.

libSceNet sockets are not changed. Their fcntl answers only F_GETFL and F_SETFL, so they have no close-on-exec flag to set yet.

Tested

Windows 11, WinLibs MinGW-w64 GCC 15.2.0, CMake + Ninja, Release, -DBUILD_TESTING=ON, on main 7ceda13.

  • guest_kernel_fcntl now checks, on an AF_INET datagram socket, that FIOCLEX returns 0 and F_GETFD reports 1, and that FIONCLEX returns 0 and F_GETFD reports 0. Each request is tried with a null argument and with a pointer.
  • With main's Udp.cpp, the new checks fail (exit 0xc0000409 from the failed Require). With this PR they pass.
  • The 44 tests matching fcntl|ioctl|socket|recvmsg|sendfile|^guest_udp$|^guest_poll$ pass.
  • python tools/check_conventions.py --base origin/main reports no errors. Its one warning is the length of the ioctl debt entry, which was 1650 characters on main and is 1672 here.
  • Not run on Linux, where the socket path is the same code. No title or homebrew run.

Overlap (tools/pr_overlap.py): it reports no conflict among the PRs it could compare. Of the rest, and the PRs that change the same function:

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: fix(libkernel): reject empty ioctl requests and answer INET socket requests as measured on a PS5 #11219
  • Fixes: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

@Noxy-HNR

Noxy-HNR commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Windows data point for 4b3b93f0ec: Windows 11 (build 26300), Intel Core Ultra 7 255HX, WinLibs MinGW-w64 GCC 15.2.0, Release, BUILD_TESTING=ON. Built and ran guest_kernel_fcntl on this PR and on its base 7ceda1326c, built the same way: it passes on both, and the PR build includes the four new FIOCLEX/FIONCLEX assertions on a socket.

Reading the change: 0x20006601 and 0x20006602 are _IO('f', 1) and _IO('f', 2), so they are the right request words for FIOCLEX and FIONCLEX, neither takes an argument, and the new branch updates closeOnExecDescriptors under socketsMutex like the existing F_SETFD path, so both ways of setting the flag agree.

@github-actions

github-actions Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

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 4630 docs/dev/debt/libkernel.md
PR 5021 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 6936 docs/dev/debt/libkernel.md
PR 7088 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 7105 docs/dev/debt/libkernel.md
PR 7121 docs/dev/debt/libkernel.md
PR 7135 docs/dev/debt/libkernel.md
PR 7149 docs/dev/debt/libkernel.md
PR 7192 docs/dev/debt/libkernel.md
PR 7447 docs/dev/debt/libkernel.md
PR 7464 docs/dev/debt/libkernel.md
PR 7804 docs/dev/debt/libkernel.md
PR 9156 docs/dev/debt/libkernel.md
PR 9199 docs/dev/debt/libkernel.md
PR 9293 docs/dev/debt/libkernel.md
PR 9297 docs/dev/debt/libkernel.md
PR 9302 docs/dev/debt/libkernel.md
PR 9389 docs/dev/debt/libkernel.md
PR 9557 docs/dev/debt/libkernel.md
PR 9566 docs/dev/debt/libkernel.md
PR 9843 docs/dev/debt/libkernel.md
PR 9938 docs/dev/debt/libkernel.md
PR 10025 docs/dev/debt/libkernel.md
PR 10319 docs/dev/debt/libkernel.md
PR 10442 docs/dev/debt/libkernel.md
PR 11896 core/libs/prx/libkernel/Socket/src/Udp.cpp
docs/dev/debt/libkernel.md
PR 11939 docs/dev/debt/libkernel.md
PR 12104 docs/dev/debt/libkernel.md
PR 12560 docs/dev/debt/libkernel.md
PR 14212 docs/dev/debt/libkernel.md

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — looks good

The socket ioctl path sets and clears the same descriptor flag reported by F_GETFD, and the added checks cover both request forms and null/non-null arguments.

What I ran

Reviewed head 4b3b93f0ec292d978f4bbb038c4f2ad59aa3275c against PR base f0c0b2d19a6ab6bfb11de584bfc38895816ccaf5; GitHub reports it mergeable and clean. The stack preview used current target bff0289787c98570491c8f175179c8a5651c8017; #13695 and #11219 preview individually cleanly, while their combined preview conflicts only in the debt file. Environment: Linux 6.1, Python 3.11.2, GCC 12.2.0. No build or compile was run.

python3 tools/pr_precheck.py --repo boykopovar/AnyPS5 --pr 13695
git diff --check f0c0b2d19a6ab6bfb11de584bfc38895816ccaf5...4b3b93f0ec292d978f4bbb038c4f2ad59aa3275c
python3 tools/check_conventions.py --base f0c0b2d19a6ab6bfb11de584bfc38895816ccaf5 --head 4b3b93f0ec292d978f4bbb038c4f2ad59aa3275c --title 'fix(libkernel): set and clear close-on-exec with FIOCLEX and FIONCLEX on a socket' --body /tmp/anyps5-pr-precheck/boykopovar-AnyPS5-pr-13695-4b3b93f0ec29/pr-body.md
python3 tools/pr_stack_check.py 13695 --repo boykopovar/AnyPS5
python3 tools/pr_test_quality.py --root /tmp/anyps5-pr-precheck/boykopovar-AnyPS5-pr-13695-4b3b93f0ec29/head-4b3b93f0ec29-a051f1fd --json core/libs/tests/GuestKernelFcntl.cpp

Results

Check Result
Diff whitespace Passed.
Conventions and PR template Passed with one existing-style debt-entry length warning (1,672 characters).
Test registration Confirmed guest_kernel_fcntl is registered in core/libs/prx/libkernel/CMakeLists.txt. The heuristic's possible-unregistered warning is a false positive.
Optional audit helpers The isolated precheck worktree lacks the workspace-local ABI and test-quality helper scripts; the ABI audit is inapplicable (no exports changed), and I ran the test-quality helper directly.
Dependency and mergeability PR is clean against its base. #11219 remains open; a pairwise preview auto-merges the code and tests but conflicts in docs/dev/debt/libkernel.md. The declared chain also includes #9713, which currently conflicts with the target.
Hosted checks Conventions, Windows, eligibility, intake, labels, library-portability, and report passed; Linux and specialized jobs were skipped.
Test execution The author reports the focused test and 44 related tests passed on Windows. I did not run those C++ tests locally.
Relevant raw output
$ git diff --check f0c0b2d19a6ab6bfb11de584bfc38895816ccaf5...4b3b93f0ec292d978f4bbb038c4f2ad59aa3275c
(no output)

docs/dev/debt/libkernel.md:304: warning: [debt-length] Technical debt entry over 1000 characters: state the gap first (what differs from the console, what is unverified, what throws) and keep only the behaviour it needs: 1672 characters
1 findings, 0 errors; GitHub shows at most 10 error annotations, the job summary lists all

4b3b93f0ec292d978f4bbb038c4f2ad59aa3275c:core/libs/prx/libkernel/CMakeLists.txt:2202:    add_test_executable(guest_kernel_fcntl_tests ${LIBS_INCLUDE_DIR}/tests/GuestKernelFcntl.cpp)
4b3b93f0ec292d978f4bbb038c4f2ad59aa3275c:core/libs/prx/libkernel/CMakeLists.txt:2206:    add_test(NAME guest_kernel_fcntl COMMAND guest_kernel_fcntl_tests)

{
  "findings": [
    {
      "path": "core/libs/tests/GuestKernelFcntl.cpp",
      "line": 1,
      "kind": "possible-unregistered-cpp-test",
      "message": "Changed C++ test source path was not found literally in CMake files; inspect registration, generated source lists, and workflow coverage."
    }
  ],
  "negative_control_candidates": [],
  "limits": [
    "Heuristics can miss weak assertions and can flag valid tests; inspect every finding.",
    "A test that passes against base is not by itself proof that the PR test is inadequate.",
    "Negative controls are only candidates until the test actually runs against base and fails by assertion rather than import/setup error."
  ]
}
$ gh pr checks 13695 --repo boykopovar/AnyPS5
conventions	pass	14s	https://github.com/boykopovar/AnyPS5/actions/runs/41151024848/job/124387302691
labels	pass	6s	https://github.com/boykopovar/AnyPS5/actions/runs/41151024778/job/124387302627
windows	pass	4m52s	https://github.com/boykopovar/AnyPS5/actions/runs/41151024853/job/124387647452
linux	skipping	0	https://github.com/boykopovar/AnyPS5/actions/runs/41151024853/job/124387649077
register-context	skipping	0	https://github.com/boykopovar/AnyPS5/actions/runs/41151024853/job/124387649508
relinker	skipping	0	https://github.com/boykopovar/AnyPS5/actions/runs/41151024853/job/124387648561
relinker-asan	skipping	0	https://github.com/boykopovar/AnyPS5/actions/runs/41151024853/job/124387649013
write-watch	skipping	0	https://github.com/boykopovar/AnyPS5/actions/runs/41151024853/job/124387648508
eligibility	pass	4s	https://github.com/boykopovar/AnyPS5/actions/runs/41151024853/job/124387301978
intake	pass	6s	https://github.com/boykopovar/AnyPS5/actions/runs/41151024843/job/124387302986
library-portability	pass	1m45s	https://github.com/boykopovar/AnyPS5/actions/runs/41151024935/job/124387301277
report	pass	35s	https://github.com/boykopovar/AnyPS5/actions/runs/41151024867/job/124387300949

Negative control

Assertion Change to code under test Failed?
New FIOCLEX/FIONCLEX socket assertions Author reports testing them with the base Udp.cpp implementation Author reports yes; I did not reproduce this control locally.

Findings

Blocking: None found.

Non-blocking: The #11219 dependency has not landed. Its declared order should be retained; resolve the debt-file conflict when that dependency is integrated.

Sources

Not verified

I did not build or run C++ tests locally, test Linux behavior, or verify behavior on PS5 hardware. Linux and specialized hosted jobs were skipped. The Windows test results above are author/CI evidence, not local runs.

To get this to a good state

No code change is needed for this review. Keep the declared dependency order and reconcile the debt-file wording when #11219 lands; I can recheck the integrated head then.

@JJenkx

JJenkx commented Oct 31, 2026

Copy link
Copy Markdown
Contributor

Windows run of this head (4b3b93f0ec), merged onto main 25f435d7e9 (clean merge, local only). Windows 11 Enterprise 10.0.22621, Ryzen 9 5950X, WinLibs GCC 15.2.0 UCRT r7, Ninja, Release, BUILD_TESTING=ON, ANYPS5_ENABLE_SPIRV_TOOLS=ON, no Vulkan driver visible.

  • This PR: guest_kernel_fcntl passes 3 of 3 (ctest --repeat until-fail:3).
  • Negative control: with this PR's source files (core/libs/prx/libkernel/Socket/src/Udp.cpp) put back to main's version and its tests kept, guest_kernel_fcntl fails.

AI-assisted: yes (Claude Code).

@jotitadev jotitadev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linux run of this head (4b3b93f0ec), merged onto main 6475267b45 (local only). Ubuntu 26.04 LTS (kernel 7.0), Intel Core i7-8700, GCC 15.2.0, Ninja, Release, BUILD_TESTING=ON.

  • guest_kernel_fcntl passes 3 of 3; public_headers passes; ctest -R "fcntl|socket|net_|ioctl|guest_poll" 50/50.
  • Negative control: same tree with Udp.cpp back to main (test kept): guest_kernel_fcntl aborts 3 of 3.

Together with #13738, which also changes Udp.cpp, on main 2364892547: clean merge, full build passes, guest_kernel_fcntl and guest_poll pass 3 of 3, the socket/poll/fcntl subset 52/52 and public_headers pass.

Written with AI assistance (Claude Code). I ran the builds and tests above on my machine.

This branch has not been deployed

No deployments
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.

6 participants