Skip to content

fix(libkernel): drop the kqueue and knotes of a descriptor that dup2 replaces with a socket - #11445

Open
MaxLikesCode wants to merge 1 commit into
boykopovar:mainfrom
MaxLikesCode:fix/dup2-socket-closes-kqueue
Open

MaxLikesCode wants to merge 1 commit into
boykopovar:mainfrom
MaxLikesCode:fix/dup2-socket-closes-kqueue

Conversation

@MaxLikesCode

@MaxLikesCode MaxLikesCode commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

What

dup2 from a guest socket now drops the kqueue and the knotes of the descriptor it replaces, as the pipe and file paths of dup2 already do.

GuestSockets::DuplicateTo, which the socket branch of dup2_nid_postfix (and so fcntl F_DUP2FD) uses, called EqueueDescriptorClosed only when the replaced descriptor was itself a guest socket. Two consequences, as #11429 reports:

  • a kqueue replaced by a socket stayed registered, so kevent on the number that now names the socket still returned the old queue's events;
  • knotes that another kqueue had on a replaced pipe or file stayed attached.

FreeBSD 11.4's kern_dup closes the replaced file through closefp, whose knote_fdclose drops every knote on that descriptor; kevent on a descriptor that is not a kqueue then fails with EBADF (kqueue_acquire), and EV_DELETE of a dropped knote with ENOENT (kqueue_register). DuplicateTo now notifies the event queues after every successful replacement. Merged #3764 already gives dup2 onto a kqueue these semantics for the other sources.

Tested

Windows 11 Pro 26200, WinLibs GCC 15.2.0 r7, full Release build with BUILD_TESTING=ON.

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
  • Fixes: Fixes libkernel: dup2 from a socket leaves the replaced kqueue alive #11429
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes. Written with Claude Code (Claude Opus 5.5).

🤖 Generated with Claude Code

@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 head 01679f8dbe3856b1f70d8dc0d8ffd10453c00788 against current main 9656d366be903e570594e0726f9bd86f2c32ddb0. The replacement notification is correctly after successful descriptor duplication and is safe for unregistered descriptors: EqueueDescriptorClosed removes a kqueue at that descriptor number and removes descriptor notes from all owning queues. The two regression cases cover replacing a kqueue with a socket and replacing a pipe watched by another kqueue. The latest main commit after the reported bf77a9e9cb base does not touch the changed files. Local tools/check_conventions.py --base upstream/main --head upstream/pr-11445 and git diff --check upstream/main...upstream/pr-11445 pass.

The PR reports the focused guest_kqueue red/green test and full Windows suite after rebasing; I did not independently run that Windows suite. Hosted eligibility, conventions, intake, labels and report checks pass. Build and product jobs were skipped by path policy. I found no blocking issue.

@github-actions

github-actions Bot commented Oct 26, 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 10014 core/libs/tests/GuestKqueue.cpp L1627
PR 10016 core/libs/tests/GuestKqueue.cpp L1627

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 5021 core/libs/prx/libkernel/Socket/src/Udp.cpp
PR 7088 core/libs/prx/libkernel/Socket/src/Udp.cpp
core/libs/tests/GuestKqueue.cpp
PR 7121 core/libs/tests/GuestKqueue.cpp
PR 7507 core/libs/prx/libkernel/Socket/src/Udp.cpp
PR 8463 core/libs/prx/libkernel/Socket/src/Udp.cpp
PR 9843 core/libs/tests/GuestKqueue.cpp
PR 11896 core/libs/prx/libkernel/Socket/src/Udp.cpp
PR 12682 core/libs/prx/libkernel/Socket/src/Udp.cpp
PR 12688 core/libs/prx/libkernel/Socket/src/Udp.cpp
PR 12700 core/libs/prx/libkernel/Socket/src/Udp.cpp
PR 12965 core/libs/prx/libkernel/Socket/src/Udp.cpp

@B-Howell

Copy link
Copy Markdown
Contributor

Independent component qualification of exact head 01679f8dbe3856b1f70d8dc0d8ffd10453c00788:

  • Windows, MSYS2 UCRT64 GCC 16.2, Release: guest_kqueue and guest_socket_descriptors pass 2/2.
  • Linux, WSL Ubuntu / GCC 11, Release: the same suites pass 2/2 (5.68 s and 0.06 s).
  • Linux negative control: with the same PR test executables and dependencies, substituting only libkernel.prx built with baseline bf77a9e9cb's Udp.cpp makes guest_kqueue abort while guest_socket_descriptors still passes.
  • Restoring the PR library passes 2/2 again. All 12 archived executable/library/source/configuration/dependency hashes match the original positive capture; only libkernel.prx changed for the negative control.

This qualifies these two component suites, not the full suite, a retail title, macOS or PS5 console behavior. The Linux negative control is a baseline-library substitution, not an unmodified-main full build.

I had independently reached the same implementation locally, but found this PR in the final overlap check and am contributing the qualification here rather than opening a duplicate.

AI-assisted: yes.

@MaxLikesCode

Copy link
Copy Markdown
Contributor Author

Thanks @B-Howell for the Windows and Linux runs and the baseline-library control on Linux. I added them to the Tested section.

AI-assisted: yes (Claude Code)

@github-actions github-actions Bot added the fix error Closes an issue (Fixes #N) label Oct 26, 2026

@THEMANnt THEMANnt left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Reviewed 01679f8d against 9656d366. No actionable findings.

GuestSockets::DuplicateTo notifies event queues only after replacement succeeds and after releasing the socket lock. That clears a kqueue registered at the target and removes notes from every owning queue. The regression covers replacing a pending timer kqueue with a socket and replacing a pipe watched by another kqueue, checking EBADF, no event, and ENOENT.

Validation: The exact-head Windows Release build succeeded; focused CTest passed 3 tests, 0 skipped, 0 failed. A pinned current-main control passed the same two descriptor tests. The author also reports a baseline failure with the previous Udp.cpp; I did not independently rerun that negative control. Local conventions and git diff --check passed. Hosted intake, eligibility, labels, conventions, and report checks passed; hosted product jobs were skipped by path policy.

This is local Windows x64 validation. I did not test Linux, macOS, console firmware, or a title.

@babyhuey

Copy link
Copy Markdown
Contributor

Windows run of this head 01679f8dbe3, merged onto main 27ad7244d22 (clean merge, local only), with the selection CI's skipped Windows product job uses: Windows 11, Ryzen 7 5800X, RTX 3080, WinLibs GCC 15.2, Release, BUILD_TESTING=ON. The tree was configured fresh after the merge, then select_tests.py select / ninja -f select.ninja selected / --target libs / select_tests.py ctest --parallel 4 --timeout 120.

  • Selected 2122 tests: 1422 pass, 0 fail, 700 skip. No failures. 27ad7244d22 itself skips the same tests on this machine; one is machine-local (pad_sdl, because a controller is plugged in).

AI-assisted: yes (Claude Code)

@MaxLikesCode

Copy link
Copy Markdown
Contributor Author

Thanks @babyhuey for running CI's Windows selection on this head. I added it to the Tested section.

AI-assisted: yes (Claude Code)

@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.

Follow-up on branch currency: this PR is still based on 9656d366be, while upstream main is 801d5b9bc1. The exact changed paths (Udp.cpp and GuestKqueue.cpp) are unchanged on current main, and the submitted implementation/tests still look correct; however, the repository asks branches to stay up to date before merge. Please rebase onto current main and rerun the focused guest_kqueue and conventions checks. This supersedes the earlier approval until the current-main tree is checked.

@Gonic0

Gonic0 commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

macOS run of head 01679f8dbe, for the "Not tested: macOS" line: Apple M5 Pro, macOS 27.2, Apple clang 21, x86-64 Release under Rosetta 2.

Tree: main 801d5b9bc1 plus the open macOS build PRs (#7631, #11249, #11358, #10669, #10552, #7804, #11447, #11437, #11554), with this PR's diff (bf77a9e9cb..01679f8dbe) on top. The diff applies cleanly there.

guest_kqueue
base, without this PR passes, 3 of 3
with this PR passes, 3 of 3
this PR with only Udp.cpp restored from the base aborts, 3 of 3

Same pattern as your Windows run and B-Howell's Linux control.

AI-assisted: yes

@MaxLikesCode
MaxLikesCode force-pushed the fix/dup2-socket-closes-kqueue branch from 01679f8 to 56c1a45 Compare October 27, 2026 14:52
@MaxLikesCode

Copy link
Copy Markdown
Contributor Author

@MarcZX10 rebased onto main 2ee8e97 (head 56c1a45, was 01679f8); the change itself is unchanged. guest_kqueue passes 3 of 3 on Windows and tools/check_conventions.py --base origin/main passes. The full suite on the same change rebased onto main f2f759a, four commits earlier and none of them in libkernel, passes 2666 of 2667: the one failure, guest_wide_formatting, fails the same way on main here (#10981).

Thanks @Gonic0 for the macOS run with the Udp.cpp control. I added it and the rebase to the Tested section.

AI-assisted: yes (Claude Code)

@MarcZX10

Copy link
Copy Markdown
Contributor

Thanks for rebasing this PR onto main 2ee8e9751a and reporting the focused Windows rerun. The change and regression tests still apply cleanly in a merge preview against current main, and the newer Udp.cpp edits are in separate functions from the DuplicateTo hunk.

The branch is still behind current main 4c7d275bef89, which has since changed Udp.cpp. Please rebase onto the current tip and rerun guest_kqueue plus conventions on that exact integrated head. The earlier Windows, Linux, and macOS reports remain useful evidence for the reviewed head, but do not cover this newer base.

I did not run a local build or test for this PR in this workspace.

…replaces with a socket

dup2 from a guest socket notified the event queues only when the
replaced descriptor was itself a guest socket. A kqueue replaced this
way stayed registered, so kevent on the number that now names the
socket still returned the old queue's events, and knotes registered on
a replaced pipe or file stayed attached. FreeBSD 11.4's kern_dup closes
the replaced file through closefp, whose knote_fdclose drops every
knote on the descriptor, as the other dup2 paths here already do.

Fixes boykopovar#11429

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxLikesCode
MaxLikesCode force-pushed the fix/dup2-socket-closes-kqueue branch from 56c1a45 to dcdf9f3 Compare October 29, 2026 12:24
@MaxLikesCode

Copy link
Copy Markdown
Contributor Author

@MarcZX10 rebased onto main 5454cd2 (head dcdf9f3, was 56c1a45); the change itself is unchanged, and main's newer Udp.cpp edits are in other functions. On that exact head, full Release build on Windows: guest_kqueue passes 3 of 3 and tools/check_conventions.py --base origin/main passes. The full suite passes 3016 of 3018; the two failures, guest_wide_formatting (#10981) and guest_freebsd_clocks (#12736: this machine's zone is not UTC), fail the same way on main here. I added this to the Tested section.

AI-assisted: yes (Claude Code)

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

Labels

fix error Closes an issue (Fixes #N) libkernel

Projects

None yet

Development

Successfully merging this pull request may close these issues.

libkernel: dup2 from a socket leaves the replaced kqueue alive

7 participants