mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-25 12:12:50 +01:00
Patches: Fix potential RPCS3 crashes due to invalid patches
This commit is contained in:
parent
c0280b43f2
commit
083b4f0d3b
@ -77,6 +77,7 @@ void fmt_class_string<patch_type>::format(std::string& out, u64 arg)
|
||||
case patch_type::lef32: return "lef32";
|
||||
case patch_type::lef64: return "lef64";
|
||||
case patch_type::utf8: return "utf8";
|
||||
case patch_type::c_utf8: return "cutf8";
|
||||
case patch_type::move_file: return "move_file";
|
||||
case patch_type::hide_file: return "hide_file";
|
||||
}
|
||||
@ -805,7 +806,7 @@ void unmap_vm_area(std::shared_ptr<vm::block_t>& ptr)
|
||||
}
|
||||
|
||||
// Returns old 'applied' size
|
||||
static usz apply_modification(std::basic_string<u32>& applied, patch_engine::patch_info& patch, std::function<u8*(u32)> mem_translate, u32 filesz, u32 min_addr)
|
||||
static usz apply_modification(std::basic_string<u32>& applied, patch_engine::patch_info& patch, std::function<u8*(u32, u32)> mem_translate, u32 filesz, u32 min_addr)
|
||||
{
|
||||
const usz old_applied_size = applied.size();
|
||||
|
||||
@ -847,7 +848,7 @@ static usz apply_modification(std::basic_string<u32>& applied, patch_engine::pat
|
||||
if (p.type != patch_type::alloc) continue;
|
||||
|
||||
// Do not allow null address or if resultant ptr is not a VM ptr
|
||||
if (const u32 alloc_at = vm::try_get_addr(mem_translate(p.offset & -4096)).first; alloc_at >> 16)
|
||||
if (const u32 alloc_at = (p.offset & -4096); alloc_at >> 16)
|
||||
{
|
||||
const u32 alloc_size = utils::align(static_cast<u32>(p.value.long_value) + alloc_at % 4096, 4096);
|
||||
|
||||
@ -926,7 +927,74 @@ static usz apply_modification(std::basic_string<u32>& applied, patch_engine::pat
|
||||
relocate_instructions_at = 0;
|
||||
}
|
||||
|
||||
if (!relocate_instructions_at && (offset < min_addr || offset - min_addr >= filesz))
|
||||
u32 memory_size = 0;
|
||||
|
||||
switch (p.type)
|
||||
{
|
||||
case patch_type::invalid:
|
||||
case patch_type::load:
|
||||
{
|
||||
// Invalid in this context
|
||||
break;
|
||||
}
|
||||
case patch_type::alloc:
|
||||
{
|
||||
// Applied before
|
||||
memory_size = 0;
|
||||
continue;
|
||||
}
|
||||
case patch_type::byte:
|
||||
{
|
||||
memory_size = sizeof(u8);
|
||||
break;
|
||||
}
|
||||
case patch_type::le16:
|
||||
case patch_type::be16:
|
||||
{
|
||||
memory_size = sizeof(u16);
|
||||
break;
|
||||
}
|
||||
case patch_type::code_alloc:
|
||||
case patch_type::jump:
|
||||
case patch_type::jump_link:
|
||||
case patch_type::jump_func:
|
||||
case patch_type::le32:
|
||||
case patch_type::lef32:
|
||||
case patch_type::bd32:
|
||||
case patch_type::be32:
|
||||
case patch_type::bef32:
|
||||
{
|
||||
memory_size = sizeof(u32);
|
||||
break;
|
||||
}
|
||||
case patch_type::lef64:
|
||||
case patch_type::le64:
|
||||
case patch_type::bd64:
|
||||
case patch_type::be64:
|
||||
case patch_type::bef64:
|
||||
{
|
||||
memory_size = sizeof(u64);
|
||||
break;
|
||||
}
|
||||
case patch_type::utf8:
|
||||
{
|
||||
memory_size = p.original_value.size();
|
||||
break;
|
||||
}
|
||||
case patch_type::c_utf8:
|
||||
{
|
||||
memory_size = utils::add_saturate<u32>(p.original_value.size(), 1);
|
||||
break;
|
||||
}
|
||||
case patch_type::move_file:
|
||||
case patch_type::hide_file:
|
||||
{
|
||||
memory_size = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (memory_size != 0 && !relocate_instructions_at && (filesz < memory_size || offset < min_addr || offset - min_addr > filesz - memory_size))
|
||||
{
|
||||
// This patch is out of range for this segment
|
||||
continue;
|
||||
@ -934,9 +1002,9 @@ static usz apply_modification(std::basic_string<u32>& applied, patch_engine::pat
|
||||
|
||||
offset -= min_addr;
|
||||
|
||||
auto ptr = mem_translate(offset);
|
||||
auto ptr = mem_translate(offset, memory_size);
|
||||
|
||||
if (!ptr)
|
||||
if (!ptr && memory_size != 0)
|
||||
{
|
||||
// Memory translation failed
|
||||
continue;
|
||||
@ -966,7 +1034,7 @@ static usz apply_modification(std::basic_string<u32>& applied, patch_engine::pat
|
||||
}
|
||||
case patch_type::code_alloc:
|
||||
{
|
||||
const u32 out_branch = vm::try_get_addr(mem_translate(offset & -4)).first;
|
||||
const u32 out_branch = vm::try_get_addr(mem_translate(offset & -4, 4)).first;
|
||||
|
||||
// Allow only if points to a PPU executable instruction
|
||||
if (out_branch < 0x10000 || out_branch >= 0x4000'0000 || !vm::check_addr<4>(out_branch, vm::page_executable))
|
||||
@ -1050,7 +1118,7 @@ static usz apply_modification(std::basic_string<u32>& applied, patch_engine::pat
|
||||
case patch_type::jump:
|
||||
case patch_type::jump_link:
|
||||
{
|
||||
const u32 out_branch = vm::try_get_addr(mem_translate(offset & -4)).first;
|
||||
const u32 out_branch = vm::try_get_addr(mem_translate(offset & -4, 4)).first;
|
||||
const u32 dest = static_cast<u32>(p.value.long_value);
|
||||
|
||||
// Allow only if points to a PPU executable instruction
|
||||
@ -1066,7 +1134,7 @@ static usz apply_modification(std::basic_string<u32>& applied, patch_engine::pat
|
||||
{
|
||||
const std::string& str = p.original_value;
|
||||
|
||||
const u32 out_branch = vm::try_get_addr(mem_translate(offset & -4)).first;
|
||||
const u32 out_branch = vm::try_get_addr(mem_translate(offset & -4, 4)).first;
|
||||
const usz sep_pos = str.find_first_of(':');
|
||||
|
||||
// Must contain only a single ':' or none
|
||||
@ -1082,7 +1150,7 @@ static usz apply_modification(std::basic_string<u32>& applied, patch_engine::pat
|
||||
|
||||
if (func_name.starts_with("0x"sv))
|
||||
{
|
||||
// Raw hexadeciaml-formatted FNID (real function name cannot contain a digit at the start, derived from C/CPP which were used in PS3 development)
|
||||
// Raw hexadecimal-formatted FNID (real function name cannot contain a digit at the start, derived from C/CPP which were used in PS3 development)
|
||||
const auto result = std::from_chars(func_name.data() + 2, func_name.data() + func_name.size() - 2, id, 16);
|
||||
|
||||
if (result.ec != std::errc() || str.data() + sep_pos != result.ptr)
|
||||
@ -1203,6 +1271,11 @@ static usz apply_modification(std::basic_string<u32>& applied, patch_engine::pat
|
||||
std::memcpy(ptr, p.original_value.data(), p.original_value.size());
|
||||
break;
|
||||
}
|
||||
case patch_type::c_utf8:
|
||||
{
|
||||
std::memcpy(ptr, p.original_value.data(), p.original_value.size() + 1);
|
||||
break;
|
||||
}
|
||||
case patch_type::move_file:
|
||||
case patch_type::hide_file:
|
||||
{
|
||||
@ -1257,7 +1330,7 @@ static usz apply_modification(std::basic_string<u32>& applied, patch_engine::pat
|
||||
return old_applied_size;
|
||||
}
|
||||
|
||||
std::basic_string<u32> patch_engine::apply(const std::string& name, std::function<u8*(u32)> mem_translate, u32 filesz, u32 min_addr)
|
||||
std::basic_string<u32> patch_engine::apply(const std::string& name, std::function<u8*(u32, u32)> mem_translate, u32 filesz, u32 min_addr)
|
||||
{
|
||||
if (!m_map.contains(name))
|
||||
{
|
||||
@ -1269,7 +1342,7 @@ std::basic_string<u32> patch_engine::apply(const std::string& name, std::functio
|
||||
const auto& serial = Emu.GetTitleID();
|
||||
const auto& app_version = Emu.GetAppVersion();
|
||||
|
||||
// Different containers in order to seperate the patches
|
||||
// Different containers in order to separate the patches
|
||||
std::vector<std::shared_ptr<patch_info>> patches_for_this_serial_and_this_version;
|
||||
std::vector<std::shared_ptr<patch_info>> patches_for_this_serial_and_all_versions;
|
||||
std::vector<std::shared_ptr<patch_info>> patches_for_all_serials_and_this_version;
|
||||
|
@ -53,13 +53,14 @@ enum class patch_type
|
||||
bef32,
|
||||
bef64,
|
||||
utf8, // Text of string (not null-terminated automatically)
|
||||
c_utf8, // Text of string (null-terminated automatically)
|
||||
move_file, // Move file
|
||||
hide_file, // Hide file
|
||||
};
|
||||
|
||||
static constexpr bool patch_type_uses_hex_offset(patch_type type)
|
||||
{
|
||||
return type >= patch_type::alloc && type <= patch_type::utf8;
|
||||
return type >= patch_type::alloc && type <= patch_type::c_utf8;
|
||||
}
|
||||
|
||||
enum class patch_configurable_type
|
||||
@ -213,7 +214,7 @@ public:
|
||||
void append_title_patches(const std::string& title_id);
|
||||
|
||||
// Apply patch (returns the number of entries applied)
|
||||
std::basic_string<u32> apply(const std::string& name, std::function<u8*(u32)> mem_translate, u32 filesz = -1, u32 min_addr = 0);
|
||||
std::basic_string<u32> apply(const std::string& name, std::function<u8*(u32, u32)> mem_translate, u32 filesz = -1, u32 min_addr = 0);
|
||||
|
||||
// Deallocate memory used by patches
|
||||
void unload(const std::string& name);
|
||||
|
@ -112,7 +112,7 @@ struct ppu_module
|
||||
void validate(u32 reloc);
|
||||
|
||||
template <typename T>
|
||||
to_be_t<T>* get_ptr(u32 addr) const
|
||||
to_be_t<T>* get_ptr(u32 addr, u32 size_bytes) const
|
||||
{
|
||||
auto it = addr_to_seg_index.upper_bound(addr);
|
||||
|
||||
@ -127,9 +127,7 @@ struct ppu_module
|
||||
const u32 seg_size = seg.size;
|
||||
const u32 seg_addr = seg.addr;
|
||||
|
||||
constexpr usz size_element = std::is_void_v<T> ? 0 : sizeof(std::conditional_t<std::is_void_v<T>, char, T>);
|
||||
|
||||
if (seg_size >= std::max<usz>(size_element, 1) && addr <= seg_addr + seg_size - size_element)
|
||||
if (seg_size >= std::max<usz>(size_bytes, 1) && addr <= seg_addr + seg_size - size_bytes)
|
||||
{
|
||||
return reinterpret_cast<to_be_t<T>*>(static_cast<u8*>(seg.ptr) + (addr - seg_addr));
|
||||
}
|
||||
@ -137,10 +135,18 @@ struct ppu_module
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename T, typename U> requires requires (const U& obj) { +obj.addr() * 0; }
|
||||
template <typename T>
|
||||
to_be_t<T>* get_ptr(u32 addr) const
|
||||
{
|
||||
constexpr usz size_element = std::is_void_v<T> ? 0 : sizeof(std::conditional_t<std::is_void_v<T>, char, T>);
|
||||
return get_ptr<T>(addr, u32{size_element});
|
||||
}
|
||||
|
||||
template <typename T, typename U> requires requires (const U& obj) { +obj.size() * 0; }
|
||||
to_be_t<T>* get_ptr(U&& addr) const
|
||||
{
|
||||
return get_ptr<T>(addr.addr());
|
||||
constexpr usz size_element = std::is_void_v<T> ? 0 : sizeof(std::conditional_t<std::is_void_v<T>, char, T>);
|
||||
return get_ptr<T>(addr.addr(), u32{size_element});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1142,12 +1142,12 @@ static void ppu_check_patch_spu_images(const ppu_module& mod, const ppu_segment&
|
||||
for (const auto& prog : obj.progs)
|
||||
{
|
||||
// Apply the patch
|
||||
applied += g_fxo->get<patch_engine>().apply(hash, [&](u32 addr) { return addr + elf_header + prog.p_offset; }, prog.p_filesz, prog.p_vaddr);
|
||||
applied += g_fxo->get<patch_engine>().apply(hash, [&](u32 addr, u32 /*size*/) { return addr + elf_header + prog.p_offset; }, prog.p_filesz, prog.p_vaddr);
|
||||
|
||||
if (!Emu.GetTitleID().empty())
|
||||
{
|
||||
// Alternative patch
|
||||
applied += g_fxo->get<patch_engine>().apply(Emu.GetTitleID() + '-' + hash, [&](u32 addr) { return addr + elf_header + prog.p_offset; }, prog.p_filesz, prog.p_vaddr);
|
||||
applied += g_fxo->get<patch_engine>().apply(Emu.GetTitleID() + '-' + hash, [&](u32 addr, u32 /*size*/) { return addr + elf_header + prog.p_offset; }, prog.p_filesz, prog.p_vaddr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1617,12 +1617,12 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, bool virtual_lo
|
||||
const std::string hash_seg = fmt::format("%s-%u", hash, i);
|
||||
|
||||
// Apply the patch
|
||||
auto _applied = g_fxo->get<patch_engine>().apply(hash_seg, [&](u32 addr) { return prx->get_ptr<u8>(addr + seg.addr); }, seg.size);
|
||||
auto _applied = g_fxo->get<patch_engine>().apply(hash_seg, [&](u32 addr, u32 size) { return prx->get_ptr<u8>(addr + seg.addr, size); }, seg.size);
|
||||
|
||||
if (!Emu.GetTitleID().empty())
|
||||
{
|
||||
// Alternative patch
|
||||
_applied += g_fxo->get<patch_engine>().apply(Emu.GetTitleID() + '-' + hash_seg, [&](u32 addr) { return prx->get_ptr<u8>(addr + seg.addr); }, seg.size);
|
||||
_applied += g_fxo->get<patch_engine>().apply(Emu.GetTitleID() + '-' + hash_seg, [&](u32 addr, u32 size) { return prx->get_ptr<u8>(addr + seg.addr, size); }, seg.size);
|
||||
}
|
||||
|
||||
// Rebase patch offsets
|
||||
@ -1933,12 +1933,12 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str
|
||||
Emu.SetExecutableHash(hash);
|
||||
|
||||
// Apply the patch
|
||||
auto applied = g_fxo->get<patch_engine>().apply(!ar ? hash : std::string{}, [&](u32 addr) { return _main.get_ptr<u8>(addr); });
|
||||
auto applied = g_fxo->get<patch_engine>().apply(!ar ? hash : std::string{}, [&](u32 addr, u32 size) { return _main.get_ptr<u8>(addr, size); });
|
||||
|
||||
if (!ar && !Emu.GetTitleID().empty())
|
||||
{
|
||||
// Alternative patch
|
||||
applied += g_fxo->get<patch_engine>().apply(Emu.GetTitleID() + '-' + hash, [&](u32 addr) { return _main.get_ptr<u8>(addr); });
|
||||
applied += g_fxo->get<patch_engine>().apply(Emu.GetTitleID() + '-' + hash, [&](u32 addr, u32 size) { return _main.get_ptr<u8>(addr, size); });
|
||||
}
|
||||
|
||||
if (applied.empty())
|
||||
@ -2571,12 +2571,12 @@ std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_ex
|
||||
}
|
||||
|
||||
// Apply the patch
|
||||
auto applied = g_fxo->get<patch_engine>().apply(hash, [ovlm](u32 addr) { return ovlm->get_ptr<u8>(addr); });
|
||||
auto applied = g_fxo->get<patch_engine>().apply(hash, [ovlm](u32 addr, u32 size) { return ovlm->get_ptr<u8>(addr, size); });
|
||||
|
||||
if (!Emu.GetTitleID().empty())
|
||||
{
|
||||
// Alternative patch
|
||||
applied += g_fxo->get<patch_engine>().apply(Emu.GetTitleID() + '-' + hash, [ovlm](u32 addr) { return ovlm->get_ptr<u8>(addr); });
|
||||
applied += g_fxo->get<patch_engine>().apply(Emu.GetTitleID() + '-' + hash, [ovlm](u32 addr, u32 size) { return ovlm->get_ptr<u8>(addr, size); });
|
||||
}
|
||||
|
||||
// Embedded SPU elf patching
|
||||
|
@ -184,13 +184,18 @@ void sys_spu_image::deploy(u8* loc, std::span<const sys_spu_segment> segs, bool
|
||||
hash[5 + i * 2] = pal[sha1_hash[i] & 15];
|
||||
}
|
||||
|
||||
auto mem_translate = [loc](u32 addr, u32 size)
|
||||
{
|
||||
return utils::add_saturate<u32>(addr, size) <= SPU_LS_SIZE ? loc + addr : nullptr;
|
||||
};
|
||||
|
||||
// Apply the patch
|
||||
auto applied = g_fxo->get<patch_engine>().apply(hash, [loc](u32 addr) { return loc + addr; });
|
||||
auto applied = g_fxo->get<patch_engine>().apply(hash, mem_translate);
|
||||
|
||||
if (!Emu.GetTitleID().empty())
|
||||
{
|
||||
// Alternative patch
|
||||
applied += g_fxo->get<patch_engine>().apply(Emu.GetTitleID() + '-' + hash, [loc](u32 addr) { return loc + addr; });
|
||||
applied += g_fxo->get<patch_engine>().apply(Emu.GetTitleID() + '-' + hash, mem_translate);
|
||||
}
|
||||
|
||||
(is_verbose ? spu_log.notice : sys_spu.trace)("Loaded SPU image: %s (<- %u)%s", hash, applied.size(), dump);
|
||||
|
Loading…
Reference in New Issue
Block a user