#include "prx/libc/include/general/VabiMacros.hpp" #include "prx/libkernel/KernelErrors.hpp" #include "SceTypes.hpp" #include #include #include #include extern "C" { void* APS5_VABI mmap_nid_postfix(void*, std::size_t, int, int, int, std::int64_t) noexcept; int APS5_VABI munmap_nid_postfix(void*, std::size_t) noexcept; int APS5_VABI mprotect_nid_postfix(void*, std::size_t, int) noexcept; int APS5_VABI sceKernelMprotect(const void*, std::size_t, int); int APS5_VABI sceKernelMtypeprotect(const void*, std::size_t, int, int); int APS5_VABI sceKernelVirtualQuery(const void*, int, VirtualQueryInfo*, std::uint64_t); int* APS5_VABI __error_nid_postfix(); } namespace { constexpr std::size_t Page = 0x4000; constexpr int ErrorInvalid = 22; constexpr int ProtectRead = 1; constexpr int ProtectReadWrite = 3; void Require(bool condition, const char* what) { if (condition) return; std::fprintf(stderr, "%s\n", what); std::abort(); } int Protect(const void* address, std::size_t bytes, int protection) { try { return sceKernelMprotect(address, bytes, protection); } catch (const std::exception& error) { std::fprintf(stderr, "sceKernelMprotect threw: %s\n", error.what()); std::abort(); } } int ProtectionAt(const unsigned char* address) { VirtualQueryInfo info{}; Require(sceKernelVirtualQuery(address, 0, &info, sizeof(info)) == 0, "virtual query of a mapped page failed"); return info.protection; } unsigned char* Map(std::size_t pages) { void* raw = mmap_nid_postfix(nullptr, Page * pages, ProtectReadWrite, 0x1002, -1, 0); Require(raw != reinterpret_cast(static_cast(-1)), "mmap failed"); return static_cast(raw); } } int main() { unsigned char* memory = Map(3); Require(munmap_nid_postfix(memory + Page, Page) == 0, "munmap of the middle page failed"); Require(Protect(memory, Page * 3, ProtectRead) == 0, "a range with a gap in the middle must succeed for its mapped parts"); Require(ProtectionAt(memory) == ProtectRead && ProtectionAt(memory + Page * 2) == ProtectRead, "the mapped parts on both sides of the gap must be protected"); Require(mprotect_nid_postfix(memory, Page * 3, ProtectReadWrite) == 0, "mprotect over a range with a gap must succeed for its mapped parts"); Require(ProtectionAt(memory) == ProtectReadWrite && ProtectionAt(memory + Page * 2) == ProtectReadWrite, "mprotect must change the mapped parts on both sides of the gap"); Require(sceKernelMtypeprotect(memory, Page * 3, 0, ProtectRead) == 0, "sceKernelMtypeprotect over a range with a gap must succeed for its mapped parts"); Require(Protect(memory + Page - 8, 16, ProtectReadWrite) == 0, "an unaligned range that reaches into the unmapped page protects the mapped page"); Require(Protect(memory + 100, Page - 200, ProtectReadWrite) == 0, "an unaligned range inside the first page must work"); Require(Protect(memory + Page, Page, ProtectRead) == SCE_KERNEL_ERROR_EINVAL, "a range that is completely unmapped must return EINVAL"); Require(Protect(memory + Page * 2, Page, ProtectReadWrite) == 0, "protecting the last page alone must work"); Require(Protect(memory + Page, Page * 2, ProtectRead) == SCE_KERNEL_ERROR_EINVAL, "a range whose start is unmapped must return EINVAL even when later pages are mapped"); Require(ProtectionAt(memory + Page * 2) == ProtectReadWrite, "a refused range must not change the mapped pages behind its start"); Require(mprotect_nid_postfix(memory + Page, Page, ProtectRead) == -1 && *__error_nid_postfix() == ErrorInvalid, "mprotect over a range that is completely unmapped must fail with EINVAL (22)"); Require(munmap_nid_postfix(memory, Page) == 0 && munmap_nid_postfix(memory + Page * 2, Page) == 0, "munmap of the remaining pages failed"); unsigned char* tail = Map(2); Require(munmap_nid_postfix(tail + Page, Page) == 0, "munmap of the last page failed"); Require(Protect(tail, Page * 2, ProtectRead) == 0, "a range that ends in an unmapped page must succeed for its mapped part"); Require(ProtectionAt(tail) == ProtectRead, "the mapped part before a trailing gap must be protected"); Require(munmap_nid_postfix(tail, Page) == 0, "munmap of the remaining page failed"); return 0; }