Merge pull request #541 from DotDebian/fix/nanosleep-errors

fix(libkernel): report invalid nanosleep requests through errno and the sce error codes
This commit is contained in:
Dean 2026-10-05 10:31:42 +13:00 committed by GitHub
commit 2d21b3badc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 131 additions and 10 deletions

View File

@ -176,6 +176,16 @@ if(BUILD_TESTING)
set_tests_properties(guest_cpu_time_clocks PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(guest_nanosleep_tests ${LIBS_INCLUDE_DIR}/tests/GuestNanosleep.cpp)
target_include_directories(guest_nanosleep_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_nanosleep_tests PRIVATE libkernel libc)
configure_windows_unwind(guest_nanosleep_tests)
add_test(NAME guest_nanosleep COMMAND guest_nanosleep_tests)
set_tests_properties(guest_nanosleep PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_nanosleep PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(guest_pthread_sem_tests ${LIBS_INCLUDE_DIR}/tests/GuestPthreadSem.cpp)
target_include_directories(guest_pthread_sem_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_pthread_sem_tests PRIVATE libkernel libc)

View File

@ -2,6 +2,7 @@
#include "prx/libkernel/Time/include/TimedWait.hpp"
#include "prx/libc/include/General.hpp"
#include "prx/libkernel/KernelErrors.hpp"
#include <algorithm>
#include <cerrno>
#include <cstdint>
@ -24,6 +25,8 @@
#include <sys/time.h>
#endif
extern "C" int* APS5_VABI __error_nid_postfix();
static std::uint64_t RawMonotonicNanos() {
#ifdef _WIN32
return TimedWait::NowNanos();
@ -222,15 +225,15 @@ int APS5_VABI sceKernelUsleep_nid_postfix(KernelUseconds microseconds) {
return 0;
}
int APS5_VABI sceKernelNanosleep(const KernelTimespec* rqtp, KernelTimespec* rmtp) {
if (rqtp == nullptr) {
return -1;
static int SleepForRequest(const KernelTimespec* rqtp, KernelTimespec* rmtp) {
constexpr int guestFault = 14;
constexpr int guestInvalid = 22;
if (rqtp == nullptr) return guestFault;
if (rqtp->tv_nsec < 0 || rqtp->tv_nsec >= 1000000000LL) return guestInvalid;
if (rqtp->tv_sec >= 0) {
TimedWait::SleepNanos(static_cast<std::uint64_t>(rqtp->tv_sec) * 1000000000ULL +
static_cast<std::uint64_t>(rqtp->tv_nsec));
}
if (rqtp->tv_sec < 0 || rqtp->tv_nsec < 0 || rqtp->tv_nsec >= 1000000000LL) {
return -1;
}
TimedWait::SleepNanos(static_cast<std::uint64_t>(rqtp->tv_sec) * 1000000000ULL +
static_cast<std::uint64_t>(rqtp->tv_nsec));
if (rmtp != nullptr) {
rmtp->tv_sec = 0;
rmtp->tv_nsec = 0;
@ -238,12 +241,20 @@ int APS5_VABI sceKernelNanosleep(const KernelTimespec* rqtp, KernelTimespec* rmt
return 0;
}
int APS5_VABI sceKernelNanosleep(const KernelTimespec* rqtp, KernelTimespec* rmtp) {
const int error = SleepForRequest(rqtp, rmtp);
return error == 0 ? 0 : SceKernelError(error);
}
int APS5_VABI nanosleep_nid_postfix(const KernelTimespec* rqtp, KernelTimespec* rmtp) {
return sceKernelNanosleep(rqtp, rmtp);
const int error = SleepForRequest(rqtp, rmtp);
if (error == 0) return 0;
*__error_nid_postfix() = error;
return -1;
}
int APS5_VABI _nanosleep_nid_postfix(const KernelTimespec* rqtp, KernelTimespec* rmtp) {
return sceKernelNanosleep(rqtp, rmtp);
return nanosleep_nid_postfix(rqtp, rmtp);
}
int APS5_VABI usleep_nid_postfix(KernelUseconds microseconds) {

View File

@ -0,0 +1,100 @@
#include "SceTypes.hpp"
#include <cstdint>
#include <cstdlib>
extern "C" {
int APS5_VABI nanosleep_nid_postfix(const KernelTimespec* rqtp, KernelTimespec* rmtp);
int APS5_VABI _nanosleep_nid_postfix(const KernelTimespec* rqtp, KernelTimespec* rmtp);
int APS5_VABI sceKernelNanosleep(const KernelTimespec* rqtp, KernelTimespec* rmtp);
int APS5_VABI clock_gettime_nid_postfix(int clockId, KernelTimespec* tp);
int* APS5_VABI __error_nid_postfix();
}
using Nanosleep = int (APS5_VABI *)(const KernelTimespec*, KernelTimespec*);
static constexpr int SCE_OK = 0;
static constexpr int SCE_KERNEL_ERROR_EFAULT = static_cast<int>(0x8002000E);
static constexpr int SCE_KERNEL_ERROR_EINVAL = static_cast<int>(0x80020016);
static constexpr int GUEST_EACCES = 13;
static constexpr int GUEST_EFAULT = 14;
static constexpr int GUEST_EINVAL = 22;
static constexpr int GUEST_CLOCK_MONOTONIC = 4;
static constexpr std::int64_t NANOS_PER_SECOND = 1000000000LL;
static constexpr std::int64_t NANOS_PER_MILLISECOND = 1000000LL;
static constexpr std::int64_t SLEEP_NANOS = 50 * NANOS_PER_MILLISECOND;
static constexpr std::int64_t EARLY_WAKE_MARGIN_NANOS = 5 * NANOS_PER_MILLISECOND;
static constexpr std::int64_t NO_SLEEP_LIMIT_NANOS = 2 * NANOS_PER_SECOND;
static void Require(bool value) { if (!value) std::abort(); }
static std::int64_t MonotonicNanos() {
KernelTimespec time{-1, -1};
Require(clock_gettime_nid_postfix(GUEST_CLOCK_MONOTONIC, &time) == SCE_OK);
return time.tv_sec * NANOS_PER_SECOND + time.tv_nsec;
}
static void PosixRejects(Nanosleep sleep, const KernelTimespec* request, int error) {
*__error_nid_postfix() = GUEST_EACCES;
Require(sleep(request, nullptr) == -1);
Require(*__error_nid_postfix() == error);
}
static void PosixRejectsInvalidRequests(Nanosleep sleep) {
const KernelTimespec negativeNanos{0, -1};
const KernelTimespec wholeSecondOfNanos{0, NANOS_PER_SECOND};
const KernelTimespec negativeBoth{-1, -1};
PosixRejects(sleep, &negativeNanos, GUEST_EINVAL);
PosixRejects(sleep, &wholeSecondOfNanos, GUEST_EINVAL);
PosixRejects(sleep, &negativeBoth, GUEST_EINVAL);
PosixRejects(sleep, nullptr, GUEST_EFAULT);
}
static void SceRejectsInvalidRequests() {
const KernelTimespec negativeNanos{0, -1};
const KernelTimespec wholeSecondOfNanos{0, NANOS_PER_SECOND};
const KernelTimespec negativeBoth{-1, -1};
Require(sceKernelNanosleep(&negativeNanos, nullptr) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelNanosleep(&wholeSecondOfNanos, nullptr) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelNanosleep(&negativeBoth, nullptr) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelNanosleep(nullptr, nullptr) == SCE_KERNEL_ERROR_EFAULT);
}
static void ReturnsAtOnce(Nanosleep sleep, KernelTimespec request) {
const std::int64_t start = MonotonicNanos();
*__error_nid_postfix() = GUEST_EACCES;
Require(sleep(&request, nullptr) == SCE_OK);
Require(*__error_nid_postfix() == GUEST_EACCES);
Require(MonotonicNanos() - start < NO_SLEEP_LIMIT_NANOS);
}
static void NegativeSecondsDoNotSleep(Nanosleep sleep) {
ReturnsAtOnce(sleep, {-1, 0});
ReturnsAtOnce(sleep, {-1, NANOS_PER_SECOND - 1});
ReturnsAtOnce(sleep, {INT64_MIN, 0});
ReturnsAtOnce(sleep, {0, 0});
}
static void SleepsForTheRequest(Nanosleep sleep) {
const KernelTimespec request{0, SLEEP_NANOS};
const std::int64_t start = MonotonicNanos();
*__error_nid_postfix() = GUEST_EACCES;
Require(sleep(&request, nullptr) == SCE_OK);
Require(*__error_nid_postfix() == GUEST_EACCES);
Require(MonotonicNanos() - start >= SLEEP_NANOS - EARLY_WAKE_MARGIN_NANOS);
}
int main() {
PosixRejectsInvalidRequests(nanosleep_nid_postfix);
PosixRejectsInvalidRequests(_nanosleep_nid_postfix);
SceRejectsInvalidRequests();
NegativeSecondsDoNotSleep(nanosleep_nid_postfix);
NegativeSecondsDoNotSleep(_nanosleep_nid_postfix);
NegativeSecondsDoNotSleep(sceKernelNanosleep);
SleepsForTheRequest(nanosleep_nid_postfix);
SleepsForTheRequest(_nanosleep_nid_postfix);
SleepsForTheRequest(sceKernelNanosleep);
}