From 4820e60412ebd4799ca63141e0bd391af70ff597 Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Thu, 13 Jan 2022 18:22:47 +0000 Subject: [PATCH] Rework BCeNabler memory permissions for ---X --- src/bcenabler.cpp | 47 ++++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/src/bcenabler.cpp b/src/bcenabler.cpp index 8f6ed46..b10cd8f 100644 --- a/src/bcenabler.cpp +++ b/src/bcenabler.cpp @@ -37,6 +37,10 @@ static void *find_free_page(uintptr_t address) { return nullptr; } +static void *align_ptr(void *ptr) { + return reinterpret_cast(reinterpret_cast(ptr) & ~(PAGE_SIZE - 1)); +} + bool adrenotools_patch_bcn(void *vkGetPhysicalDeviceFormatPropertiesFn) { union Branch { struct { @@ -48,6 +52,21 @@ bool adrenotools_patch_bcn(void *vkGetPhysicalDeviceFormatPropertiesFn) { }; static_assert(sizeof(Branch) == 4, "Branch size is invalid"); + // Find the nearest unmapped page where we can place patch code + void *patchPage{find_free_page(reinterpret_cast(vkGetPhysicalDeviceFormatPropertiesFn))}; + if (!patchPage) + return false; + + // Map patch region + void *ptr{mmap(patchPage, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0)}; + if (ptr != patchPage) + return false; + + // Allow reading from the blob's .text section since some devices enable ---X + // Protect two pages just in case we happen to land on a page boundary + if (mprotect(align_ptr(vkGetPhysicalDeviceFormatPropertiesFn), PAGE_SIZE * 2, PROT_WRITE | PROT_READ | PROT_EXEC)) + return false; + // First branch in this function is targeted at the function we want to patch Branch *blInst{reinterpret_cast(vkGetPhysicalDeviceFormatPropertiesFn)}; @@ -60,6 +79,11 @@ bool adrenotools_patch_bcn(void *vkGetPhysicalDeviceFormatPropertiesFn) { // Internal QGL format conversion function that we need to patch uint32_t *convFormatFn{reinterpret_cast(blInst) + blInst->offset}; + // See mprotect call above + // This time we also set PROT_WRITE so we can write our patch to the page + if (mprotect(align_ptr(convFormatFn), PAGE_SIZE * 2, PROT_WRITE | PROT_READ | PROT_EXEC)) + return false; + // This would normally set the default result to 0 (error) in the format not found case constexpr uint32_t ClearResultSignature{0x2a1f03e0}; @@ -68,15 +92,6 @@ bool adrenotools_patch_bcn(void *vkGetPhysicalDeviceFormatPropertiesFn) { while (*clearResultPtr != ClearResultSignature) clearResultPtr++; - // Find the nearest unmapped page where we can place patch code - void *patchPage{find_free_page(reinterpret_cast(clearResultPtr))}; - if (!patchPage) - return false; - - void *ptr{mmap(patchPage, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0)}; - if (ptr != patchPage) - return false; - // Ensure we don't write out of bounds if (PatchRawData_size > PAGE_SIZE) return false; @@ -88,14 +103,14 @@ bool adrenotools_patch_bcn(void *vkGetPhysicalDeviceFormatPropertiesFn) { constexpr uint32_t PatchReturnFixupMagic{0xffffffff}; constexpr uint8_t BranchSignature{0x5}; - uint32_t *fixupTargetPtr = clearResultPtr + 1; - uint32_t *fixupPtr = reinterpret_cast(patchPage); + uint32_t *fixupTargetPtr{clearResultPtr + 1}; + auto *fixupPtr{reinterpret_cast(patchPage)}; for (long unsigned int i{}; i < (PatchRawData_size / sizeof(uint32_t)); i++, fixupPtr++) { if (*fixupPtr == PatchReturnFixupMagic) { Branch branchToDriver{ { - .offset = static_cast((reinterpret_cast(fixupTargetPtr) - reinterpret_cast(fixupPtr)) / sizeof(int32_t)), - .sig = BranchSignature, + .offset = static_cast((reinterpret_cast(fixupTargetPtr) - reinterpret_cast(fixupPtr)) / sizeof(int32_t)), + .sig = BranchSignature, } }; @@ -110,12 +125,6 @@ bool adrenotools_patch_bcn(void *vkGetPhysicalDeviceFormatPropertiesFn) { } }; - void *driverPatchPage{reinterpret_cast(reinterpret_cast(clearResultPtr) & ~(PAGE_SIZE - 1))}; - - // For some reason mprotect just breaks entirely after we patch the QGL instruction so just set perms to RWX - if (mprotect(driverPatchPage, PAGE_SIZE, PROT_WRITE | PROT_READ | PROT_EXEC)) - return false; - *clearResultPtr = branchToPatch.raw; asm volatile("ISB");