Skip to content

fix(libkernel): answer read, write, lseek, fsync and flock on a kqueue descriptor as FreeBSD does - #10014

Merged
oneandonlydean merged 6 commits into
boykopovar:mainfrom
xXPBSXx:fix/kqueue-descriptor-read-write-seek-sync
Nov 2, 2026
Merged

oneandonlydean merged 6 commits into
boykopovar:mainfrom
xXPBSXx:fix/kqueue-descriptor-read-write-seek-sync

Conversation

@xXPBSXx

@xXPBSXx xXPBSXx commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

What

sceKernelRead, sceKernelWrite, sceKernelLseek, sceKernelFsync, sceKernelFdatasync and flock now fail on a kqueue descriptor with EOPNOTSUPP, EOPNOTSUPP, ESPIPE, EINVAL, EINVAL and EOPNOTSUPP, using the existing IsKqueueDescriptor. guest_kqueue gets the cases. readv, writev, pread and pwrite on a kqueue descriptor are not changed here.

FreeBSD 11.4 kqueueops, invfo_rdwr, kern_fsync, sys_flock; found by comparing the descriptor operations against that source, not from a title. A second commit, 8deb85d957, finishes a zero-length read on a kqueue descriptor with 0 instead of EOPNOTSUPP: FreeBSD 11.4's dofileread returns 0 for uio_resid == 0 before it calls fo_read (sys_generic.c:367, "Finish zero length reads right here"), so only a non-empty read reaches the kqueue's EOPNOTSUPP. dofilewrite has no such shortcut, so a zero-length write stays EOPNOTSUPP. guest_kqueue gets both cases.

Fixes: #10013

Tested

Head 5ba5164c72, rebased onto main bd8e13428a; git merge-tree against current main e7e3e9c198 is clean. The diff is 3 files, +22/-4: Open.cpp +3/-1, Stdio.cpp +3/-3 and GuestKqueue.cpp +16. The rebase dropped exactly one hunk of the earlier head b01e5ae181: the local declarations of lseek_nid_postfix, fsync_nid_postfix, fdatasync_nid_postfix and flock_nid_postfix in GuestKqueue.cpp, which main now declares in the library headers. The other hunks are unchanged.

  • Windows 10 Pro 19045, MinGW-w64 GCC 15.2, Release, on main bd8e13428a with this head (ninja exit code 0): guest_kqueue passes 3 of 3 runs.
  • Without the changes in Open.cpp and Stdio.cpp (both taken from main, same build directory, test kept; ninja exit code 0): guest_kqueue fails 3 of 3 runs with exit code 0xc0000409.
  • The family file|posix|shm|kqueue|equeue|pipe|truncate|lseek|flock|fsync|vector|socket_read|dup|fcntl|descriptor|stdio|aio|kernel_sys has 153 tests on main now (the earlier description named 112); with this head 151 pass and 2 are skipped (the named shared memory tests guest_named_shm_windows_short_temp and guest_named_shm_windows_long_short_temp, which skip in this configuration); ctest reports 100% tests passed, 0 tests failed out of 153, counting the skips as passed.
  • check_conventions --base upstream/main passes.
  • Not run by me: a title, a console, and macOS at the current head. Measured by wachi35676 on Windows 11 and WSL2 Ubuntu 24.04 (GCC 13.3): at head 5ba5164c72 merged onto main 9c435c2812, guest_kqueue, guest_kernel_fcntl and guest_filesystem pass 3 of 3 on both hosts, and guest_kqueue fails with Open.cpp and Stdio.cpp from main; at the current head 8deb85d957 merged onto main 504ea3b53b, guest_kqueue passes 3 of 3 on both hosts and still fails with main's Open.cpp/Stdio.cpp, and a probe gives 0 for sceKernelRead(kq, buf, 0) and read(kq, buf, 0) and 0x8002002d for sceKernelWrite(kq, buf, 0). Measured by jotitadev on macOS 27.2 (Apple M2 Max, x86_64 under Rosetta 2, on feat: macOS host support (Apple Silicon via Rosetta 2, Vulkan on MoltenVK) #8658) at the earlier head b01e5ae181 (5ba5164c72 only drops four local declarations from GuestKqueue.cpp; the zero-length-read commit 8deb85d957 came after and was not run on macOS): the new test aborts 3 of 3 without the fix, and guest_kqueue and guest_kernel_sys pass 5 of 5 with it. During development one run of guest_kqueue right after a rebuild failed 3/3; the cause was not identified. It did not occur in the three runs with this head above; that is all I can say about it.

Checklist

AI-assisted (Claude Code).

🤖 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 exact head b01e5ae against current main 9d6adeb. The targeted guards consistently route kqueue descriptors through the requested FreeBSD-compatible errors for read/write, seek, fsync/fdatasync, and flock; the new guest_kqueue case covers each path and closes its descriptor. The code reuses IsKqueueDescriptor and existing error mappings, and the PR explicitly scopes out readv/writev/pread/pwrite. I ran git diff --check and the repository convention check against current main; both pass. I could not run the native guest_kqueue binary in this environment. The reported Windows run and 112-test kernel/file family are author-reported, including the disclosed unexplained initial failure; the hosted product build jobs are skipped by repository policy. I found no blocking issue in the reviewed diff.

@github-actions

github-actions Bot commented Oct 23, 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 5021 core/libs/prx/libkernel/File/src/Open.cpp
core/libs/prx/libkernel/File/src/Stdio.cpp
PR 6936 core/libs/prx/libkernel/File/src/Open.cpp
core/libs/prx/libkernel/File/src/Stdio.cpp
PR 7088 core/libs/tests/GuestKqueue.cpp
PR 7121 core/libs/prx/libkernel/File/src/Open.cpp
core/libs/prx/libkernel/File/src/Stdio.cpp
core/libs/tests/GuestKqueue.cpp
PR 7447 core/libs/prx/libkernel/File/src/Stdio.cpp
PR 9019 core/libs/prx/libkernel/File/src/Open.cpp
core/libs/prx/libkernel/File/src/Stdio.cpp
PR 9293 core/libs/prx/libkernel/File/src/Stdio.cpp
PR 9297 core/libs/prx/libkernel/File/src/Stdio.cpp
PR 9302 core/libs/prx/libkernel/File/src/Stdio.cpp
PR 9843 core/libs/tests/GuestKqueue.cpp
PR 10319 core/libs/prx/libkernel/File/src/Open.cpp
PR 11831 core/libs/prx/libkernel/File/src/Open.cpp
core/libs/prx/libkernel/File/src/Stdio.cpp
PR 12560 core/libs/prx/libkernel/File/src/Open.cpp
core/libs/prx/libkernel/File/src/Stdio.cpp
PR 12682 core/libs/prx/libkernel/File/src/Open.cpp
core/libs/prx/libkernel/File/src/Stdio.cpp
PR 12688 core/libs/prx/libkernel/File/src/Open.cpp
core/libs/prx/libkernel/File/src/Stdio.cpp
PR 12700 core/libs/prx/libkernel/File/src/Open.cpp
core/libs/prx/libkernel/File/src/Stdio.cpp
PR 12965 core/libs/prx/libkernel/File/src/Open.cpp
core/libs/prx/libkernel/File/src/Stdio.cpp
PR 14832 core/libs/tests/GuestKqueue.cpp
PR 14854 core/libs/tests/GuestKqueue.cpp
PR 14886 core/libs/tests/GuestKqueue.cpp
PR 14894 core/libs/tests/GuestKqueue.cpp

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

macOS test: Apple M2 Max, macOS 27.2

Apple M2 Max, macOS 27.2, Apple clang (Xcode 27), x86-64 Release build under Rosetta 2, BUILD_TESTING=ON. Head b01e5ae181. main does not build on macOS, so the platform is #8658 (feat/macos-port) @ fa063d8b07 with this PR's diff applied (clean apply).

On macOS the kqueue placeholder is also /dev/null, so the same calls reach the host device without the change.

guest_kqueue guest_kernel_sys
#8658 alone pass pass
#8658 + this PR's test only 3/3 abort (SIGABRT) –
#8658 + this PR 5/5 pass 5/5 pass

So the new KqueueDescriptorOperations case catches the bug on macOS as well, and the fix holds there.

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

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 24, 2026
…e descriptor as FreeBSD does

A kqueue's file operations (kern_event.c kqueueops) refuse read and write with EOPNOTSUPP through invfo_rdwr, and a kqueue is not seekable, has no vnode for fsync, and is refused by flock. The host placeholder behind a kqueue descriptor is a null device on every host, so read returned end of file, write EBADF, lseek 0, fsync EBADF and flock 0. These calls now fail with EOPNOTSUPP, EOPNOTSUPP, ESPIPE, EINVAL (fsync and fdatasync) and EOPNOTSUPP.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@xXPBSXx
xXPBSXx force-pushed the fix/kqueue-descriptor-read-write-seek-sync branch from b01e5ae to 5ba5164 Compare October 25, 2026 03:09
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 25, 2026
@wachi35676

Copy link
Copy Markdown
Contributor

Tested head 5ba5164c72 merged onto main 9c435c2812, Windows 11 (WinLibs GCC 15.2.0, Release) and WSL2 Ubuntu 24.04 (GCC 13.3), BUILD_TESTING=ON. This adds the Linux run.

  • guest_kqueue, guest_kernel_fcntl and guest_filesystem pass 3 of 3 runs on both hosts. With Open.cpp and Stdio.cpp from main, guest_kqueue fails on both.
  • The six answers match release/11.4.0, on both hosts:
    • read and write: invfo_rdwr
    • lseek: ESPIPE, since kqueueops has no DFLAG_SEEKABLE
    • fsync/fdatasync: EINVAL from getvnode
    • flock: EOPNOTSUPP for a non-vnode

One case now differs from 11.4: a zero-length read. read_nid_postfix(kq, buf, 0) and sceKernelRead(kq, buf, 0) now give EOPNOTSUPP (0x8002002d), where main returned 0. 11.4's dofileread returns 0 for uio_resid == 0 before it calls fo_read ("Finish zero length reads right here", sys_generic.c). dofilewrite has no such shortcut, so a zero-length write giving EOPNOTSUPP is right. Checking nbytes == 0 before the kqueue check in sceKernelRead would keep that case at 0.

For the follow-up the description mentions (vector and positioned calls), here is what they do today on a kqueue, where 11.4 gives:

  • readv/sceKernelReadv return 0 on both hosts; 11.4: EOPNOTSUPP
  • writev/sceKernelWritev give EBADF; 11.4: EOPNOTSUPP
  • pread/preadv give ESPIPE on Windows but return 0 on Linux; 11.4: ESPIPE (kern_preadv)

AI-assisted: yes (Claude Code); results above were run on this machine.

FreeBSD 11.4 dofileread returns 0 for uio_resid == 0 before it calls
fo_read, so read(kq, buf, 0) is 0 and only a non-empty read reaches the
kqueue's EOPNOTSUPP. dofilewrite has no such shortcut, so a zero-length
write stays EOPNOTSUPP. guest_kqueue gets both cases.

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

Copy link
Copy Markdown
Contributor

Re-tested 8deb85d957 merged onto main 504ea3b53b, on the same Windows 11 and WSL2 Ubuntu 24.04 machines. guest_kqueue passes 3 of 3 runs on both hosts and still fails with main's Open.cpp/Stdio.cpp. A probe gives sceKernelRead(kq, buf, 0) and read(kq, buf, 0) 0, and sceKernelWrite(kq, buf, 0) 0x8002002d. That matches 11.4's dofileread shortcut and dofilewrite's lack of one. Thanks.

AI-assisted: yes (Claude Code); results above were run on this machine.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 26, 2026
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 26, 2026
@xXPBSXx

xXPBSXx commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

Merged main (2dbe9b99f5) into the branch, head 6c44410f529ca3e1ddf35f08f6b27d2247c537b1. There were five conflict hunks in two files: sceKernelRead and sceKernelWrite in Open.cpp, and flock, sceKernelFsync and sceKernelFdatasync in Stdio.cpp; main had put a directory check returning EISDIR in front of the socket check in each, and the resolution keeps that check first and then this PR's kqueue check, with the diff against main unchanged at 3 files, +24/-4. Retested on the exact head: on Windows 10 (MinGW-w64 GCC 15.2, Release) guest_kernel_sys, guest_filesystem, guest_kqueue and guest_kernel_fcntl pass in 3 of 3 runs, and with Open.cpp and Stdio.cpp taken from main guest_kqueue aborts in 3 of 3 runs; on Linux (WSL2, Ubuntu 24.04, GCC 13.3, ordinary user) guest_kqueue, guest_kernel_sys and guest_filesystem pass in 3 of 3 runs.

AI-assisted (Claude Code).

@MarcZX10

Copy link
Copy Markdown
Contributor

The overlap bot now identifies open PR #12503 touching the same GuestKqueue.cpp insertion area as this PR. The repository rule asks authors to agree on merge order for overlapping lines and record Depends on where needed. Please coordinate with #12503 and note any required order on the checklist.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 29, 2026
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 29, 2026
@xXPBSXx

xXPBSXx commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

Merged main (3b6ccf73ae) into this branch because it conflicted with main in core/libs/tests/GuestKqueue.cpp, and fixed a compile error the merge exposed.

  • The conflict: this PR adds KqueueDescriptorOperations in front of main(), and main (feat(libkernel): support EV_CLEAR on a kqueue watched by another kqueue #9575) changed main() to take argc and argv for the cleared-race case. Both are kept.
  • The compile error: main renamed IsKqueueDescriptor to IsKqueueDescriptor_nid_no_patch; this PR called the old name at six places in Open.cpp and Stdio.cpp, which now use the new one (follow-up commit 14dbd31937).

Tested on the exact head 14dbd31937 (ninja exit code 0):

  • Windows 10 Pro, WinLibs GCC 15.2.0, Release, BUILD_TESTING=ON: guest_kqueue passes in 3 of 3 runs. With Open.cpp and Stdio.cpp put back to main's version and the test kept, it fails in 3 of 3 runs, and passes again with them restored.
  • Ubuntu 24.04 under WSL2 (a virtualised kernel, not bare-metal), GCC 13.3, Release: guest_kqueue passes in 3 of 3 runs; with the two files reverted it fails in 3 of 3 runs.
  • check_conventions.py --base upstream/main passes; git merge-tree against main (5454cd2e45 when measured) is clean.
  • Not run here: macOS and the full suite.

Could you take another look at the new head?

AI-assisted (Claude Code).

adriadam10 pushed a commit that referenced this pull request Oct 29, 2026
The staging buffer was sized by the guest's nevents, so a request for
INT_MAX events allocated about 64 GiB before the queue reported how many
were pending. It's now bounded at 1024 events; kevent may return fewer
than requested, and events left in the queue come back on the next call.
The test covers an INT_MAX request and a 1030-event queue, and moves away
from the end of StandardInput and the start of main, where #9575 and
#10014 also add tests.
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Nov 2, 2026
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@xXPBSXx

xXPBSXx commented Nov 2, 2026

Copy link
Copy Markdown
Contributor Author

Merged main 24238c4cb1 into this branch (c9abed0516, normal merge commit, no force). The only conflict was in core/libs/tests/GuestKqueue.cpp, in the call list of main(): main added SocketReplacesDescriptor (#11445) where this PR adds KqueueDescriptorOperations. I kept both, main's line first.

Tested on Windows 10 Pro (19045), WinLibs GCC 15.2.0 r7, Release, head c9abed0516 (ninja exit code 0 in both arms, one build each):

  • guest_kqueue passes 3 of 3 runs.
  • With Open.cpp and Stdio.cpp taken from main (the production change reverted) and the test kept, guest_kqueue fails 3 of 3 runs (exit code 8).

This head merges cleanly with main 4f64f81 (checked 2026-11-02 at 13:51 UTC).

AI-assisted (Claude Code).

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.

read, write, lseek, fsync and flock on a kqueue descriptor answer like a null device, FreeBSD refuses them

6 participants