AnyPS5/core/libs/tests/GuestJitSharedMemory.cpp

175 lines
7.4 KiB
C++

#include "prx/libc/include/general/VabiMacros.hpp"
#include "prx/libkernel/KernelErrors.hpp"
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <source_location>
extern "C" {
int APS5_VABI sceKernelJitCreateSharedMemory(const char*, std::size_t, int, int*);
int APS5_VABI sceKernelJitCreateAliasOfSharedMemory(int, int, int*);
int APS5_VABI sceKernelJitMapSharedMemory(int, int, void**);
int APS5_VABI sceKernelMunmap(void*, std::size_t);
int APS5_VABI close_nid_postfix(int);
int APS5_VABI dup_nid_postfix(int);
int APS5_VABI dup2_nid_postfix(int, int);
int APS5_VABI shm_open_nid_postfix(const char*, int, int);
}
namespace {
constexpr std::size_t Size = 0x10000;
constexpr int Read = 0x1;
constexpr int Write = 0x2;
constexpr int Execute = 0x4;
void Require(bool condition, std::source_location location = std::source_location::current()) {
if (condition) return;
std::fprintf(stderr, "requirement failed at line %u\n", static_cast<unsigned>(location.line()));
std::abort();
}
int Create(const char* name, int maxProt) {
int fd = -1;
Require(sceKernelJitCreateSharedMemory(name, Size, maxProt, &fd) == 0);
Require(fd >= 0);
return fd;
}
void* Map(int fd, int prot, void* hint = nullptr) {
void* address = hint;
Require(sceKernelJitMapSharedMemory(fd, prot, &address) == 0);
Require(address != nullptr && (reinterpret_cast<std::uintptr_t>(address) & 0x3fff) == 0);
return address;
}
int Call(const void* code) {
using Function = int (*)();
Function function;
std::memcpy(&function, &code, sizeof(function));
return function();
}
void WriteReturn(void* code, std::uint32_t value) {
unsigned char bytes[] = {0xb8, 0, 0, 0, 0, 0xc3};
std::memcpy(bytes + 1, &value, sizeof(value));
std::memcpy(code, bytes, sizeof(bytes));
}
void WriteThroughAliasRunsThroughExecutableView() {
const int fd = Create(nullptr, Read | Write | Execute);
int alias = -1;
Require(sceKernelJitCreateAliasOfSharedMemory(fd, Read | Write, &alias) == 0);
Require(alias >= 0 && alias != fd);
auto* writable = static_cast<unsigned char*>(Map(alias, Read | Write));
auto* executable = static_cast<unsigned char*>(Map(fd, Read | Execute));
Require(writable != executable);
Require(executable[0] == 0 && executable[Size - 1] == 0);
WriteReturn(writable, 0x12345678);
Require(std::memcmp(writable, executable, 6) == 0);
Require(Call(executable) == 0x12345678);
WriteReturn(writable + 0x4000, 0x0badcafe);
Require(Call(executable + 0x4000) == 0x0badcafe);
writable[Size - 1] = 0x5a;
Require(executable[Size - 1] == 0x5a);
Require(close_nid_postfix(fd) == 0);
auto* again = static_cast<unsigned char*>(Map(alias, Read));
Require(again[Size - 1] == 0x5a);
Require(sceKernelMunmap(again, Size) == 0);
Require(sceKernelMunmap(writable, Size) == 0);
Require(Call(executable) == 0x12345678);
Require(sceKernelMunmap(executable, Size) == 0);
Require(close_nid_postfix(alias) == 0);
}
void OneViewWritesAndRuns() {
const int fd = Create("ps5-platform-probe", Read | Write | Execute);
void* hint = reinterpret_cast<void*>(std::uintptr_t{0x1000000000});
auto* code = static_cast<unsigned char*>(Map(fd, Read | Write | Execute, hint));
Require(reinterpret_cast<std::uintptr_t>(code) >= 0x1000000000);
WriteReturn(code, 42);
Require(Call(code) == 42);
const int duplicate = dup_nid_postfix(fd);
Require(duplicate >= 0);
Require(close_nid_postfix(fd) == 0);
auto* view = static_cast<unsigned char*>(Map(duplicate, Read | Execute));
Require(Call(view) == 42);
Require(sceKernelMunmap(view, Size) == 0);
Require(sceKernelMunmap(code, Size) == 0);
Require(close_nid_postfix(duplicate) == 0);
}
void Dup2KeepsTheDescriptorTableInStep() {
const int fd = Create(nullptr, Read | Write);
const int target = shm_open_nid_postfix(reinterpret_cast<const char*>(1), 0x2, 0600);
Require(target >= 0 && target != fd);
Require(dup2_nid_postfix(fd, target) == target);
auto* writable = static_cast<unsigned char*>(Map(fd, Read | Write));
auto* readable = static_cast<unsigned char*>(Map(target, Read));
writable[0x100] = 0x77;
Require(readable[0x100] == 0x77);
void* address = nullptr;
Require(sceKernelJitMapSharedMemory(target, Read | Execute, &address) == SCE_KERNEL_ERROR_EACCES);
Require(dup2_nid_postfix(fd, fd) == fd);
auto* same = static_cast<unsigned char*>(Map(fd, Read));
Require(same[0x100] == 0x77);
const int plain = shm_open_nid_postfix(reinterpret_cast<const char*>(1), 0x2, 0600);
Require(plain >= 0);
Require(dup2_nid_postfix(plain, target) == target);
address = nullptr;
Require(sceKernelJitMapSharedMemory(target, Read, &address) == SCE_KERNEL_ERROR_EBADF);
Require(address == nullptr);
Require(sceKernelMunmap(same, Size) == 0);
Require(sceKernelMunmap(readable, Size) == 0);
Require(sceKernelMunmap(writable, Size) == 0);
Require(close_nid_postfix(plain) == 0);
Require(close_nid_postfix(target) == 0);
Require(close_nid_postfix(fd) == 0);
}
void InvalidArgumentsAreRejected() {
int fd = -1;
Require(sceKernelJitCreateSharedMemory(nullptr, 0, Read | Write | Execute, &fd) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelJitCreateSharedMemory(nullptr, 0x1000, Read | Write | Execute, &fd) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelJitCreateSharedMemory(nullptr, Size + 0x2000, Read | Write | Execute, &fd) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelJitCreateSharedMemory(nullptr, Size, 0x10, &fd) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelJitCreateSharedMemory(nullptr, Size, Read | Write | Execute, nullptr) == SCE_KERNEL_ERROR_EINVAL);
Require(fd == -1);
fd = Create(nullptr, Read | Execute);
int alias = -1;
void* address = nullptr;
Require(sceKernelJitCreateAliasOfSharedMemory(fd, Read | Write, &alias) == SCE_KERNEL_ERROR_EACCES);
Require(sceKernelJitCreateAliasOfSharedMemory(fd, 0x20, &alias) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelJitCreateAliasOfSharedMemory(fd, Read, nullptr) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelJitCreateAliasOfSharedMemory(-1, Read, &alias) == SCE_KERNEL_ERROR_EBADF);
Require(alias == -1);
Require(sceKernelJitMapSharedMemory(fd, Read | Write, &address) == SCE_KERNEL_ERROR_EACCES);
Require(sceKernelJitMapSharedMemory(fd, 0x8, &address) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelJitMapSharedMemory(fd, Read, nullptr) == SCE_KERNEL_ERROR_EINVAL);
Require(sceKernelJitMapSharedMemory(-1, Read, &address) == SCE_KERNEL_ERROR_EBADF);
Require(address == nullptr);
Require(sceKernelJitCreateAliasOfSharedMemory(fd, Read, &alias) == 0);
Require(sceKernelJitMapSharedMemory(alias, Read | Execute, &address) == SCE_KERNEL_ERROR_EACCES);
Require(close_nid_postfix(alias) == 0);
Require(close_nid_postfix(fd) == 0);
Require(sceKernelJitMapSharedMemory(fd, Read, &address) == SCE_KERNEL_ERROR_EBADF);
Require(sceKernelJitCreateAliasOfSharedMemory(alias, Read, &alias) == SCE_KERNEL_ERROR_EBADF);
const int shared = shm_open_nid_postfix(reinterpret_cast<const char*>(1), 0x2, 0600);
Require(shared >= 0);
Require(sceKernelJitMapSharedMemory(shared, Read, &address) == SCE_KERNEL_ERROR_EBADF);
Require(close_nid_postfix(shared) == 0);
}
}
int main() {
WriteThroughAliasRunsThroughExecutableView();
OneViewWritesAndRuns();
Dup2KeepsTheDescriptorTableInStep();
InvalidArgumentsAreRejected();
}