Skip to content
Merged
1 change: 1 addition & 0 deletions core/libs/prx/libc/include/General.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ extern "C" int CopyGuestPath_nid_no_patch(const char* source, std::string& desti

extern "C" std::filesystem::path ResolvePath_nid_no_patch(const char* path);
extern "C" bool GuestCopyOut_nid_no_patch(void* destination, const void* source, std::size_t bytes);
extern "C" bool GuestCopyIn_nid_no_patch(void* destination, const void* source, std::size_t bytes);
extern "C" std::filesystem::path ResolvePathOrRelinkedModule_nid_no_patch(const char* path);
extern "C" std::filesystem::path ResolveArchivedPath_nid_no_patch(const char* path, bool module = false);
extern "C" std::filesystem::path ResolveDiskPath_nid_no_patch(const char* path);
Expand Down
7 changes: 7 additions & 0 deletions core/libs/prx/libc/src/General.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,13 @@ extern "C" bool GuestCopyOut_nid_no_patch(void* destination, const void* source,
#endif
}

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);
}

extern "C" std::filesystem::path ResolvePath_nid_no_patch(const char* path) {
auto resolved = ResolveLocked(path, false, true);
AppArchiveRefuseOnly_nid_no_patch(resolved, path, "reading");
Expand Down
10 changes: 10 additions & 0 deletions core/libs/prx/libkernel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1875,6 +1875,16 @@ if(BUILD_TESTING)
set_tests_properties(guest_file_vector_memory PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(guest_output_pointer_fault_tests ${LIBS_INCLUDE_DIR}/tests/GuestOutputPointerFault.cpp)
target_include_directories(guest_output_pointer_fault_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_output_pointer_fault_tests PRIVATE libkernel libc)
configure_windows_unwind(guest_output_pointer_fault_tests)
add_test(NAME guest_output_pointer_fault COMMAND guest_output_pointer_fault_tests)
set_tests_properties(guest_output_pointer_fault PROPERTIES TIMEOUT 15)
if(WIN32)
set_tests_properties(guest_output_pointer_fault PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(guest_fstat_fault_tests ${LIBS_INCLUDE_DIR}/tests/GuestFstatFault.cpp)
target_include_directories(guest_fstat_fault_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_fstat_fault_tests PRIVATE libkernel libc)
Expand Down
21 changes: 17 additions & 4 deletions core/libs/prx/libkernel/System/src/Signals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ extern "C" unsigned int APS5_VABI GuestAlarm_nid_no_patch(unsigned int seconds)

static_assert(sizeof(GuestItimerval) == 32 && offsetof(GuestItimerval, value) == 16);

extern "C" int APS5_VABI getitimer_nid_postfix(int which, GuestItimerval* output) {
static int GetitimerInto(int which, GuestItimerval* output) {
if (which < 0 || which > 2) {
*__error_nid_postfix() = 22;
return -1;
Expand Down Expand Up @@ -394,6 +394,16 @@ extern "C" int APS5_VABI getitimer_nid_postfix(int which, GuestItimerval* output
return 0;
}

extern "C" int APS5_VABI getitimer_nid_postfix(int which, GuestItimerval* output) {
GuestItimerval result{};
const int status = GetitimerInto(which, output == nullptr ? nullptr : &result);
if (status == 0 && !GuestCopyOut_nid_no_patch(output, &result, sizeof(result))) {
*__error_nid_postfix() = 14;
return -1;
}
return status;
}

namespace {
constexpr int ItimerReal = 0;
constexpr int ItimerProf = 2;
Expand Down Expand Up @@ -421,15 +431,18 @@ extern "C" int APS5_VABI setitimer_nid_postfix(int which, const GuestItimerval*
if (value == nullptr && previous == nullptr) { *__error_nid_postfix() = 14; return -1; }
GuestItimerval requested{};
if (value != nullptr) {
requested = *value;
if (!GuestCopyIn_nid_no_patch(&requested, value, sizeof(requested))) { *__error_nid_postfix() = 14; return -1; }
if (!ValidItimer(requested.value)) { *__error_nid_postfix() = 22; return -1; }
if (!ItimerSet(requested.value)) requested.interval = {};
else if (!ValidItimer(requested.interval)) { *__error_nid_postfix() = 22; return -1; }
}
if (which != ItimerReal) {
if (ItimerSet(requested.value))
throw std::runtime_error(std::string("setitimer: ") + (which == ItimerProf ? "ITIMER_PROF" : "ITIMER_VIRTUAL") + " needs process CPU time accounting, which is not implemented");
if (previous != nullptr) *previous = {};
if (previous != nullptr) {
const GuestItimerval cleared{};
if (!GuestCopyOut_nid_no_patch(previous, &cleared, sizeof(cleared))) { *__error_nid_postfix() = 14; return -1; }
}
return 0;
}
if (requested.interval.tv_sec == 0 && requested.interval.tv_usec != 0 && requested.interval.tv_usec < MinExactIntervalMicros)
Expand All @@ -448,7 +461,7 @@ extern "C" int APS5_VABI setitimer_nid_postfix(int which, const GuestItimerval*
NotifyAlarms(alarms);
}
}
if (previous != nullptr) *previous = old;
if (previous != nullptr && !GuestCopyOut_nid_no_patch(previous, &old, sizeof(old))) { *__error_nid_postfix() = 14; return -1; }
return 0;
}

Expand Down
23 changes: 17 additions & 6 deletions core/libs/prx/libkernel/Time/Time.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -501,9 +501,11 @@ int APS5_VABI clock_gettime_nid_postfix(int clockId, KernelTimespec* tp) {
return 0;
}

int APS5_VABI gettimeofday_nid_postfix(KernelTimeval* tv, KernelTimezone* tz) {
KernelTimeval ignoredTime{};
if (tv == nullptr) tv = &ignoredTime;
int APS5_VABI gettimeofday_nid_postfix(KernelTimeval* output, KernelTimezone* zoneOutput) {
KernelTimeval current{};
auto* tv = &current;
KernelTimezone zone{};
auto* tz = zoneOutput == nullptr ? nullptr : &zone;
#ifdef _WIN32
FILETIME ft{};
GetSystemTimePreciseAsFileTime(&ft);
Expand All @@ -523,6 +525,14 @@ int APS5_VABI gettimeofday_nid_postfix(KernelTimeval* tv, KernelTimezone* tz) {
tz->tz_minuteswest = 0;
tz->tz_dsttime = 0;
}
if (output != nullptr && !GuestCopyOut_nid_no_patch(output, &current, sizeof(current))) {
*__error_nid_postfix() = 14;
return -1;
}
if (zoneOutput != nullptr && !GuestCopyOut_nid_no_patch(zoneOutput, &zone, sizeof(zone))) {
*__error_nid_postfix() = 14;
return -1;
}
return 0;
}

Expand Down Expand Up @@ -705,13 +715,14 @@ int APS5_VABI sceKernelConvertUtcToLocaltime(int64_t utc_time, int64_t* local_ti

int APS5_VABI sceKernelGettimeofday(KernelTimeval* tp) {
if (tp == nullptr) return SceKernelErrorEfault;
return gettimeofday_nid_postfix(tp, nullptr);
const int result = gettimeofday_nid_postfix(tp, nullptr);
return result != 0 && *__error_nid_postfix() == 14 ? SceKernelErrorEfault : result;
}

int APS5_VABI sceKernelGettimezone(KernelTimezone* tz) {
if (tz == nullptr) return SceKernelErrorEfault;
*tz = {0, 0};
return 0;
const KernelTimezone zone{0, 0};
return GuestCopyOut_nid_no_patch(tz, &zone, sizeof(zone)) ? 0 : SceKernelErrorEfault;
}

uint64_t APS5_VABI sceKernelReadTsc(void) {
Expand Down
111 changes: 111 additions & 0 deletions core/libs/tests/GuestOutputPointerFault.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#include "prx/libc/include/Api.hpp"
#include "prx/libc/include/General.hpp"
#include "prx/libkernel/Time/include/Time.hpp"
#include "prx/libkernel/include/Api.hpp"

#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <initializer_list>
#include <stdexcept>
#ifdef __linux__
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <unistd.h>
#endif

static void Check(bool value, int line) {
if (!value) {
std::fprintf(stderr, "output pointer check failed at line %d\n", line);
std::abort();
}
}
#define Require(value) Check((value), __LINE__)

static constexpr int GuestEfault = 14;
static constexpr int KernelEfault = static_cast<int>(0x8002000Eu);

static bool Efault(int result) {
return result == -1 && *__error_nid_postfix() == GuestEfault;
}

#ifdef __linux__
static void RequireNonEfaultFailureThrows() {
const pid_t child = fork();
Require(child >= 0);
if (child == 0) {
sock_filter filter[] = {
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SYS_process_vm_readv, 0, 1),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | EPERM),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
};
sock_fprog program{static_cast<unsigned short>(sizeof(filter) / sizeof(filter[0])), filter};
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0 || prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &program) != 0) _exit(2);
char source[8] = {1, 2, 3, 4, 5, 6, 7, 8};
char destination[8] = {};
try {
GuestCopyIn_nid_no_patch(destination, source, sizeof(source));
} catch (const std::runtime_error&) {
_exit(destination[0] == 0 ? 0 : 3);
}
_exit(1);
}
int status = 0;
Require(waitpid(child, &status, 0) == child);
Require(WIFEXITED(status) && WEXITSTATUS(status) == 0);
}
#endif

int main() {
alignas(8) unsigned char valid[512]{};
alignas(8) unsigned char zeroTimer[32]{};
auto* timeValid = reinterpret_cast<KernelTimeval*>(valid);
auto* zoneValid = reinterpret_cast<KernelTimezone*>(valid + 32);
auto* timerValid = reinterpret_cast<GuestItimerval*>(valid);
auto* zeroTimerValue = reinterpret_cast<const GuestItimerval*>(zeroTimer);
for (const std::uintptr_t address : {std::uintptr_t{1}, ~std::uintptr_t{0xfff}}) {
void* bad = reinterpret_cast<void*>(address);
auto* badTime = reinterpret_cast<KernelTimeval*>(address);
auto* badZone = reinterpret_cast<KernelTimezone*>(address);
auto* badTimer = reinterpret_cast<GuestItimerval*>(address);
*__error_nid_postfix() = 0;
*__error_nid_postfix() = 0;
*__error_nid_postfix() = 0;
Require(Efault(gettimeofday_nid_postfix(badTime, nullptr)));
*__error_nid_postfix() = 0;
Require(Efault(gettimeofday_nid_postfix(timeValid, badZone)));
*__error_nid_postfix() = 0;
Require(Efault(getitimer_nid_postfix(0, badTimer)));
*__error_nid_postfix() = 0;
Require(Efault(setitimer_nid_postfix(0, zeroTimerValue, badTimer)));
*__error_nid_postfix() = 0;
Require(Efault(setitimer_nid_postfix(0, badTimer, timerValid)));
*__error_nid_postfix() = 0;
Require(Efault(getitimer_nid_postfix(1, badTimer)));
*__error_nid_postfix() = 0;
Require(Efault(setitimer_nid_postfix(1, zeroTimerValue, badTimer)));
Require(sceKernelGettimeofday(badTime) == KernelEfault);
Require(sceKernelGettimezone(badZone) == KernelEfault);
}
Require(gettimeofday_nid_postfix(timeValid, zoneValid) == 0);
Require(getitimer_nid_postfix(0, timerValid) == 0);
Require(setitimer_nid_postfix(0, zeroTimerValue, timerValid) == 0);
Require(getitimer_nid_postfix(1, timerValid) == 0);
Require(setitimer_nid_postfix(1, zeroTimerValue, timerValid) == 0);
GuestItimerval armed{};
armed.value.tv_sec = 3600;
*__error_nid_postfix() = 0;
Require(Efault(setitimer_nid_postfix(0, &armed, reinterpret_cast<GuestItimerval*>(~std::uintptr_t{0xfff}))));
GuestItimerval current{};
Require(getitimer_nid_postfix(0, &current) == 0 && current.value.tv_sec > 0);
Require(setitimer_nid_postfix(0, zeroTimerValue, nullptr) == 0);
Require(sceKernelGettimeofday(timeValid) == 0);
Require(sceKernelGettimezone(zoneValid) == 0);
#ifdef __linux__
RequireNonEfaultFailureThrows();
#endif
}
Loading