fix(libc): register guest heap blocks without GPU access

AnyPS5's libc heap registered every block with GPU access, so every
address-based build imported or mirrored each malloc block the title had
made. The console's libc.prx maps its heap with protection 3 (CPU read
and write, no GPU bits), so the GPU never sees it there either.

Heap blocks are now registered without GPU access; reallocating keeps the
block's access. The AvPlayer fallback for a title that passes no texture
allocator stands in for that allocator, which hands out memory the GPU
reads, so it allocates through a new GuestHeapAlignForGpu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
raghav314 2026-10-11 06:52:21 +05:30
parent d31b57a4ca
commit aa013f59ac
5 changed files with 47 additions and 10 deletions

View File

@ -741,11 +741,38 @@ void RunBdaResourceTests(const Context& context, const BdaTestAccess& access) {
tail.Upload(true);
reject([&] { tail.Descriptor(address + 58, 4, adjustment); }, "exceeds its GPU owner");
}
{
void* plain = GuestHeap::GuestHeapAllocate_nid_postfix(64);
void* gpu = GuestHeap::GuestHeapAlignForGpu_nid_postfix(16, 64);
const auto lookup = [](const void* pointer) {
GuestAllocations::Mutation mutation;
return mutation.Find(pointer);
};
Require(!lookup(plain).gpu && lookup(gpu).gpu, "a guest heap block's GPU access does not follow how it was allocated");
gpu = GuestHeap::GuestHeapReallocate_nid_postfix(gpu, 128);
plain = GuestHeap::GuestHeapRealign_nid_postfix(plain, 128, 64);
Require(!lookup(plain).gpu && lookup(gpu).gpu, "a reallocated guest heap block changed its GPU access");
{
GuestBufferMemory leased(context);
leased.AcquireRegistered();
leased.Upload(true);
const auto ranges = leased.AddressRanges();
const auto listed = [&](const void* pointer) {
const auto address = reinterpret_cast<std::uintptr_t>(pointer);
return std::any_of(ranges.begin(), ranges.end(), [&](const auto& range) { return range.begin <= address && address < range.end; });
};
Require(!listed(plain), "a guest heap block without GPU access is in an address-based build");
Require(listed(gpu), "a guest heap block with GPU access is missing from an address-based build");
leased.WriteBack();
}
GuestHeap::GuestHeapFree_nid_postfix(plain);
GuestHeap::GuestHeapFree_nid_postfix(gpu);
}
{
// The cached address space: a second build in an unchanged registry takes the first one's
// space, and a guest free of a range pinned only by the cache goes through the pin waiter's
// drop (no GPU work to wait for) and empties the registry of it.
void* block = GuestHeap::GuestHeapAllocate_nid_postfix(64);
void* block = GuestHeap::GuestHeapAlignForGpu_nid_postfix(16, 64);
const auto blockAddress = reinterpret_cast<std::uintptr_t>(block);
std::memset(block, 0x5a, 64);
const auto registered = [&] {

View File

@ -450,8 +450,8 @@ std::array<std::uint32_t, 7> memoryCopy(const void* destination, const void* sou
void testCopyIntoHostMemory() {
constexpr std::size_t words = 32768;
constexpr auto bytes = static_cast<std::uint32_t>(words * 4);
auto* produced = static_cast<std::uint32_t*>(GuestHeap::GuestHeapAlign_nid_postfix(65536, bytes));
auto* source = static_cast<std::uint32_t*>(GuestHeap::GuestHeapAlign_nid_postfix(65536, bytes));
auto* produced = static_cast<std::uint32_t*>(GuestHeap::GuestHeapAlignForGpu_nid_postfix(65536, bytes));
auto* source = static_cast<std::uint32_t*>(GuestHeap::GuestHeapAlignForGpu_nid_postfix(65536, bytes));
std::vector<std::uint32_t> destination(words);
for (const bool label : {false, true}) {
for (std::size_t i = 0; i < words; ++i) {

View File

@ -29,13 +29,17 @@ void APS5_VABI GuestDeallocate_nid_no_patch(void*, void* memory) {
GuestHeap::GuestHeapFree_nid_postfix(memory);
}
void* APS5_VABI GuestAllocateTexture_nid_no_patch(void*, std::uint32_t alignment, std::uint32_t size) {
return GuestHeap::GuestHeapAlignForGpu_nid_postfix(std::max<std::uint32_t>(alignment, 16), size);
}
AvPlayerMemAllocator WithGuestHeapFallback(AvPlayerMemAllocator memory) {
if (!memory.allocate || !memory.deallocate) {
memory.allocate = GuestAllocate_nid_no_patch;
memory.deallocate = GuestDeallocate_nid_no_patch;
}
if (!memory.allocate_texture || !memory.deallocate_texture) {
memory.allocate_texture = GuestAllocate_nid_no_patch;
memory.allocate_texture = GuestAllocateTexture_nid_no_patch;
memory.deallocate_texture = GuestDeallocate_nid_no_patch;
}
return memory;

View File

@ -12,6 +12,7 @@ void GuestHeapFree_nid_postfix(void* pointer);
void* GuestHeapReallocate_nid_postfix(void* pointer, std::size_t bytes);
void* GuestHeapAlign_nid_postfix(std::size_t alignment, std::size_t bytes);
void* GuestHeapRealign_nid_postfix(void* pointer, std::size_t bytes, std::size_t alignment);
void* GuestHeapAlignForGpu_nid_postfix(std::size_t alignment, std::size_t bytes);
}

View File

@ -193,7 +193,7 @@ void rawFree(void* raw, std::size_t blockBytes) {
else std::free(raw);
}
void* allocate(GuestAllocations::Mutation& mutation, std::size_t alignment, std::size_t bytes) {
void* allocate(GuestAllocations::Mutation& mutation, std::size_t alignment, std::size_t bytes, bool gpu) {
if (alignment == 0 || (alignment & (alignment - 1)) != 0) throw std::invalid_argument("invalid guest heap alignment");
alignment = std::max(alignment, MinimumAlignment);
const std::size_t padding = alignment > RawAlignment ? alignment : 0;
@ -205,7 +205,7 @@ void* allocate(GuestAllocations::Mutation& mutation, std::size_t alignment, std:
reinterpret_cast<void**>(pointer)[-2] = raw;
reinterpret_cast<std::size_t*>(pointer)[-1] = blockBytes;
try {
mutation.Add(pointer, bytes, true, true, true);
mutation.Add(pointer, bytes, true, true, gpu);
} catch (...) {
rawFree(raw, blockBytes);
throw;
@ -222,7 +222,7 @@ void free(GuestAllocations::Mutation& mutation, void* pointer) {
void* GuestHeapAllocate_nid_postfix(std::size_t bytes) {
GuestAllocations::Mutation mutation;
return allocate(mutation, MinimumAlignment, bytes);
return allocate(mutation, MinimumAlignment, bytes, false);
}
void GuestHeapFree_nid_postfix(void* pointer) {
@ -242,7 +242,7 @@ void* GuestHeapReallocate_nid_postfix(void* pointer, std::size_t bytes) {
free(mutation, pointer);
return nullptr;
}
void* result = allocate(mutation, MinimumAlignment, bytes);
void* result = allocate(mutation, MinimumAlignment, bytes, range.gpu);
std::memcpy(result, pointer, std::min(bytes, range.bytes));
free(mutation, pointer);
return result;
@ -257,7 +257,7 @@ void* GuestHeapRealign_nid_postfix(void* pointer, std::size_t bytes, std::size_t
free(mutation, pointer);
return nullptr;
}
void* result = allocate(mutation, alignment, bytes);
void* result = allocate(mutation, alignment, bytes, range.gpu);
std::memcpy(result, pointer, std::min(bytes, range.bytes));
free(mutation, pointer);
return result;
@ -265,7 +265,12 @@ void* GuestHeapRealign_nid_postfix(void* pointer, std::size_t bytes, std::size_t
void* GuestHeapAlign_nid_postfix(std::size_t alignment, std::size_t bytes) {
GuestAllocations::Mutation mutation;
return allocate(mutation, alignment, bytes);
return allocate(mutation, alignment, bytes, false);
}
void* GuestHeapAlignForGpu_nid_postfix(std::size_t alignment, std::size_t bytes) {
GuestAllocations::Mutation mutation;
return allocate(mutation, alignment, bytes, true);
}
}