Repository navigation
Conversation
…k_getres result clock_gettime and clock_getres wrote the result through the guest pointer directly, so a non-null pointer to unmapped or read-only memory crashed the host. Both now compute the value locally and copy it out with GuestCopyOut_nid_no_patch, failing with EFAULT like FreeBSD's copyout; the sceKernelClockGettime and sceKernelClockGetres wrappers return 0x8002000e. A null clock_getres result still succeeds, and an unsupported clock still throws before the pointer is used.
MarcZX10
left a comment
There was a problem hiding this comment.
Reviewed exact head f4863ca. The change computes into local KernelTimespec values and routes only the final guest write through GuestCopyOut, preserving clock validation before output-pointer validation for clock_gettime and preserving the null-output success path for clock_getres. The regression covers unmapped and read-only targets for four valid clock IDs, both libc NID calls and SceKernel wrappers, and verifies the read-only target remains unchanged. The current-main comparison shows no intervening changes to Time.cpp or GuestFreebsdClocks.cpp since the tested 2227f13 base; current-main diff check and conventions pass. I did not rerun the large Linux/Windows suites locally. Hosted Windows and report checks were pending when checked, so I have not approved.
|
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.
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.
|
|
@babyhuey thanks; One question about cost: |
rooklz
left a comment
There was a problem hiding this comment.
Reviewed exact head f4863ca1f5, adding the macOS evidence the body doesn't have (it lists Linux and Windows only).
Gates. git merge-tree against current main 750cf6cf48 is clean. check_conventions.py --base main passes. pr_overlap.py reports only the three uncomparable Time.cpp sharers. I checked them by hand: #12130 touches only CancellableSleep (L354), and merge-tree pr-12418 × pr-12130 is clean. #7804 and #5021 conflict with main already.
What GuestCopyOut does with these pointers on macOS (standalone probe, Apple clang, arm64, 16-byte mach_vm_write like the __APPLE__ arm):
| destination | kr | target after |
|---|---|---|
| host stack | 0 | written |
| RW mmap page + 8 | 0 | written |
| PROT_READ page | 1 (KERN_INVALID_ADDRESS) |
unchanged |
| PROT_NONE page | 1 | n/a |
0x10 |
1 | n/a |
| 16 bytes straddling RW → RO | 1 | first 8 bytes unchanged (no partial write) |
So on macOS both cases in BadTimePointersFault hit the false path, and the read-only page is left untouched, as the final Require expects. The same probe in the Linux (process_vm_writev, Ubuntu 24.04 GCC 13) image gives stack → 16, RO → -1/EFAULT, 0x10 → -1/EFAULT. The straddle case → 8, and the RW half is written. GuestCopyOut still returns false there (copied != bytes), so the result is EFAULT with a partial write. That matches FreeBSD copyout faulting part-way through, so it isn't a problem here. I'm only noting it because the test's "unchanged" assertion is only meaningful for a whole-page RO target, which is what the test uses.
Host callers still work. clock_gettime_nid_postfix is also called internally with host-stack KernelTimespecs: Pthread/Posix/Common.hpp:30 RelativeMicroseconds (every timed mutex/cond/sem/rwlock wait), Ktimer.cpp:55, SyncOnAddress.cpp:236, Socket/src/Udp.cpp:1673 (per received datagram), SystemConfiguration.cpp:103 and libkernel_sys/Uuid.cpp:37. The probe above shows host-stack writes succeed on both POSIX primitives. On Windows, BeginHostWrite only walks views, so a non-guest address returns true. On macOS, GuestWriteWatch::forEachPage only visits registered ranges, so these callers keep working.
Cost (non-blocking). 2M iterations of CLOCK_MONOTONIC + 16-byte checked copy to a stack local:
| host | clock_gettime |
+ checked copy |
|---|---|---|
| macOS arm64 (M-series) | 13.9 ns | 399 ns |
| macOS x86_64 under Rosetta | 22.0 ns | 494 ns |
| Linux x86_64 (emulated container) | 19.5–19.9 ns | 448–459 ns |
For guest calls this is the price of correctness, and gettimeofday already pays it. The internal host callers pay it too, though, for pointers that can't fault. On macOS that also means taking the write-watch _lock plus a _hostWrites push/erase when the watch is available. Timed waits and per-datagram stamping hide a few hundred ns, so I'm not blocking on it. A cheap follow-up would be to route those callers through the existing unchecked ReadClock (e.g., a ReadClock_nid_no_patch host export) instead of the guest-facing entry point.
Semantics checked against main. These stay the same: clock validation before the null/fault check in clock_gettime (OmittedTimeIsFaultAfterClockValidation), null res → 0 before validation in clock_getres, unsupported clock still throws without touching the output (UnsupportedResolutionStillThrows), and the CPU-clock recursion now goes through ReadClockResolution into the local value. I didn't build the libs lane on macOS (no submodules here), so guest_freebsd_clocks itself wasn't run on macOS. The primitive-level probe above covers the host-specific part of the change.
The change looks correct to me. I'm leaving this as a comment, not an approval, because I couldn't run the PR's own test on macOS. If someone with a libs-lane macOS build runs guest_freebsd_clocks on this head, that closes the gap. The cost note doesn't block.
AI-assisted: yes
|
I ran a small native Linux proxy loop to estimate the syscall cost raised above: one million direct This is only a host-side proxy for the syscall component. It does not execute AnyPS5's guest |
|
The
If the null behaviour is a PS5 quirk worth keeping, a one-line comment on |
|
One semantic clarification on the new null-result question: FreeBSD's |
|
@oneandonlydean measured on the guest path, as you asked.
So the checked copy costs about 1.28 µs per call here, about 43× That's too much to add to a call titles make many times per frame, so I don't think this should merge as it is. I see two ways forward and would like your view before changing anything:
AI-assisted: yes (Claude Code) |
|
One more measurement, since the same question covers my other EFAULT PRs: every one of them uses
So the two hot paths, AI-assisted: yes (Claude Code) |
|
Windows run of this head (
AI-assisted: yes (Claude Code). |
MarcZX10
left a comment
There was a problem hiding this comment.
Re-reviewed exact head f4863ca1f5a65ea06b7eea491713c1a5628c1491 and the new Windows report. The checked guest write and invalid-pointer regressions are the right correctness direction; I agree the FreeBSD null-result behavior is preserved. The remaining issue is the reported hot-path cost: the guest-path measurement is about 1.31 µs/call versus 30.4 ns on main (about 43×), and the author says titles call this path many times per frame. I would not restore an unchecked store, which would lose the EFAULT behavior. Please pursue a safe fast path in the shared guest-copy helper as a prerequisite (so the sibling syscall fixes can use it too), or provide representative title/frame call-count evidence showing the aggregate cost is acceptable before merging this hot-path change. Keep the bad-pointer tests against the checked behavior.
The Windows run reports the PR alone still aborts on an Eastern-time host because main’s unrelated KernelTimezoneIsUtc assertion is present; it passes when combined with open #12660. Please coordinate and record the order with #12660 so rebasing preserves both its timezone-test correction and this PR’s bad-pointer regression. The reported Windows run is on the combined tree; it is not a standalone pass for this head. Linux/product hosted jobs are skipped. I did not run these tests locally.
|
@MarcZX10 thanks, I'll do the fast path in the shared helpers as its own PR first. Before writing it, here's what I found on What the helpers pay today. On Linux the cost is only the What's tracked. Option 1: per-thread cache with a global generation. Option 2: fault-tolerant store. I'd start with option 1 unless the race in it is a blocker for you, and add option 2 later if strict exactness is wanted. Either way the bad-pointer tests stay on the checked behaviour, and I'll report the same per-call benchmark on AI-assisted: yes (Claude Code) |
adriadam10
left a comment
There was a problem hiding this comment.
Thanks, the change itself is right: GuestCopyOut gives the EFAULT, clock validation still comes first, the null clock_getres result matches FreeBSD, and the test pins the unmapped and read-only cases.
Blocking, as already raised by @oneandonlydean and @MarcZX10:
core/libs/prx/libkernel/Time/Time.cpp(clock_gettime_nid_postfix): the checked copy takes aprocess_vm_writevsyscall on every successful call, 1,308 ns vs 30 ns onmainby your own measurement.clock_gettimeis on a hot path, so please merge this only after the guest-copy fast path lands (your option 1, a per-thread cache invalidated by a generation bumped in map/mprotect/munmap, is a reasonable start; keep the bad-pointer tests on the checked path) and rerun the same per-call benchmark on this head.- Order: #12660 first (the new clock assertions need its time-zone fix on non-UTC hosts), then the fast path, then this PR rebased.
|
@babyhuey #12660 is closed: #12747 merged into AI-assisted: yes (Claude Code). |
|
A Windows run with GCC 16: Windows 11 Pro 10.0.22631, i9-9900K, MSYS2 UCRT64 GCC 16.1.0, Release, this PR (
AI-assisted: yes (Claude Code) |
|
Gruzzberry's #15035 (settimeofday and clock_settime return EPERM for a jailed caller) touches neighbouring lines of the same file. Proposal: yours first, mine rebases. AI-assisted: yes (Claude Code). |
# Conflicts: # core/libs/tests/GuestFreebsdClocks.cpp
What
clock_gettimeandclock_getreswrote their result straight through the guest pointer, so a non-null pointer into unmapped or read-only memory crashed the host.gettimeofdayandsceKernelGettimezonenext to them already copy out withGuestCopyOut_nid_no_patch. FreeBSD'ssys_clock_gettimeandsys_clock_getresusecopyoutand returnEFAULT.clock_gettimecomputes the value as before, then copies it out withGuestCopyOut_nid_no_patch, failing withEFAULT(14). A null pointer still fails withEFAULTafter clock validation, as before.clock_getrescomputes into a local value (the body moves to a staticReadClockResolution, unchanged apart from the recursion for CPU-time clocks) and copies it out once. A null result still succeeds, and an unsupported clock still throws before the pointer is used.sceKernelClockGettimeandsceKernelClockGetresreturn0x8002000ethrough their existingSceKernelErrormapping.Tested
BadTimePointersFaultincore/libs/tests/GuestFreebsdClocks.cpp: forCLOCK_REALTIME,CLOCK_MONOTONIC,CLOCK_SECONDandCLOCK_THREAD_CPUTIME_ID, both the_nid_postfixcalls and thesceKernelwrappers with an unmapped pointer (0x10) and a read-only page fail withEFAULT/0x8002000e, and the read-only page is left unchanged.anyps5-buildCI image, Ubuntu 24.04, GCC 13, Release,BUILD_TESTING=ON), this headf4863ca1f5aonmain2227f136618:guest_freebsd_clockspasses; the fullctestpasses (2,680 run, 0 failed), andcheck_conventions.pypasses. Withmain'sTime.cppthe same test segfaults.BUILD_TESTING=ON, this head: full build andlibssucceed;guest_freebsd_clockspasses 3 of 3 alone; withmain'sTime.cppit crashes (0xc0000005). CI's test selection for this change (2,348 tests): 1,621 pass, 726 skip, and the only failure isguest_raise_exception, which also fails onmainon this machine (libkernel (Windows): guest_raise_exception slowed about 3x and sometimes aborts at "address waking" since 540364a24 (#10666) #11813).check_conventions.pypasses.Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)