Skip to content

fix(libkernel): fail gettimeofday, getitimer, setitimer and the sceKernel time getters with EFAULT on an unusable pointer - #10975

Merged
oneandonlydean merged 9 commits into
boykopovar:mainfrom
xXPBSXx:fix/gettimeofday-itimer-efault
Oct 28, 2026
Merged

oneandonlydean merged 9 commits into
boykopovar:mainfrom
xXPBSXx:fix/gettimeofday-itimer-efault

Conversation

@xXPBSXx

@xXPBSXx xXPBSXx commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

What

The six functions compute into a local and copy out with the checked write from #9618 (GuestCopyOut_nid_no_patch); setitimer reads its value with a new checked read, GuestCopyIn_nid_no_patch. They fail with EFAULT (guest 14, or 0x8002000E from the sceKernel forms). The order of the existing error checks is unchanged. sceKernelGettimeofday now returns 0x8002000E instead of -1 when the copy-out fails.

Fixes: #10969

Tested

New guest_output_pointer_fault: bad pointers 1 and 0xfffffffffffff000 through gettimeofday (both pointers), getitimer, setitimer (value and previous), sceKernelGettimeofday and sceKernelGettimezone expect -1/EFAULT and 0x8002000E; a valid pointer still succeeds. Head c8430e739c on main 66e6772ca2; git merge-tree against current main a5a780eaaa is clean:

  • Windows 10, MinGW-w64 GCC 15.2, Release: the build succeeds and 8 of 8 tests pass: guest_output_pointer_fault, guest_fstat_fault, guest_freebsd_clocks, guest_getitimer, guest_itimer, guest_itimer_pending_default, guest_system_configuration and the public_headers audit. With General.hpp, General.cpp, Signals.cpp and Time.cpp taken from main and the test kept, guest_output_pointer_fault fails with a SegFault.
  • Ubuntu 24.04 under WSL2 (a virtualised kernel, not bare-metal), GCC 13.3, Release: the build succeeds and the same 8 of 8 pass; with the same four files from main, guest_output_pointer_fault fails with a SegFault.
  • check_conventions --base upstream/main passes.
  • Run by others on macOS (Apple M5 Pro, macOS 27.2, Apple clang 21, x86-64 Release under Rosetta 2, main 801d5b9bc1 plus the open macOS build PRs, this PR's diff on top, three runs per row; GuestCopyIn_nid_no_patch and GuestCopyOut_nid_no_patch go through mach_vm_read_overwrite and mach_vm_write there): guest_output_pointer_fault passes with this PR and crashes with a segmentation fault when General.hpp, General.cpp, Signals.cpp and Time.cpp are restored from the base; guest_getitimer, guest_itimer, guest_itimer_pending_default, guest_time_conversion and guest_time_extras pass either way, on head 78cb208c0c (comment).
  • Run by others on Windows (Windows 11 Home 10.0.26200, WinLibs GCC 15.2.0 r7 (MinGW-w64 UCRT), Release, BUILD_TESTING=ON) on the current head 78cb208c0c merged onto main c547deaacb: guest_output_pointer_fault_tests builds without errors and guest_output_pointer_fault passes both runs of ctest --repeat until-fail:2; with main's General.hpp, General.cpp, Signals.cpp and Time.cpp and the rest of this head it crashes with 0xc0000005. The other tests of this description's set, macOS and a title were not run there (comment).

Checklist

AI-assisted (Claude Code).

🤖 Generated with Claude Code

…rnel time getters with EFAULT on an unusable pointer

These wrote their result through the caller's pointer in place, or read
the itimerval of setitimer without a check, so a pointer into unmapped
memory crashed the host. Build the result locally and copy it out with
the checked write (and read the setitimer value with a checked read),
failing with EFAULT as FreeBSD 11.4 does: copyout in sys_gettimeofday
(sys/kern/kern_time.c:646 and 651) and sys_getitimer (kern_time.c:745),
copyin (kern_time.c:803) and copyout (kern_time.c:808) in sys_setitimer.

getrlimit and getrusage are left to boykopovar#8986 and boykopovar#8988, which cover the same
family for those two calls.

Builds on fix/output-structs-efault, which adds GuestCopyOut_nid_no_patch.

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

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

Requesting changes: GuestCopyIn_nid_no_patch has checked-copy implementations for Windows and Linux, but on macOS it falls through to the unconditional std::memcpy at core/libs/prx/libc/src/General.cpp:865-867. The new setitimer path calls this helper for the guest value pointer. Therefore the PR's setitimer_nid_postfix(0, badTimer, ...) regression case will dereference the deliberately unmapped pointer and fault the host on macOS instead of returning -1/EFAULT. GuestCopyOut_nid_no_patch already has a macOS mach_vm_write path, but the read helper does not have the corresponding checked read.

Please add a checked macOS read (for example using the appropriate Mach VM read API and validating that the full requested byte count was copied), or use an existing cross-platform guest-memory copy primitive. Then run guest_output_pointer_fault on macOS or provide a focused result showing both invalid source addresses return EFAULT without terminating the process. The current Windows and WSL2 results do not cover this host-specific branch.

@MarcZX10

Copy link
Copy Markdown
Contributor

Update from the queue: #10978 now also implements these same time exports and adds a checked macOS mach_vm_read_overwrite path for GuestCopyIn. The repository overlap rule applies to these two open PRs; please coordinate with that author and maintainers on a single implementation path, preserving the macOS read fix and tests. My earlier finding still applies to the current #10975 head, which falls through to raw memcpy on macOS; I am not treating the separate PR as if that code were already present here.

@AdnanKhan-ak47

Copy link
Copy Markdown
Contributor

Closed my duplicate #10978 in favour of this one. The one addition worth carrying over is the macOS branch of GuestCopyIn_nid_no_patch (mach_vm_read_overwrite, mirroring GuestCopyOut); the exact lines are in my closing comment there. Happy to review once it is in.

@github-actions

github-actions Bot commented Oct 25, 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 3986 core/libs/prx/libkernel/System/src/Signals.cpp
PR 4563 core/libs/prx/libkernel/CMakeLists.txt
PR 5021 core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel/Time/Time.cpp
PR 6936 core/libs/prx/libkernel/CMakeLists.txt
PR 7192 core/libs/prx/libkernel/CMakeLists.txt
PR 7804 core/libs/prx/libkernel/Time/Time.cpp
PR 9156 core/libs/prx/libkernel/CMakeLists.txt
PR 9284 core/libs/prx/libkernel/CMakeLists.txt
PR 9293 core/libs/prx/libkernel/CMakeLists.txt
PR 9297 core/libs/prx/libkernel/CMakeLists.txt
PR 9302 core/libs/prx/libkernel/CMakeLists.txt
PR 9383 core/libs/prx/libkernel/CMakeLists.txt
PR 9389 core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel/System/src/Signals.cpp
PR 9491 core/libs/prx/libkernel/CMakeLists.txt
PR 9557 core/libs/prx/libkernel/CMakeLists.txt
PR 9566 core/libs/prx/libkernel/CMakeLists.txt
PR 9575 core/libs/prx/libkernel/CMakeLists.txt
PR 11002 core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel/System/src/Signals.cpp
PR 11042 core/libs/prx/libkernel/Time/Time.cpp

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

Requesting one change in the new GuestCopyIn_nid_no_patch helper. On Linux, if process_vm_readv fails with anything other than EFAULT, the helper falls back to std::memcpy(source, bytes). For an invalid source pointer, an EPERM/ENOSYS result (for example, if the syscall is blocked or unavailable) therefore dereferences the untrusted pointer and recreates the host crash this PR is meant to prevent. The existing GuestCopyOut_nid_no_patch treats non-EFAULT syscall failures as infrastructure errors instead of using an unchecked copy. Please use a similarly safe failure path here (or return failure if that is the intended guest-visible behavior) and cover the non-EFAULT syscall failure path.

I reviewed the exact diff and verified that the existing null-output getitimer guard remains in GetitimerInto; this finding is only about the Linux copy-in fallback. The PR's Windows and WSL2 runtime results are author-reported; I did not run the full CMake tests locally.

GuestCopyIn_nid_no_patch fell through to a plain memcpy on macOS, so an
unreadable pointer was dereferenced in the host. Read with
mach_vm_read_overwrite and require KERN_SUCCESS and a full copy, the
mirror of the mach_vm_write branch in GuestCopyOut_nid_no_patch and of
CopyGuestBytes.

The test now also checks the order FreeBSD 11.4 uses in sys_setitimer
(sys/kern/kern_time.c:803-808): the new timer is set first and the old
value is copied out afterwards, so a failing copyout reports EFAULT and
leaves the timer armed. The macOS branch has not been run.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: AdnanKhan-ak47 <93566182+AdnanKhan-ak47@users.noreply.github.com>
@xXPBSXx
xXPBSXx force-pushed the fix/gettimeofday-itimer-efault branch from 18b725f to 4ab1636 Compare October 25, 2026 12:42
…EFAULT

When process_vm_readv failed with anything but EFAULT (EPERM under a
seccomp filter, ENOSYS), GuestCopyIn_nid_no_patch fell back to a plain
memcpy, so the host dereferenced the unchecked guest pointer, the crash
the checked read exists to prevent. Throw as GuestCopyOut_nid_no_patch
does for process_vm_writev.

The test runs the call in a child process under a seccomp filter that
answers process_vm_readv with EPERM and expects the exception.

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

xXPBSXx commented Oct 25, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for both points. Head d69520c794feeaaddcd5cbd409dd738fb6ae4504; could you re-review that exact head?

macOS: GuestCopyIn_nid_no_patch now has a macOS branch that reads with mach_vm_read_overwrite and requires KERN_SUCCESS and a full copy, in the form of CopyGuestBytes and as the counterpart of the mach_vm_write branch in GuestCopyOut_nid_no_patch. It is the code AdnanKhan-ak47 proposed on #10978 (closed in favour of this PR), and the commit carries a Co-authored-by trailer for them. I cannot run macOS, so the branch is checked only by reading it against those two functions; guest_output_pointer_fault has not been run on macOS, and a run on this head by someone with a Mac would still close that point.

Linux: when process_vm_readv fails with anything other than EFAULT, GuestCopyIn_nid_no_patch now throws std::runtime_error("GuestCopyIn: process_vm_readv failed with errno ..."), as GuestCopyOut_nid_no_patch does, instead of falling back to memcpy. A new Linux-only part of guest_output_pointer_fault forks a child, installs a seccomp filter that answers process_vm_readv with EPERM, calls GuestCopyIn_nid_no_patch on a valid source and expects the throw, with the destination left untouched.

  • Linux (WSL2: a virtualised kernel, not bare-metal; Ubuntu 24.04, GCC 13.3, Release), this head: ninja exit 0; public_headers and guest_output_pointer_fault pass, 2 of 2. With the memcpy fallback restored in the same checkout, ninja exit 0 and guest_output_pointer_fault fails (Subprocess aborted).
  • Windows 10 (MinGW-w64 GCC 15.2, Release), this head: ninja exit 0; the same two tests pass, 2 of 2 (the seccomp part is Linux only); check_conventions passes. One run per arm.

setitimer: the earlier commit extends the test for the order FreeBSD 11.4 uses (the new timer is set first and the old value copied out afterwards, so a failing copy-out reports EFAULT and leaves the timer armed). The code already followed that order, so that check pins it and has no failing arm.

AI-assisted (Claude Code).

@ahmadmuj ahmadmuj 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 d69520c794feeaaddcd5cbd409dd738fb6ae4504 (base 66e6772ca2094b665488d6ca5cbae3f6fb0c5034). git merge-tree onto current main 4017310678562c63594d99b8f45db2d68b9aaa6c is clean.

What I ran, WSL2 Ubuntu 24.04, GCC 13.3, Release:

  • Built only the focused targets. guest_output_pointer_fault, guest_fstat_fault, guest_freebsd_clocks, guest_getitimer, guest_itimer, guest_itimer_pending_default, guest_system_configuration, guest_time_extras, guest_kernel_sys_ktimer and public_headers pass 10/10. The four timer/fault tests also pass 20/20 with --repeat until-fail:20. check_conventions.py passes.
  • Red check 1: Signals.cpp and Time.cpp back to base, test kept. guest_output_pointer_fault dies with SIGSEGV.
  • Red check 2: General.cpp from 4ab1636 (the memcpy fallback on a non-EFAULT process_vm_readv error). The seccomp child fails at line 59, so that test does catch the EPERM case.
  • Compared against FreeBSD releng/11.4 sys/kern/kern_time.c. gettimeofday copies tp first, then tzp only if that worked (L644-652). getitimer checks which before the copyout (L754, L745). setitimer with itv == NULL forwards to getitimer (L797-800), and copies oitv out after the timer is armed (L805-808). Your code matches all of that, and the test pins the armed-then-EFAULT case.

One suggestion:

GuestCopyIn_nid_no_patch re-implements CopyGuestBytes from the same file (General.cpp:86-104), which is already a checked read on every host. The copies differ in two places. In #else, GuestCopyIn does a raw memcpy and returns true, while CopyGuestBytes and GuestCopyOut both throw there (CONTRIBUTING: does what it should or throws). On Windows it does VirtualQuery then memcpy, while CopyGuestBytes uses ReadProcessMemory, which can't race with an unmap between the check and the copy. Keeping your null/overflow guard and delegating works:

extern "C" bool GuestCopyIn_nid_no_patch(void* destination, const void* source, std::size_t bytes) {
    if (bytes == 0) return true;
    const auto start = reinterpret_cast<std::uintptr_t>(source);
    if (start == 0 || bytes > UINTPTR_MAX - start) return false;
    return CopyGuestBytes(destination, source, bytes);
}

I tried that locally: same 4 timer/fault tests pass 5/5, including the seccomp child (CopyGuestBytes throws std::runtime_error on non-EFAULT too). It also drops about 28 lines.

Small notes, not blocking:

  • setitimer(3, (GuestItimerval*)1, &ok) gives EINVAL here. On FreeBSD the copyin (L803) runs before the which check in kern_setitimer (L822), so it gives EFAULT. That order is old code, and you said you kept it, so this is just a note. It's now reachable because the bad-pointer path exists.
  • No test hits the which != 0 copyout of previous (Signals.cpp:442-445), e.g. setitimer(1, zero, bad) should be EFAULT.
  • GuestOutputPointerFault.cpp:75-77 sets *__error_nid_postfix() = 0 three times in a row.

Limits: Linux only, no Windows or macOS run, no dump.

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

I reviewed the exact head d69520c794feeaaddcd5cbd409dd738fb6ae4504 and the focused test report. The Linux non-EFAULT fallback and macOS unchecked read issues are addressed, and the added seccomp regression covers the former.

One remaining correctness issue is the final #else in GuestCopyIn_nid_no_patch: it uses std::memcpy and returns success on every host without a platform-specific checked-copy path. An invalid guest source can still terminate the host there. The repository already has the cross-platform checked CopyGuestBytes helper; please keep the zero/null/overflow guard and use that helper (or make unsupported hosts fail safely) instead of falling through to raw memcpy.

Please also add the focused setitimer previous-value copyout fault case noted in review (valid value, invalid previous) if feasible, since it exercises the GuestCopyOut branch after the timer update. Then rerun the focused test and report which host(s) ran it. I have not run the full CMake suite locally.

The last branch of GuestCopyIn_nid_no_patch was a plain memcpy that
reported success, so an unreadable pointer on any other host ended the
process. Keep the null, overflow and zero-length guards and make exactly
one call to CopyGuestBytes, which reads with ReadProcessMemory,
process_vm_readv or mach_vm_read_overwrite and throws on a host without a
checked read. The per-platform branches are removed.

On Windows this replaces the VirtualQuery walk and memcpy with
ReadProcessMemory.

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

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

Thanks for the macOS checked-copy addition and the Linux seccomp regression; the reviewed current diff now has safe host-copy implementations for those cases. One part of the reported issue still appears uncovered: the getitimer and setitimer bodies do not change in this PR, and Time.cpp only routes gettimeofday/sceKernelGettimeofday and sceKernelGettimezone through GuestCopyOut. Could you confirm how the bad getitimer output and setitimer value/previous pointers are made safe on this head, or add the checked copies and regression cases if they remain direct dereferences? The issue specifically lists those pointers; I do not see them covered by guest_output_pointer_fault in the changed test diff.

@AdnanKhan-ak47 AdnanKhan-ak47 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 d9e8185cc1 on macOS 27.0.1 (Apple silicon), the host nobody else in the thread has.

The macOS run everyone asked for is not possible on main. core/libs does not build on macOS at all: GuestAllocations.cpp:26 includes the Linux-only <link.h> under a guard that does not exclude Apple, and docs/dev/BUILD.md:17 states the libraries ship for Linux and Windows only. So guest_output_pointer_fault cannot run here for this or any PR until the libraries are ported; that is out of scope for this fix and should not hold it.

What can be verified is the macOS branch itself. GuestCopyIn_nid_no_patch now delegates to CopyGuestBytes, whose Apple path is mach_vm_read_overwrite(..., &copied) == KERN_SUCCESS && copied == bytes (General.cpp:98-100), the same code as GuestCopyOut's mach_vm_write counterpart. A standalone x86_64 binary with that exact function, run under Rosetta, on the issue's two addresses plus null and a valid one:

source 0x0000000000000001 -> rejected, destination untouched
source 0xfffffffffffff000 -> rejected, destination untouched
source 0x0000000000000000 -> rejected, destination untouched
source 0x0000000308b44ba8 -> copied, destination holds 12/34

No fault, no partial write. The surrounding setitimer logic is host-independent, so this is the host-specific part of the behaviour that the Linux and Windows runs do not cover.

On the 13:36 request: on this head the bodies do change. getitimer_nid_postfix fills a local through GetitimerInto and copies out with GuestCopyOut (Signals.cpp), setitimer reads value through GuestCopyIn and copies previous out through GuestCopyOut on both the which != 0 and ITIMER_REAL branches, and Time.cpp routes the three time getters the same way. ahmadmuj's red check 1 (bodies reverted, test kept, SIGSEGV) covers that these are the lines that matter.

Approving. The behaviour and ordering match what I had in the closed #10978, and delegating to CopyGuestBytes is the better shape; the two small gaps ahmadmuj noted (setitimer(1, zero, bad) for the which != 0 copy-out, and the copyin-before-which order on FreeBSD) are in AdnanKhan-ak47:fix/libkernel-time-efault (GuestItimer.cpp) if you want to lift them, but neither should block this.

… with an unusable pointer

getitimer and setitimer(which != ITIMER_REAL, previous) copy the result
out through the checked write in Signals.cpp; the fault test only
exercised ITIMER_REAL. Add getitimer(ITIMER_VIRTUAL) and
setitimer(ITIMER_VIRTUAL, zero, previous) with an unmapped pointer
expecting EFAULT, and the matching calls with a valid pointer.

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

xXPBSXx commented Oct 25, 2026

Copy link
Copy Markdown
Contributor Author

Thanks again. Head d9e8185cc11085274d56fc03115541e3d43c813c; could you re-review that exact head?

GuestCopyIn_nid_no_patch: it keeps the zero-size, null and overflow guard and now returns CopyGuestBytes(destination, source, bytes); the Windows, Linux, macOS and raw memcpy branches are gone. That is the checked read the file already had: Windows reads with ReadProcessMemory (instead of a VirtualQuery check followed by memcpy), Linux with process_vm_readv and macOS with mach_vm_read_overwrite, and an unsupported host throws instead of copying unchecked. The macOS branch in the earlier commit came from the proposal of AdnanKhan-ak47 on #10978 (that commit keeps the Co-authored-by trailer); the final form, delegating to CopyGuestBytes, is the suggestion in the review of ahmadmuj. Thank you both.

getitimer and setitimer: they do change in this PR, in System/src/Signals.cpp (already on the previous head), not in Time.cpp. getitimer builds the result in a local and copies it out with GuestCopyOut_nid_no_patch; setitimer reads value with GuestCopyIn_nid_no_patch and writes previous with GuestCopyOut_nid_no_patch, on the which != 0 path as well as the normal one. guest_output_pointer_fault covers: getitimer with an unmapped output pointer, setitimer with a zero timer value and an unmapped previous, setitimer with an unmapped value, and setitimer with a 3600 s value and an unmapped previous, which returns EFAULT and leaves the timer armed (getitimer afterwards reports a non-zero value). The which != 0 path with an unmapped previous has no test case.

Hosts: Windows 10 (MinGW-w64 GCC 15.2, Release), this head: ninja exit 0; public_headers, guest_output_pointer_fault and guest_fstat_fault pass, 3 of 3; check_conventions passes. Linux (WSL2: a virtualised kernel, not bare-metal; Ubuntu 24.04, GCC 13.3, Release), this head: ninja exit 0; public_headers and guest_output_pointer_fault pass, 2 of 2, including the seccomp child. One run each. There is no failing arm for this commit, because it replaces branches by the existing checked read. On macOS, AdnanKhan-ak47 reports in the approving review that core/libs does not build there, so guest_output_pointer_fault cannot run on that host, and that a standalone program with the mach_vm_read_overwrite read, run under Rosetta, rejected the two addresses from the issue and a null source without touching the destination and copied a valid one. Thank you for that run too.

AI-assisted (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.

Re-reviewed exact head c67997b. The new test cases cover getitimer(1, bad) and setitimer(1, zero, bad), including successful calls with valid pointers, closing the previous non-ITIMER_REAL coverage gap. The checked guest read delegates to CopyGuestBytes; the Linux non-EFAULT syscall failure no longer falls through to raw memcpy, and the new seccomp case covers that failure.

Exact-head local checks: git diff --check, tools/check_conventions.py, and a C++20 syntax check of GuestOutputPointerFault.cpp pass. The PR reports the focused tests passing on Windows and WSL2 Linux; hosted product jobs are skipped, and I did not run the linked suite locally. The macOS library test cannot run because core/libs is not currently buildable on macOS; the standalone checked-read result on macOS is author-reported by another reviewer. With those limits stated, the requested correctness and test changes are addressed.

@ahmadmuj ahmadmuj 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 my earlier review. Re-checked head c67997b99bf14ef79e702457f87f732ba62d3e7d (base 66e6772ca2094b665488d6ca5cbae3f6fb0c5034); git merge-tree onto current main d2b3ee3bcc8b3724ab953ea7d1b9419e07a38346 is clean.

What I ran, WSL2 Ubuntu 24.04, GCC 13.3, Release:

  • GuestCopyIn_nid_no_patch now keeps the null/overflow guard and returns CopyGuestBytes(...). That's byte-for-byte the version I tried locally last time.
  • The same 10 focused tests (guest_output_pointer_fault, guest_fstat_fault, guest_freebsd_clocks, guest_getitimer, guest_itimer, guest_itimer_pending_default, guest_system_configuration, guest_time_extras, guest_kernel_sys_ktimer, public_headers) with --repeat until-fail:20: 200/200 pass, seccomp child included. check_conventions.py passes.
  • Red checks on guest_output_pointer_fault, one mutant at a time:
    • setitimer which != 0 branch back to *previous = {}: SIGSEGV, so the new setitimer(1, zero, bad) line catches it.
    • getitimer writing *output = {} directly for which 1/2: SIGSEGV, so the new getitimer(1, bad) line catches it.
    • GuestCopyIn back to a raw memcpy: SIGSEGV on setitimer(0, bad, ok).

Approving. The setitimer(3, bad) EINVAL-vs-EFAULT order from my last review is still only a note.

Limits: Linux only, no Windows or macOS run, no dump.

@Gruzzberry

Copy link
Copy Markdown
Contributor

Draft 11231 (kldsym) touches neighbouring lines and adds the same GuestCopyIn_nid_no_patch lines. Proposal: yours first, mine rebases.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 25, 2026
@Gruzzberry

Copy link
Copy Markdown
Contributor

Draft #11264 (sysctl EFAULT) touches neighbouring lines and adds the same GuestCopyIn_nid_no_patch lines. Proposal: yours first, mine rebases.

@Gruzzberry

Copy link
Copy Markdown
Contributor

Draft #11273 (signal calls EFAULT) touches neighbouring lines and adds the same GuestCopyIn_nid_no_patch lines. Proposal: yours first, mine rebases.

@Gruzzberry

Copy link
Copy Markdown
Contributor

Draft #11281 (getsockname/getpeername EFAULT) adds the same GuestCopyIn_nid_no_patch lines. Proposal: yours first, mine rebases.

The only conflict is in Time.cpp with the guest timezone store of the
settimeofday change: gettimeofday reads the timezone under the mutex into
a local copy, releases it, then copies it out with the checked write.

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

xXPBSXx commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

Head 080c77547cf1c5bffb6a83bbd0db244805d1025b: I merged main (49d1f9b07e) into the branch; could you re-review that head? The approvals so far are on c67997b99b and d9e8185cc1. The only conflict was Time.cpp with #9682 (the guest timezone store): gettimeofday now reads the timezone under the mutex into a local and copies it out afterwards, and nothing else changed. The tree equals a rebase onto the same main commit.

Measured on that rebase (same tree, base main 49d1f9b07e): Windows 10 (MinGW-w64 GCC 15.2, Release), 27 of 28 tests of the touched family pass; the failure is guest_wide_formatting, the known #10987 family. With only the timezone copy-out in Time.cpp put back to a direct write, guest_output_pointer_fault segfaults. Linux (WSL2, Ubuntu 24.04, GCC 13.3, Release): 28 of 28 pass (three of them were first Not Run until their targets were built), and the same direct-write change segfaults guest_output_pointer_fault. On the merge head itself: Linux 2 of 2 (public_headers, guest_output_pointer_fault); on Windows guest_output_pointer_fault passes, while public_headers hit its 30 s limit under machine load and its audit command, run by hand, exits 0 after 28 s. One run per arm.

AI-assisted (Claude Code).

@xXPBSXx

xXPBSXx commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

Agreed that this one should land first; main is merged into it (head 080c77547c, waiting for a re-review). I compared it with #11231, #11264, #11273 and #11281, each on the merge base git finds: git merge-tree reports no conflict for any of the four, and in General.cpp each adds exactly the same GuestCopyIn_nid_no_patch lines as this PR, so they combine as identical changes; the drafts can rebase onto it afterwards.

AI-assisted (Claude Code).

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

Re-reviewed 080c77547c. git merge-tree of main 49d1f9b07e with the previously approved d9e8185cc1 produces a tree byte-identical to this head, so the merge commit adds nothing beyond resolving #9682's timezone store: gettimeofday fills current and zone as before and copies each out with GuestCopyOut, clock_gettime still maps the errno 14 path to SceKernelErrorEfault, and settimeofday's tz write goes through the checked copy. Against 49d1f9b07e the PR is the same 6 files (+163/-10) as before. Approval stands.

@wachi35676

Copy link
Copy Markdown
Contributor

Windows re-run of head 080c77547cf1 on the current main 14eb2cda22d2, WinLibs GCC 15.2.0, Release. It still merges cleanly. guest_output_pointer_fault, guest_getitimer, guest_itimer and guest_system_configuration pass 3/3. With main's General.hpp, General.cpp, Signals.cpp and Time.cpp, guest_output_pointer_fault segfaults.

#11264, #11281, #11273 and #11231 all reuse this PR's GuestCopyIn_nid_no_patch and ask to land after it. With this one merged first, each of them merges cleanly on top.

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

@oneandonlydean oneandonlydean self-assigned this Oct 26, 2026
@MarcZX10

Copy link
Copy Markdown
Contributor

I rechecked the new merge head 080c77547cf1 and the reported Windows/WSL results. The reviewed implementation still uses checked guest copies for the time outputs and setitimer inputs; I found no new source finding in the added merge commit.

The branch has since fallen behind current main: tools/check_main_sync.py reports 6 ahead / 225 behind 14c99b52ac (merge preview clean), with main-side changes in General.cpp, the libkernel CMake file and Signals.cpp since the merge base. The current reported focused run is against older main 14eb2cda22d2. Please refresh onto current main and rerun the focused time/pointer tests on that exact head before merge. This request is for current-head integration and evidence; it does not reopen the earlier checked-copy correctness findings.

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

Rechecked merge head 080c77547cf1; the reviewed checked-copy implementation remains sound and the Windows/WSL results are useful. Current upstream main has advanced: this branch is 6 ahead /225 behind 14c99b52ac, with main-side changes in General.cpp, libkernel CMake and Signals.cpp since its merge base. The reported focused run is against older 14eb2cda22d2. Please refresh onto current main and rerun the focused time/pointer tests on the resulting exact head before merge. This is a current-head integration/validation request, not a reopened code finding.

@xXPBSXx

xXPBSXx commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

Merged main (14c99b52ac) into the branch (head 7937aa3f906b7dc558e20937184cade6c7951060); there was no conflict and no other change, so the diff against main is the same six files as before. Retested on the exact head, one run per arm, base main 14c99b52ac. Windows 10 (MinGW-w64 GCC 15.2, Release): 30 of 30 tests of a selection of time, itimer, signal and pointer-fault tests and their neighbours pass, including guest_output_pointer_fault, guest_getitimer and guest_itimer; the public_headers audit command, run by hand, exits 0 after 16 s. Linux (WSL2, Ubuntu 24.04, GCC 13.3, Release; a virtualised kernel): 28 of 28 pass. With only the timezone copy-out in Time.cpp put back to a direct write, guest_output_pointer_fault segfaults on both.

AI-assisted (Claude Code).

The only conflict is sceKernelGettimezone in Time.cpp: main now reports the host zone, so the zone is built from that into a local and copied out with the checked write.
@xXPBSXx

xXPBSXx commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

Merged main into the branch twice (e0b13f2990, then fc7e583e6e); head 78cb208c0c. The first merge had one conflict, in sceKernelGettimezone: main now reports the host zone's offset and daylight saving amount, so the zone is built from that formula into a local and copied out with the checked write, which fails with EFAULT on an unusable pointer. The second merge had no conflict and the 49 newer main commits touch none of the changed files. The patch lines of the PR are otherwise unchanged.

Retested on Windows 10 (MinGW-w64 GCC 15.2), Release, ninja exit code 0: the 11 focused time, timer, signal and pointer-fault tests pass on the exact head, 30 of 30 tests passed on the first merge head 506d69b015, and the red arm (the zone written directly) makes guest_output_pointer_fault crash with a SegFault. check_conventions passes and git merge-tree against main is clean.

Linux not rerun on this head, WSL2 VM would not start; on the first merge head 506d69b015 (same patch lines) all 28 focused tests passed under WSL2 (a virtualised kernel, not bare-metal), GCC 13.3, and the red arm crashed there as well.

Not tested: macOS.

AI-assisted (Claude Code).

@Gonic0

Gonic0 commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

macOS run of head 78cb208c0c, where GuestCopyIn_nid_no_patch and GuestCopyOut_nid_no_patch go through mach_vm_read_overwrite and mach_vm_write: 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 (fc7e583e6e..78cb208c0c) on top. Three runs per row.

guest_output_pointer_fault guest_getitimer, guest_itimer, guest_itimer_pending_default, guest_time_conversion, guest_time_extras
base, without this PR (not in base) pass
with this PR passes pass
this PR with General.hpp, General.cpp, Signals.cpp and Time.cpp restored from the base SEGFAULT pass

So the bad-pointer cases return EFAULT on macOS too, and without the source changes the test crashes, as on your Windows and Linux runs.

AI-assisted: yes

@ycbuzi

ycbuzi commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Windows run of the current head 78cb208c0c merged onto main c547deaacb (8d434f4ca5, clean). The Windows run in the description is from an older head. Windows 11 Home 10.0.26200, WinLibs GCC 15.2.0 r7 (MinGW-w64 UCRT), Release, BUILD_TESTING=ON.

  • Built guest_output_pointer_fault_tests with no errors. The only warning is main's existing _sigintr_nid_postfix "initialized and declared 'extern'" in Signals.cpp.
  • guest_output_pointer_fault passes both runs of ctest --repeat until-fail:2.
  • Negative control: with main's libc/include/General.hpp, libc/src/General.cpp, libkernel/System/src/Signals.cpp and libkernel/Time/Time.cpp and the rest of this head, the test crashes with FATAL: unhandled exception 0xc0000005 and exits as Exception.
  • Not run: the other 7 tests of the description's set, macOS, a title.

AI-assisted: yes (Claude Code)

@oneandonlydean
oneandonlydean merged commit a681681 into boykopovar:main Oct 28, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

build fix error Closes an issue (Fixes #N) libc libkernel

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(libkernel): gettimeofday, getitimer, setitimer and the sceKernel time getters crash on a pointer into unmapped memory instead of failing with EFAULT

9 participants