From 9473294672f189a4126be2b74f72199a7f181bf2 Mon Sep 17 00:00:00 2001 From: frankea Date: Fri, 23 Oct 2026 06:32:06 -0400 Subject: [PATCH] fix(agc): read user-data callees up to the 1 MiB raw shader program limit --- .../src/Driver/Shaders/ShaderRegistry.cpp | 20 +++++++++----- .../tests/execution/ComputeUserDataCalls.cpp | 27 +++++++++++++++++++ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp index 1065f7921f56..1f3f9a2a1c3e 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp @@ -887,20 +887,28 @@ std::string hexText(std::uint64_t value) { std::vector ReadUserDataCallee(std::uint64_t address, std::uint32_t linkRegister) { constexpr std::uint64_t page = 4096u; - constexpr std::size_t limitWords = 16384u; + constexpr std::size_t limitWords = 1024u * 1024u / 4u; if (address % 4u != 0u) throw std::runtime_error("its callee address " + hexText(address) + " is not dword aligned"); std::vector words; + const auto finish = [&] { + const auto length = ShaderRecompiler::UserDataCalleeLength(words, linkRegister); + if (length) words.resize(*length); + return length.has_value(); + }; + std::size_t nextCheck = 0; while (words.size() < limitWords) { const auto next = address + words.size() * 4u; const auto bytes = static_cast(page - next % page); - if (!GuestMemory::Accessible(reinterpret_cast(static_cast(next)), bytes)) throw std::runtime_error("its callee at " + hexText(address) + " is not readable at " + hexText(next)); + if (!GuestMemory::Accessible(reinterpret_cast(static_cast(next)), bytes)) { + if (finish()) return words; + throw std::runtime_error("its callee at " + hexText(address) + " is not readable at " + hexText(next)); + } const auto old = words.size(); words.resize(old + bytes / 4u); GuestMemory::Read(next, std::as_writable_bytes(std::span(words).subspan(old))); - if (const auto length = ShaderRecompiler::UserDataCalleeLength(words, linkRegister)) { - words.resize(*length); - return words; - } + if (words.size() < nextCheck && words.size() < limitWords) continue; + nextCheck = words.size() * 2u; + if (finish()) return words; } throw std::runtime_error("its callee at " + hexText(address) + " does not return within " + std::to_string(limitWords * 4u) + " bytes"); } diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/ComputeUserDataCalls.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/ComputeUserDataCalls.cpp index f69cfaf201b7..314e0bc467a4 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/ComputeUserDataCalls.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/ComputeUserDataCalls.cpp @@ -4,6 +4,7 @@ #include "SceShaders.hpp" #include "VulkanTestDevice.hpp" #include +#include #include #include #include @@ -11,6 +12,11 @@ #include #include #include +#ifdef _WIN32 +#include +#else +#include +#endif namespace { @@ -92,6 +98,25 @@ void Register(bool wave32) { AgcDriverRegisterShader_nid_postfix(&header.shader); } +const void* LongCallee() { + constexpr std::size_t page = 4096u; + constexpr std::size_t words = 20000u; + constexpr std::size_t committed = (words * 4u + page - 1u) / page * page; +#ifdef _WIN32 + auto* block = static_cast(VirtualAlloc(nullptr, committed + page, MEM_RESERVE, PAGE_NOACCESS)); + Require(block != nullptr && VirtualAlloc(block, committed, MEM_COMMIT, PAGE_READWRITE) != nullptr, "cannot reserve the long callee"); +#else + void* mapped = mmap(nullptr, committed + page, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + Require(mapped != MAP_FAILED && mprotect(mapped, committed, PROT_READ | PROT_WRITE) == 0, "cannot reserve the long callee"); + auto* block = static_cast(mapped); +#endif + block[0] = 0x4a020281u; + for (std::size_t word = 1; word + 1u < words; ++word) block[word] = 0xbf800000u; + block[words - 1u] = 0xbe80200eu; + for (std::size_t word = words; word < committed / 4u; ++word) block[word] = 0xbf810000u; + return block; +} + template void ExpectFailure(TAction action, const char* expected) { try { @@ -114,10 +139,12 @@ int main(int argc, char** argv) { if (!device) return VulkanTestSkipped; } if (registered) Register(true); + const auto* longCallee = LongCallee(); for (const bool wave32 : registered ? std::vector{true} : std::vector{false, true}) { Check(AddFive.data(), AddSixtyFour.data(), wave32, 3u * (5u + 64u), "two user-data callees called from a loop"); Check(AddOne.data(), AddSixtyFour.data(), wave32, 3u * (1u + 64u), "the first callee replaced"); Check(AddSixtyFour.data(), AddSixtyFour.data(), wave32, 3u * (64u + 64u), "one callee behind both pointers"); + Check(longCallee, AddSixtyFour.data(), wave32, 3u * (1u + 64u), "a callee of 20,000 dwords whose last page is followed by unreadable memory"); } const auto* unmapped = reinterpret_cast(std::uintptr_t{0x1000u}); ExpectFailure([&] { Dispatch(AddFive.data(), unmapped, registered); }, "is not readable");