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:
commit
2d21b3badc
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
Loading…
Reference in New Issue