fix(agc): describe the pages after a gap in a Linux host page query
Without VirtualQuery, a range no cached page table covers is looked up line by line in /proc/self/maps. A cursor in a gap between mappings never matched a line, so the scan parsed the whole file and then reported the rest of the range unmapped, including mapped pages after the gap. The scan now stops at the first mapping above the cursor, reports the gap up to it as inaccessible and continues from there, as the VirtualQuery path does with a free region. PPSA21564 queries near-null addresses (0x18, 0x30, 0x58) about 1500 times a second during resource capture; each of those parsed the whole maps file (about 5700 lines here) instead of stopping at the first mapping. Tests: agc_driver_graphics_tests describes three host pages with the middle one unmapped as two ranges, a query that starts in the gap as the page after it, and a near-null address as inaccessible.
This commit is contained in:
parent
926906e257
commit
a18cf0fc32
|
|
@ -542,7 +542,14 @@ bool describePages(std::uintptr_t address, std::size_t bytes, Emit&& emit) {
|
|||
char separator = 0;
|
||||
std::string permissions;
|
||||
if (!(fields >> std::hex >> first >> separator >> last >> permissions) || separator != '-' || first >= last || permissions.size() < 2) return false;
|
||||
if (last <= cursor || first > cursor) continue;
|
||||
if (last <= cursor) continue;
|
||||
if (first > cursor) {
|
||||
const auto gapEnd = std::min(end, first);
|
||||
if (!emit(PageRun{cursor, gapEnd, false, false})) return true;
|
||||
cursor = gapEnd;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
const auto next = std::min(end, last);
|
||||
if (!emit(PageRun{cursor, next, permissions[0] == 'r', permissions[0] == 'r' && permissions[1] == 'w'})) return true;
|
||||
cursor = next;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ struct BdaTestAccess {
|
|||
|
||||
void RunBdaResourceTests(const AgcDriver::Graphics::Context& context, const BdaTestAccess& access);
|
||||
void RunGuestAllocationTests();
|
||||
void RunUnmappedGapTests();
|
||||
void RunColorTargetLayoutTests();
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1700,6 +1700,7 @@ int main() {
|
|||
});
|
||||
Require(mock.live == 0, "BDA resources leaked Vulkan objects");
|
||||
RunGuestAllocationTests();
|
||||
RunUnmappedGapTests();
|
||||
RunColorTargetLayoutTests();
|
||||
RunTextureFormatTests();
|
||||
RunTextureTilingTests();
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
#include "BdaTests.hpp"
|
||||
#include "prx/libc/include/GuestAllocations.hpp"
|
||||
#include "prx/libc/include/GuestHeap.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
|
||||
#include "prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp"
|
||||
#include <cstring>
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#if defined(__linux__)
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
|
|
@ -116,3 +123,22 @@ void RunGuestAllocationTests() {
|
|||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void RunUnmappedGapTests() {
|
||||
#if defined(__linux__)
|
||||
namespace GuestMemory = AgcDriver::GuestMemory;
|
||||
const auto page = static_cast<std::size_t>(sysconf(_SC_PAGESIZE));
|
||||
auto* block = static_cast<std::byte*>(mmap(nullptr, 3 * page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
Require(block != MAP_FAILED, "cannot map the gap test pages");
|
||||
Require(munmap(block + page, page) == 0, "cannot unmap the gap test's middle page");
|
||||
const auto base = reinterpret_cast<std::uint64_t>(block);
|
||||
const auto ranges = GuestMemory::CommittedRanges(base, 3 * page);
|
||||
const std::vector<std::pair<std::uint64_t, std::uint64_t>> expected{{base, base + page}, {base + 2 * page, base + 3 * page}};
|
||||
Require(ranges == expected, "the pages after an unmapped gap in host memory were not described");
|
||||
Require(GuestMemory::CommittedRanges(base + page, 2 * page) == std::vector<std::pair<std::uint64_t, std::uint64_t>>{{base + 2 * page, base + 3 * page}}, "the pages after a query that starts in a gap were not described");
|
||||
Require(!GuestMemory::Accessible(block, 3 * page) && GuestMemory::Accessible(block + 2 * page, page, true), "an unmapped gap in host memory is misreported");
|
||||
Require(!GuestMemory::Accessible(reinterpret_cast<const void*>(std::uintptr_t{0x18}), 4), "a near-null address counts as accessible");
|
||||
munmap(block, page);
|
||||
munmap(block + 2 * page, page);
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue