diff --git a/core/libs/tests/GuestVrHand.cpp b/core/libs/tests/GuestVrHand.cpp index ff21ab3290..f3f9b168c7 100644 --- a/core/libs/tests/GuestVrHand.cpp +++ b/core/libs/tests/GuestVrHand.cpp @@ -3,6 +3,13 @@ #include #include #include +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#else +#include +#include +#endif namespace { @@ -11,6 +18,39 @@ void Require(bool value) { if (!value) std::abort(); } constexpr int kErrInvalidArg = static_cast(0x8a870006); constexpr std::uint8_t kFill = 0xaa; +struct GuardedRegion { + std::size_t pageSize{0}; + unsigned char* memory{nullptr}; + + GuardedRegion() { +#ifdef _WIN32 + SYSTEM_INFO info{}; + GetSystemInfo(&info); + pageSize = static_cast(info.dwPageSize); + memory = static_cast(VirtualAlloc(nullptr, pageSize * 2, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)); + Require(memory != nullptr); + DWORD previous; + Require(VirtualProtect(memory + pageSize, pageSize, PAGE_NOACCESS, &previous) != 0); +#else + const long nativePageSize = sysconf(_SC_PAGESIZE); + Require(nativePageSize > 0); + pageSize = static_cast(nativePageSize); + void* allocation = mmap(nullptr, pageSize * 2, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + Require(allocation != MAP_FAILED); + memory = static_cast(allocation); + Require(mprotect(memory + pageSize, pageSize, PROT_NONE) == 0); +#endif + } + + ~GuardedRegion() { +#ifdef _WIN32 + VirtualFree(memory, 0, MEM_RELEASE); +#else + munmap(memory, pageSize * 2); +#endif + } +}; + std::array ValidParam() { std::array param{}; param[0] = 0x20; @@ -40,6 +80,24 @@ int main() { Require(sceVrHandQueryMemory(nullptr, out.data()) == kErrInvalidArg && Untouched(out)); Require(sceVrHandQueryMemory(param.data(), nullptr) == kErrInvalidArg); + GuardedRegion region; + auto* const boundaryParam = region.memory + region.pageSize - 0x20; + std::memset(boundaryParam, 0, 0x20); + boundaryParam[0] = 0x20; + out.fill(kFill); + Require(sceVrHandQueryMemory(boundaryParam, out.data()) == 0); + + auto* const spanningParam = region.memory + region.pageSize - 0x10; + out.fill(kFill); + Require(sceVrHandQueryMemory(spanningParam, out.data()) == kErrInvalidArg && Untouched(out)); + + auto* const boundaryOut = region.memory + region.pageSize - 28; + std::memset(boundaryOut, kFill, 28); + Require(sceVrHandQueryMemory(param.data(), boundaryOut) == 0); + + auto* const spanningOut = region.memory + region.pageSize - 8; + Require(sceVrHandQueryMemory(param.data(), spanningOut) == kErrInvalidArg); + for (const std::uint32_t size : {0u, 0x1fu, 0x21u, 0x40u, 0x120u, 0x10020u, 0x1000020u}) { param = ValidParam(); std::memcpy(param.data(), &size, sizeof(size)); diff --git a/docs/dev/debt/libSceVrHand.md b/docs/dev/debt/libSceVrHand.md index c91567c01d..e0348a8e35 100644 --- a/docs/dev/debt/libSceVrHand.md +++ b/docs/dev/debt/libSceVrHand.md @@ -3,4 +3,4 @@ ### Unknown function info - [sceVrHandFinalize, sceVrHandGetMesh, sceVrHandGetMeshSize, sceVrHandGetResult, sceVrHandInitialize, sceVrHandStart, sceVrHandStop](../../../core/libs/prx/libSceVrHand/Export.cpp) (libSceVrHand) - names and NIDs verified from pinned [ps5rs stubs](https://github.com/claimore22/ps5rs/tree/5f252a658ff470bcb2b6cd15205fd8ca64ae4477/data/stubs/by_library) (`libSceVrHand.txt`), with NID match confirmed via `tools/nid_names.py` `compute_nid`. Signatures, parameter layouts, and runtime behavior remain unverified; all paths throw through `NotImplemented_nid_no_patch`. -- [sceVrHandQueryMemory](../../../core/libs/prx/libSceVrHand/Export.cpp) (libSceVrHand) - the meaning of the parameter and output fields is unknown. It follows firmware 13.42's libSceVrHand.sprx, read from its disassembly and run natively on x86-64 (not a console): a null pointer, unreadable or unwritable pointers, a first 32-bit parameter word other than 0x20, or any nonzero parameter byte from offset 4 to 31 returns `0x8a870006` and writes nothing; otherwise it stores the 32-bit values 0x3c00000 and 0x10000 at output offsets 0 and 4, clears output bytes 8 to 27 and returns 0. Returning `0x8a870006` for unwritable or unreadable pointers is a defensive host-protection fallback; console behavior is unmeasured. +- [sceVrHandQueryMemory](../../../core/libs/prx/libSceVrHand/Export.cpp) (libSceVrHand) - the meaning of the parameter and output fields is unknown. It follows firmware 13.42's libSceVrHand.sprx, read from its disassembly and run natively on x86-64 (not a console): a null pointer, an unreadable parameter, a first 32-bit parameter word other than 0x20, or any nonzero parameter byte from offset 4 to 31 returns `0x8a870006` and writes nothing; an unwritable result pointer returns `0x8a870006` (with possible partial output on copy-out failure). Otherwise it stores the 32-bit values 0x3c00000 and 0x10000 at output offsets 0 and 4, clears output bytes 8 to 27 and returns 0. Returning `0x8a870006` for unwritable or unreadable pointers is a defensive host-protection fallback; console behavior is unmeasured.