Files
akkadbakkad89 44f2c7f607 fix(libScePsml): serialize the shared-resources busy check and insert
The single-instance guard checked g_mfsrObjects under g_mfsrMutex, released
the lock, then reacquired it inside CreateMfsrObject to insert, so two
concurrent creates could both observe no shared-resources object and both
succeed. Hold the mutex across the busy check and the emplace instead; the
lock scope also preserves the firmware order, which returns 0x8A810015
before validating the arguments. guest_psml_tests gains a concurrent
create check for the reachable uninitialized contract; the busy branch
itself cannot be exercised while scePsmlMfsrInit cannot succeed on the
modelled console.
2026-10-26 01:41:41 +05:30

178 lines
8.2 KiB
C++

#include "prx/libScePsml_debug/libScePsml_debug.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include <array>
#include <atomic>
#include <cstdint>
#include <cstdlib>
#include <thread>
#include <vector>
namespace {
void Require(bool value) { if (!value) std::abort(); }
constexpr std::int32_t kErrNotInitialized = static_cast<std::int32_t>(0x8A810001);
using GetDispatchPacket = std::int32_t (APS5_VABI*)(void*, void*, const void*);
template <typename TParam>
void CreateContextUntouched(std::int32_t (APS5_VABI* create)(void**, const TParam*)) {
std::array<std::uint8_t, 0x158> param{};
std::uint64_t contextStorage = 0;
void* const sentinel = &contextStorage;
void* context = sentinel;
Require(create(&context, reinterpret_cast<const TParam*>(param.data())) == kErrNotInitialized);
Require(context == sentinel);
Require(create(nullptr, nullptr) == kErrNotInitialized);
}
void DispatchPacketUntouched(GetDispatchPacket dispatch) {
std::array<std::uint8_t, 0x158> param{};
std::uint64_t contextStorage = 0;
std::array<std::uint32_t, 64> dwords;
dwords.fill(0xa5a5a5a5u);
const auto untouchedDwords = dwords;
MfsrCommandBuffer commandBuffer{dwords.data(), static_cast<std::uint32_t>(dwords.size())};
Require(dispatch(&contextStorage, &commandBuffer, param.data()) == kErrNotInitialized);
Require(dispatch(nullptr, &commandBuffer, nullptr) == kErrNotInitialized);
Require(commandBuffer.cursor == dwords.data());
Require(commandBuffer.sizeInDwords == dwords.size());
Require(dwords == untouchedDwords);
}
void SetupBeforeInit() {
std::uint64_t storage = 0;
void* const sentinel = &storage;
void* handle = sentinel;
MfsrRequirement requirement{1, 2, 3};
MfsrSharedResourcesParam sharedParam{};
Require(scePsmlMfsrGetSharedResourcesInitRequirement(&requirement, &sharedParam) == kErrNotInitialized);
Require(requirement.blockBytes == 1 && requirement.alignment == 2 && requirement.blockCount == 3);
Require(scePsmlMfsrCreateSharedResources(&handle, &sharedParam) == kErrNotInitialized);
Require(handle == sentinel);
Require(scePsmlMfsrReleaseContext(sentinel) == kErrNotInitialized);
Require(scePsmlMfsrReleaseSharedResources(sentinel) == kErrNotInitialized);
std::uint32_t packetDwords = 0xa5a5a5a5;
Require(scePsmlMfsrGetDispatchMfsrPacketSizeInDwords(sentinel, &packetDwords) == kErrNotInitialized);
Require(packetDwords == 0xa5a5a5a5);
float bias = 0.5f;
Require(scePsmlMfsrGetMipmapBias(sentinel, &bias, 3840) == kErrNotInitialized);
Require(bias == 0.5f);
Require(scePsmlMfsrSelectConfig(sentinel) == kErrNotInitialized);
Require(scePsmlMfsrRequestCapture(sentinel) == kErrNotInitialized);
Require(scePsmlMfsrIsCaptureInProgress(sentinel) == kErrNotInitialized);
}
void Mfsr1OnBaseConsole() {
constexpr std::int32_t unsupportedConsole = static_cast<std::int32_t>(0x8A810012);
Require(scePsmlMfsrInit() == unsupportedConsole);
Require(scePsmlMfsrInit() == unsupportedConsole);
SetupBeforeInit();
}
void ConcurrentCreatesUninitialized() {
std::atomic<int> errors{0};
std::vector<std::thread> pool;
for (int i = 0; i < 8; ++i) {
pool.emplace_back([&] {
for (int round = 0; round < 64; ++round) {
MfsrSharedResourcesParam sharedParam{};
void* handle = reinterpret_cast<void*>(static_cast<std::uintptr_t>(0xdeadbeef));
if (scePsmlMfsrCreateSharedResources(&handle, &sharedParam) != kErrNotInitialized || handle != reinterpret_cast<void*>(static_cast<std::uintptr_t>(0xdeadbeef))) ++errors;
}
});
}
for (auto& worker : pool) worker.join();
Require(errors == 0);
}
void Mfsr2OnBaseConsole() {
constexpr std::int32_t notInitialized = static_cast<std::int32_t>(0x8A8A0001);
constexpr std::int32_t unsupportedConsole = static_cast<std::int32_t>(0x8A8A0012);
Require(scePsmlMfsr2Init() == unsupportedConsole);
Require(scePsmlMfsr2Init() == unsupportedConsole);
std::array<std::uint8_t, 0x30> param{};
std::array<std::uint8_t, 0x18> requirement;
requirement.fill(0xa5);
const auto untouchedRequirement = requirement;
Require(scePsmlMfsr2GetSharedResourcesInitRequirement(reinterpret_cast<MfsrRequirement*>(requirement.data()), reinterpret_cast<const Mfsr2SharedResourcesParam*>(param.data())) == notInitialized);
Require(scePsmlMfsr2GetContextInitRequirement(reinterpret_cast<MfsrRequirement*>(requirement.data()), reinterpret_cast<const Mfsr2ContextParam*>(param.data())) == notInitialized);
Require(requirement == untouchedRequirement);
std::uint64_t storage = 0;
void* const sentinel = &storage;
void* handle = sentinel;
Require(scePsmlMfsr2CreateSharedResources(&handle, reinterpret_cast<const Mfsr2SharedResourcesParam*>(param.data())) == notInitialized);
Require(scePsmlMfsr2CreateContext(&handle, reinterpret_cast<const Mfsr2ContextParam*>(param.data())) == notInitialized);
Require(handle == sentinel);
Require(scePsmlMfsr2ReleaseContext(sentinel) == notInitialized);
Require(scePsmlMfsr2ReleaseSharedResources(sentinel) == notInitialized);
std::uint32_t packetDwords = 0xa5a5a5a5;
Require(scePsmlMfsr2GetDispatchPacketsSizeInDwords(sentinel, &packetDwords) == notInitialized);
Require(packetDwords == 0xa5a5a5a5);
std::array<std::uint8_t, 112> dispatchParam{};
std::array<std::uint32_t, 64> dwords;
dwords.fill(0xa5a5a5a5u);
const auto untouchedDwords = dwords;
MfsrCommandBuffer commandBuffer{dwords.data(), static_cast<std::uint32_t>(dwords.size())};
Require(scePsmlMfsr2GetDispatchPackets(sentinel, &commandBuffer, reinterpret_cast<const Mfsr2DispatchParam*>(dispatchParam.data())) == notInitialized);
Require(commandBuffer.cursor == dwords.data());
Require(commandBuffer.sizeInDwords == dwords.size());
Require(dwords == untouchedDwords);
}
}
int main() {
std::array<std::uint8_t, 0x158> param{};
std::array<std::uint8_t, 0x18> requirement;
requirement.fill(0xa5);
const auto untouchedRequirement = requirement;
Require(scePsmlMfsrGetContextBufferRequirement1100(requirement.data(), param.data()) == kErrNotInitialized);
Require(requirement == untouchedRequirement);
Require(scePsmlMfsrGetContextBufferRequirement1100(nullptr, nullptr) == kErrNotInitialized);
std::uint64_t contextStorage = 0;
void* const sentinel = &contextStorage;
void* context = sentinel;
Require(scePsmlMfsrCreateContext1100(&context, param.data()) == kErrNotInitialized);
Require(context == sentinel);
Require(scePsmlMfsrCreateContext1100(nullptr, nullptr) == kErrNotInitialized);
std::array<std::uint32_t, 64> dwords;
dwords.fill(0xa5a5a5a5u);
const auto untouchedDwords = dwords;
MfsrCommandBuffer commandBuffer{dwords.data(), static_cast<std::uint32_t>(dwords.size())};
Require(scePsmlMfsrGetDispatchMfsrPacket1100(&contextStorage, &commandBuffer, param.data()) == kErrNotInitialized);
Require(commandBuffer.cursor == dwords.data());
Require(commandBuffer.sizeInDwords == dwords.size());
Require(dwords == untouchedDwords);
Require(scePsmlMfsrGetDispatchMfsrPacket1100(nullptr, &commandBuffer, param.data()) == kErrNotInitialized);
Require(commandBuffer.cursor == dwords.data());
Require(dwords == untouchedDwords);
std::array<std::uint8_t, 0x18> olderRequirement;
olderRequirement.fill(0xa5);
const auto untouchedOlderRequirement = olderRequirement;
Require(scePsmlMfsrGetContextBufferRequirement800M3_2(reinterpret_cast<MfsrRequirement*>(olderRequirement.data()), reinterpret_cast<const MfsrContextParam*>(param.data())) == kErrNotInitialized);
Require(olderRequirement == untouchedOlderRequirement);
Require(scePsmlMfsrGetContextBufferRequirement800M3_2(nullptr, nullptr) == kErrNotInitialized);
CreateContextUntouched(scePsmlMfsrCreateContext800M3_2);
CreateContextUntouched(scePsmlMfsrCreateContext1300);
DispatchPacketUntouched(scePsmlMfsrGetDispatchMfsrPacket900);
DispatchPacketUntouched(scePsmlMfsrGetDispatchMfsrPacket1000);
DispatchPacketUntouched(scePsmlMfsrGetDispatchMfsrPacket1300);
SetupBeforeInit();
Mfsr1OnBaseConsole();
Mfsr2OnBaseConsole();
ConcurrentCreatesUninitialized();
}