Files
ionut27001 bd22a701f5 fix(libkernel): answer sysarch I386_GET_FSBASE, I386_GET_GSBASE and undefined operations as measured on a PS5
In a payload process on a PS5 (firmware 12.70), I386_GET_FSBASE (7) and
I386_GET_GSBASE (9) wrote 4 bytes, the low 32 bits of the FS and GS
bases, and failed with EFAULT for a null argument; both threw here. On
Linux x86-64 hosts they now do the same.

The nine operation numbers tried that FreeBSD 11's amd64 sysarch has no
case for (2, 5, 6, 12, 127, 133, 134, 1000 and -1), each with a null
argument, failed with EINVAL there, as its default case does, and threw
here. Every such number now returns EINVAL on every host.

Threads made with pthread_create and scePthreadCreate started with their
creator's GS base there; the test now checks that a guest thread does so
on Linux. The sysarch debt entry records these measurements and what
AMD64_GET_XFPUSTATE and I386_GET_XFPUSTATE returned there; both still
throw here.
2026-10-18 22:25:09 +03:00

92 lines
3.4 KiB
C++

#include "SceTypes.hpp"
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <stdexcept>
#include <string>
extern "C" {
int APS5_VABI sysarch(int number, void* args);
int* APS5_VABI __error_nid_postfix();
int APS5_VABI scePthreadCreate(Pthread* thread, const PthreadAttr* attr, PthreadEntry entry, void* arg, const char* name);
int APS5_VABI scePthreadJoin(Pthread thread, void** retval);
}
static void Require(bool value) { if (!value) std::abort(); }
template <typename TCall> static std::string Thrown(TCall call) {
try {
call();
} catch (const std::runtime_error& error) {
return error.what();
}
return {};
}
#if defined(__linux__) && defined(__x86_64__)
static void* APS5_VABI ReadGsBase(void* arg) {
auto* read = static_cast<std::uint64_t*>(arg);
if (sysarch(130, read) != 0) *read = 0;
return nullptr;
}
static void RequireLowHalf(int operation, std::uint64_t base) {
unsigned char bytes[8];
std::memset(bytes, 0xaa, sizeof(bytes));
Require(sysarch(operation, bytes) == 0);
std::uint32_t low = 0;
std::memcpy(&low, bytes, sizeof(low));
Require(low == static_cast<std::uint32_t>(base));
for (int i = 4; i < 8; ++i) Require(bytes[i] == 0xaa);
}
#endif
int main() {
Require(Thrown([] { sysarch(129, nullptr); }) == "sysarch: operation 129 not implemented");
Require(Thrown([] { sysarch(132, nullptr); }) == "sysarch: operation 132 not implemented");
for (const int operation : {2, 5, 6, 12, 127, 133, 134, 1000, -1}) {
*__error_nid_postfix() = 0;
Require(sysarch(operation, nullptr) == -1 && *__error_nid_postfix() == 22);
}
#if defined(__linux__) && defined(__x86_64__)
static std::uint64_t slots[2] = {0x1234567890abcdefull, 0};
std::uint64_t base = reinterpret_cast<std::uintptr_t>(slots);
Require(sysarch(131, &base) == 0);
std::uint64_t read = 0;
Require(sysarch(130, &read) == 0 && read == base);
std::uint64_t through = 0;
asm volatile("movq %%gs:0, %0" : "=r"(through));
Require(through == slots[0]);
std::uint64_t fs = 0;
Require(sysarch(128, &fs) == 0 && fs != 0);
std::uint64_t self = 0;
asm volatile("movq %%fs:0, %0" : "=r"(self));
Require(self == fs);
RequireLowHalf(7, fs);
RequireLowHalf(9, base);
*__error_nid_postfix() = 0;
Require(sysarch(7, nullptr) == -1 && *__error_nid_postfix() == 14);
*__error_nid_postfix() = 0;
Require(sysarch(9, nullptr) == -1 && *__error_nid_postfix() == 14);
std::uint64_t inherited = 0;
Pthread child = nullptr;
Require(scePthreadCreate(&child, nullptr, ReadGsBase, &inherited, "sysarch_gs") == 0);
Require(scePthreadJoin(child, nullptr) == 0 && inherited == base);
std::uint64_t outside = 0x0000800000000000ull;
*__error_nid_postfix() = 0;
Require(sysarch(131, &outside) == -1 && *__error_nid_postfix() == 22);
Require(sysarch(130, &read) == 0 && read == base);
*__error_nid_postfix() = 0;
Require(sysarch(131, nullptr) == -1 && *__error_nid_postfix() == 14);
Require(sysarch(130, nullptr) == -1 && *__error_nid_postfix() == 14);
std::uint64_t zero = 0;
Require(sysarch(131, &zero) == 0 && sysarch(130, &read) == 0 && read == 0);
#else
std::uint64_t base = 0;
Require(Thrown([&base] { sysarch(130, &base); }) == "sysarch: segment bases on this host not implemented");
Require(Thrown([&base] { sysarch(7, &base); }) == "sysarch: segment bases on this host not implemented");
#endif
return 0;
}