Files
AnyPS5/core/libs/prx/libSceAgcDriver/tests/LiveStack.cpp
T
Yuri Olive aec5ba133f fix(agc): report guest addresses above the user address space as unmapped on Windows
A title can hand the GPU a buffer or descriptor V# based at an address above the
Windows user-mode address space (Kena: Bridge of Spirits, PPSA01746, bases a V#
at 0xf00000000000). VirtualQuery fails for any address above
lpMaximumApplicationAddress, so describePages returned "not queried" and the
driver aborted with "AGC driver: cannot query guest memory", although nothing can
ever be mapped there and the shader never dereferences it.

Above the user address limit, report the range as unmapped (as the Linux
procmap/maps path already does for an uncovered range) instead of failing the
query. The limit is read once from GetSystemInfo.

Tested: Windows 11, MinGW-w64 GCC 15.2.0, Release, agc_driver_graphics_tests
passes with the change and fails without it ("cannot query guest memory").
GPUs tested: NVIDIA GeForce RTX 4070 SUPER.
2026-10-13 17:56:49 -03:00

82 lines
4.0 KiB
C++

#include "BdaTests.hpp"
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
#include <cstddef>
#include <cstdint>
#include <exception>
#include <stdexcept>
#include <string>
#if defined(__linux__)
#include <pthread.h>
#include <sys/mman.h>
#include <unistd.h>
#endif
namespace {
using AgcDriver::Graphics::Require;
namespace GuestMemory = AgcDriver::GuestMemory;
void checkLiveStack(std::uintptr_t stackBottom, std::uintptr_t stackTop, std::uintptr_t guardEnd) {
alignas(16) volatile std::uint64_t packet[2] = {1, 2};
const auto* address = const_cast<const std::uint64_t*>(packet);
Require(GuestMemory::Accessible(address, sizeof(packet)) && GuestMemory::Accessible(address, sizeof(packet), true), "a live stack frame is not accessible");
GuestMemory::CheckRange(address, sizeof(packet), alignof(std::uint64_t), true);
if (stackTop == 0) return;
Require(GuestMemory::Accessible(reinterpret_cast<const void*>(stackTop - 16), 16, true), "the top of the live stack is not accessible");
Require(!GuestMemory::Accessible(reinterpret_cast<const void*>(stackTop - 16), 32), "a range past the stack's top counts as accessible");
Require(!GuestMemory::Accessible(reinterpret_cast<const void*>(stackTop), guardEnd - stackTop), "the page above the stack counts as accessible");
Require(!GuestMemory::Accessible(reinterpret_cast<const void*>(stackBottom), 16), "an inaccessible page of the stack below the live frames counts as accessible");
bool rejected = false;
try {
GuestMemory::CheckRange(reinterpret_cast<const void*>(stackTop - 8), 16, 8);
} catch (const std::runtime_error&) {
rejected = true;
}
Require(rejected, "CheckRange accepted a range past the stack's top");
}
}
void RunLiveStackAccessTests() {
checkLiveStack(0, 0, 0);
#if defined(_WIN32)
for (const auto address : {std::uintptr_t{0xf00000000000ull}, std::uintptr_t{0x7fffffffffffull}}) {
Require(!GuestMemory::Accessible(reinterpret_cast<const void*>(address), 4),
"an address above the user address space is reported accessible");
const auto committed = GuestMemory::CommittedRanges(address, 4, false);
Require(committed.empty(), "an address above the user address space reports committed bytes");
}
#endif
#if defined(__linux__)
const auto page = static_cast<std::size_t>(sysconf(_SC_PAGESIZE));
const std::size_t stackBytes = 256 * 1024;
auto* block = static_cast<std::byte*>(mmap(nullptr, stackBytes + page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
Require(block != MAP_FAILED, "cannot map a test stack");
Require(mprotect(block + stackBytes, page, PROT_NONE) == 0 && mprotect(block, page, PROT_NONE) == 0, "cannot protect the test stack's guard pages");
const auto top = reinterpret_cast<std::uintptr_t>(block) + stackBytes;
struct Arguments {
std::uintptr_t bottom;
std::uintptr_t top;
std::uintptr_t guardEnd;
std::string failure;
} arguments{reinterpret_cast<std::uintptr_t>(block), top, top + page, {}};
pthread_attr_t attributes;
Require(pthread_attr_init(&attributes) == 0 && pthread_attr_setstack(&attributes, block, stackBytes) == 0, "cannot set the test stack");
pthread_t thread;
Require(pthread_create(&thread, &attributes, [](void* raw) -> void* {
auto& arguments = *static_cast<Arguments*>(raw);
try {
checkLiveStack(arguments.bottom, arguments.top, arguments.guardEnd);
} catch (const std::exception& error) {
arguments.failure = error.what();
}
return nullptr;
}, &arguments) == 0, "cannot start the stack test thread");
pthread_join(thread, nullptr);
pthread_attr_destroy(&attributes);
Require(GuestMemory::Accessible(block + page, 64, true) && !GuestMemory::Accessible(block, 16) && !GuestMemory::Accessible(block + stackBytes, 16), "the released test stack is misreported");
munmap(block, stackBytes + page);
Require(arguments.failure.empty(), arguments.failure.c_str());
#endif
}