Files
AnyPS5/core/libs/tests/GuestRaiseInHostCode.cpp
T
billythemenace2andClaude Opus 5.5 d2657b9750 fix(libkernel): classify the raise target's code without loader calls while it is suspended
GetModuleHandleExW and GetModuleFileNameW take loader locks the suspended
target may hold. While the target is suspended only VirtualQuery and a
lock-free cache of image bases are consulted; an image not yet cached is
classified after the target is resumed, and the target is sampled again.
The statics the checks use are initialized before the first suspend. The
host-code wait is recorded in the libkernel debt file, and the test raises
100 times so it fails on every run without the change.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 17:22:50 +01:00

70 lines
2.4 KiB
C++

#include "SceTypes.hpp"
#include <atomic>
#include <chrono>
#include <cstdio>
#include <cstdint>
#include <cstdlib>
#include <string>
#include <thread>
extern "C" {
int APS5_VABI scePthreadCreate(Pthread*, const PthreadAttr*, PthreadEntry, void*, const char*);
int APS5_VABI scePthreadJoin(Pthread, void**);
int APS5_VABI sceKernelInstallExceptionHandler(int, void*);
int APS5_VABI sceKernelRemoveExceptionHandler(int);
int APS5_VABI sceKernelRaiseException(Pthread, int);
}
static constexpr int SigUsr1 = 30;
static constexpr int Raises = 100;
static void Require(bool value) { if (!value) std::abort(); }
static std::FILE* shared = nullptr;
static std::atomic<int> deliveries{0};
static std::atomic<int> released{0};
static std::atomic<bool> stop{false};
static std::atomic<bool> started{false};
static void APS5_VABI Handler(int, void*) {
const auto delivery = deliveries.fetch_add(1) + 1;
while (released.load() < delivery) std::this_thread::yield();
}
static void* APS5_VABI Writer(void*) {
const std::string block(1u << 20u, 'x');
started.store(true);
while (!stop.load()) {
Require(std::fwrite(block.data(), 1, block.size(), shared) == block.size());
Require(std::fseek(shared, 0, SEEK_SET) == 0);
volatile std::uint64_t spin = 0;
for (std::uint64_t i = 0; i < 200000; ++i) spin = spin + i;
}
return nullptr;
}
int main() {
std::atomic<bool> done{false};
std::thread watchdog([&done] {
for (int i = 0; i < 20000 && !done.load(); ++i) std::this_thread::sleep_for(std::chrono::milliseconds(1));
if (!done.load()) std::abort();
});
shared = std::tmpfile();
Require(shared != nullptr);
Require(sceKernelInstallExceptionHandler(SigUsr1, reinterpret_cast<void*>(&Handler)) == 0);
Pthread writer = nullptr;
Require(scePthreadCreate(&writer, nullptr, Writer, nullptr, "host writer") == 0);
while (!started.load()) std::this_thread::yield();
for (int raise = 1; raise <= Raises; ++raise) {
Require(sceKernelRaiseException(writer, SigUsr1) == 0);
while (deliveries.load() < raise) std::this_thread::yield();
Require(std::fputc('y', shared) == 'y');
released.store(raise);
}
stop.store(true);
Require(scePthreadJoin(writer, nullptr) == 0);
Require(sceKernelRemoveExceptionHandler(SigUsr1) == 0);
std::fclose(shared);
done.store(true);
watchdog.join();
}