mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-24 21:06:55 +02:00
Resolve red-zone patcher jump tables outside the code segment
Jump tables usually sit in a read-only data segment, but the resolver only accepted tables inside the executable segment being patched. Every function with a switch was treated as having an unknown indirect branch, which disables neighbour relocation, so its short memory instructions stayed unprotected: 178406 of 520371 in PPSA10595's eboot.bin, including the zstd decoder stores whose handled GPU-tracking faults wiped its red zone. Accept tables anywhere in the loaded module once readable. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,7 @@
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h> // IWYU pragma: keep
|
||||
#include <xbyak/xbyak.h>
|
||||
#include <xbyak/xbyak_util.h>
|
||||
#endif
|
||||
@@ -306,10 +307,22 @@ bool WritesRegister(const DecodedCodeInstruction& decoded, ZydisRegister reg) {
|
||||
});
|
||||
}
|
||||
|
||||
bool IsReadableRange(uintptr_t address, size_t size) {
|
||||
for (uintptr_t current = address; current < address + size;) {
|
||||
MEMORY_BASIC_INFORMATION info {};
|
||||
if (VirtualQuery(reinterpret_cast<const void*>(current), &info, sizeof(info)) == 0 ||
|
||||
info.State != MEM_COMMIT || (info.Protect & (PAGE_NOACCESS | PAGE_GUARD)) != 0) {
|
||||
return false;
|
||||
}
|
||||
current = reinterpret_cast<uintptr_t>(info.BaseAddress) + info.RegionSize;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<std::vector<uintptr_t>>
|
||||
ResolveBoundedJumpTable(const DecodedFunction& function, uintptr_t branch_address,
|
||||
uintptr_t function_start, uintptr_t function_end, uintptr_t segment_start,
|
||||
uintptr_t segment_end) {
|
||||
uintptr_t function_start, uintptr_t function_end, uintptr_t data_start,
|
||||
uintptr_t data_end) {
|
||||
const auto branch = function.instructions.find(branch_address);
|
||||
if (branch == function.instructions.end() ||
|
||||
branch->second.instruction.mnemonic != ZYDIS_MNEMONIC_JMP ||
|
||||
@@ -463,8 +476,9 @@ ResolveBoundedJumpTable(const DecodedFunction& function, uintptr_t branch_addres
|
||||
|
||||
std::optional<std::vector<uintptr_t>> resolved_targets;
|
||||
for (const uintptr_t table_address: table_candidates) {
|
||||
if (table_address < segment_start || table_address > segment_end ||
|
||||
*table_size > (segment_end - table_address) / sizeof(s32)) {
|
||||
if (table_address < data_start || table_address > data_end ||
|
||||
*table_size > (data_end - table_address) / sizeof(s32) ||
|
||||
!IsReadableRange(table_address, *table_size * sizeof(s32))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -498,7 +512,7 @@ ResolveBoundedJumpTable(const DecodedFunction& function, uintptr_t branch_addres
|
||||
}
|
||||
|
||||
DecodedFunction DecodeFunction(uintptr_t function_start, uintptr_t function_end,
|
||||
uintptr_t segment_start, uintptr_t segment_end) {
|
||||
uintptr_t data_start, uintptr_t data_end) {
|
||||
DecodedFunction function;
|
||||
std::vector<uintptr_t> blocks {function_start};
|
||||
std::unordered_set<uintptr_t> visited;
|
||||
@@ -549,7 +563,7 @@ DecodedFunction DecodeFunction(uintptr_t function_start, uintptr_t function_end,
|
||||
continue;
|
||||
}
|
||||
const auto targets = ResolveBoundedJumpTable(function, branch_address, function_start,
|
||||
function_end, segment_start, segment_end);
|
||||
function_end, data_start, data_end);
|
||||
if (!targets) {
|
||||
continue;
|
||||
}
|
||||
@@ -1461,7 +1475,10 @@ RedZonePatchResult PatchGuestInstructions(u64 segment_addr, u64 segment_size,
|
||||
}
|
||||
|
||||
++result.function_count;
|
||||
auto function = DecodeFunction(function_start, function_end, segment_addr, segment_end);
|
||||
// Jump tables usually live in a read-only data segment, not beside the code.
|
||||
auto function =
|
||||
DecodeFunction(function_start, function_end, reinterpret_cast<uintptr_t>(module->start),
|
||||
reinterpret_cast<uintptr_t>(module->end));
|
||||
AnalyzeFramePointerRedZone(function, function_start);
|
||||
AnalyzeRedZoneLiveness(function);
|
||||
result.instruction_count += function.instructions.size();
|
||||
|
||||
Reference in New Issue
Block a user