diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 69914af39d..c847824eb3 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -390,13 +390,13 @@ extern bool ppu_patch(u32 addr, u32 value) vm::reader_lock rlock; - if (!vm::check_addr(addr, sizeof(value))) + if (!vm::check_addr(addr)) { ppu_log.fatal("Patch failed at 0x%x: invalid memory address.", addr); return false; } - const bool is_exec = vm::check_addr(addr, sizeof(value), vm::page_executable); + const bool is_exec = vm::check_addr(addr, vm::page_executable); if (is_exec && g_cfg.core.ppu_decoder == ppu_decoder_type::llvm && !Emu.IsReady()) { @@ -444,18 +444,18 @@ std::string ppu_thread::dump_regs() const fmt::append(ret, "r%d%s: 0x%-8llx", i, i <= 9 ? " " : "", reg); - const u32 max_str_len = 32; - const u32 hex_count = 8; + constexpr u32 max_str_len = 32; + constexpr u32 hex_count = 8; - if (reg <= UINT32_MAX && vm::check_addr(static_cast(reg), max_str_len)) + if (reg <= UINT32_MAX && vm::check_addr(static_cast(reg))) { bool is_function = false; u32 toc = 0; if (const u32 reg_ptr = *vm::get_super_ptr(static_cast(reg)); - vm::check_addr(reg_ptr, max_str_len)) + vm::check_addr(reg_ptr)) { - if ((reg | reg_ptr) % 4 == 0 && vm::check_addr(reg_ptr, 4, vm::page_executable)) + if ((reg | reg_ptr) % 4 == 0 && vm::check_addr(reg_ptr, vm::page_executable)) { toc = *vm::get_super_ptr(static_cast(reg + 4)); @@ -466,7 +466,7 @@ std::string ppu_thread::dump_regs() const } } } - else if (reg % 4 == 0 && vm::check_addr(reg, 4, vm::page_executable)) + else if (reg % 4 == 0 && vm::check_addr(reg, vm::page_executable)) { is_function = true; } @@ -577,7 +577,7 @@ std::vector> ppu_thread::dump_callstack_list() const const u32 stack_ptr = static_cast(r1); - if (!vm::check_addr(stack_ptr, 1, vm::page_writable)) + if (!vm::check_addr(stack_ptr, vm::page_writable)) { // Normally impossible unless the code does not follow ABI rules return {}; @@ -586,12 +586,12 @@ std::vector> ppu_thread::dump_callstack_list() const u32 stack_min = stack_ptr & ~0xfff; u32 stack_max = stack_min + 4096; - while (stack_min && vm::check_addr(stack_min - 4096, 4096, vm::page_writable)) + while (stack_min && vm::check_addr(stack_min - 4096, vm::page_writable)) { stack_min -= 4096; } - while (stack_max + 4096 && vm::check_addr(stack_max, 4096, vm::page_writable)) + while (stack_max + 4096 && vm::check_addr(stack_max, vm::page_writable)) { stack_max += 4096; } @@ -610,7 +610,7 @@ std::vector> ppu_thread::dump_callstack_list() const auto is_invalid = [](u64 addr) { - if (addr > UINT32_MAX || addr % 4 || !vm::check_addr(static_cast(addr), 1, vm::page_executable)) + if (addr > UINT32_MAX || addr % 4 || !vm::check_addr(static_cast(addr), vm::page_executable)) { return true; } @@ -1140,7 +1140,7 @@ void ppu_trap(ppu_thread& ppu, u64 addr) u32 add = static_cast(g_cfg.core.stub_ppu_traps) * 4; // If stubbing is enabled, check current instruction and the following - if (!add || !vm::check_addr(ppu.cia, 4, vm::page_executable) || !vm::check_addr(ppu.cia + add, 4, vm::page_executable)) + if (!add || !vm::check_addr(ppu.cia, vm::page_executable) || !vm::check_addr(ppu.cia + add, vm::page_executable)) { fmt::throw_exception("PPU Trap!" HERE); } @@ -1686,7 +1686,7 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) if (raddr / 128 != addr / 128 || !ppu.use_full_rdata) { // Even when the reservation address does not match the target address must be valid - if (!vm::check_addr(addr, 1, vm::page_writable)) + if (!vm::check_addr(addr, vm::page_writable)) { // Access violate data += 0; diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 0906d9147c..153af38325 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -2699,7 +2699,7 @@ bool spu_thread::do_putllc(const spu_mfc_cmd& args) } } - if (!vm::check_addr(addr, 1, vm::page_writable)) + if (!vm::check_addr(addr, vm::page_writable)) { vm::_ref>(addr) += 0; // Access violate } @@ -3296,7 +3296,7 @@ bool spu_thread::reservation_check(u32 addr, const decltype(rdata)& data) if ((lock_val >> vm::range_pos) == (vm::range_locked >> vm::range_pos)) { // All page flags are untouched and can be read safely - if (!vm::check_addr(addr, 128)) + if (!vm::check_addr(addr)) { // Assume our memory is being (de)allocated range_lock->release(0); @@ -3327,7 +3327,7 @@ bool spu_thread::reservation_check(u32 addr, const decltype(rdata)& data) if (lock_addr + lock_size <= addr || lock_addr >= addr + 128) { // We are outside locked range, so page flags are unaffected - if (!vm::check_addr(addr, 128)) + if (!vm::check_addr(addr)) { range_lock->release(0); break; diff --git a/rpcs3/Emu/Cell/lv2/sys_dbg.cpp b/rpcs3/Emu/Cell/lv2/sys_dbg.cpp index 6cef5f20c1..8375a06c9f 100644 --- a/rpcs3/Emu/Cell/lv2/sys_dbg.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_dbg.cpp @@ -16,18 +16,13 @@ error_code sys_dbg_read_process_memory(s32 pid, u32 address, u32 size, vm::ptrattribute = 0x40000ull; // SYS_MEMORY_PROT_READ_WRITE (TODO) attr->access_right = addr >> 28 == 0xdu ? SYS_MEMORY_ACCESS_RIGHT_PPU_THR : SYS_MEMORY_ACCESS_RIGHT_ANY;// (TODO) - if (vm::check_addr(addr, 1, vm::page_1m_size)) + if (vm::check_addr(addr, vm::page_1m_size)) { attr->page_size = 0x100000; } - else if (vm::check_addr(addr, 1, vm::page_64k_size)) + else if (vm::check_addr(addr, vm::page_64k_size)) { attr->page_size = 0x10000; } diff --git a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp index 19d3c7a473..5f0280cc22 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp @@ -336,7 +336,7 @@ error_code sys_rsx_context_iomap(cpu_thread& cpu, u32 context_id, u32 io, u32 ea for (u32 addr = ea, end = ea + size; addr < end; addr += 0x100000) { - if (!vm::check_addr(addr, 1, vm::page_readable | (addr < 0x20000000 ? 0 : vm::page_1m_size))) + if (!vm::check_addr(addr, vm::page_readable | (addr < 0x20000000 ? 0 : vm::page_1m_size))) { return CELL_EINVAL; } diff --git a/rpcs3/Emu/Cell/lv2/sys_tty.cpp b/rpcs3/Emu/Cell/lv2/sys_tty.cpp index 08844a43a9..916852a74f 100644 --- a/rpcs3/Emu/Cell/lv2/sys_tty.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_tty.cpp @@ -92,7 +92,7 @@ error_code sys_tty_write(s32 ch, vm::cptr buf, u32 len, vm::ptr pwrit std::string msg; - if (static_cast(len) > 0 && vm::check_addr(buf.addr(), len)) + if (static_cast(len) > 0 && vm::check_addr(buf.addr(), vm::page_readable, len)) { msg.resize(len); diff --git a/rpcs3/Emu/GDB.cpp b/rpcs3/Emu/GDB.cpp index e5fad4552f..8e73ebdc35 100644 --- a/rpcs3/Emu/GDB.cpp +++ b/rpcs3/Emu/GDB.cpp @@ -641,7 +641,7 @@ bool gdb_thread::cmd_write_memory(gdb_cmd& cmd) u32 len = hex_to_u32(cmd.data.substr(s + 1, s2 - s - 1)); const char* data_ptr = (cmd.data.c_str()) + s2 + 1; for (u32 i = 0; i < len; ++i) { - if (vm::check_addr(addr + i, 1, vm::page_writable)) { + if (vm::check_addr(addr + i, vm::page_writable)) { u8 val; int res = sscanf_s(data_ptr, "%02hhX", &val); if (!res) { diff --git a/rpcs3/Emu/Memory/vm.cpp b/rpcs3/Emu/Memory/vm.cpp index 5de4dd1d80..6276e86c0d 100644 --- a/rpcs3/Emu/Memory/vm.cpp +++ b/rpcs3/Emu/Memory/vm.cpp @@ -77,13 +77,6 @@ namespace vm // Memory range lock slots (sparse atomics) atomic_t g_range_lock_set[64]{}; - // Page information - struct memory_page - { - // Memory flags - atomic_t flags; - }; - // Memory pages std::array g_pages{}; @@ -192,7 +185,7 @@ namespace vm { const u64 new_lock_val = g_range_lock.load(); - if (!new_lock_val || new_lock_val == lock_val) [[likely]] + if (vm::check_addr(begin, vm::page_readable, size) && (!new_lock_val || new_lock_val == lock_val)) [[likely]] { break; } @@ -960,10 +953,15 @@ namespace vm return size; } - bool check_addr(u32 addr, u32 size, u8 flags) + bool check_addr(u32 addr, u8 flags, u32 size) { + if (size == 0) + { + return true; + } + // Overflow checking - if (addr + size < addr && (addr + size) != 0) + if (0x10000'0000ull - addr < size) { return false; } @@ -971,12 +969,28 @@ namespace vm // Always check this flag flags |= page_allocated; - for (u32 i = addr / 4096, max = (addr + size - 1) / 4096; i <= max; i++) + for (u32 i = addr / 4096, max = (addr + size - 1) / 4096; i <= max;) { - if ((g_pages[i].flags & flags) != flags) [[unlikely]] + auto state = +g_pages[i].flags; + + if (~state & flags) [[unlikely]] { return false; } + + if (state & page_1m_size) + { + i = ::align(i + 1, 0x100000 / 4096); + continue; + } + + if (state & page_64k_size) + { + i = ::align(i + 1, 0x10000 / 4096); + continue; + } + + i++; } return true; @@ -1534,12 +1548,7 @@ namespace vm { vm::reader_lock lock; - if (size == 0) - { - return true; - } - - if (vm::check_addr(addr, size, is_write ? page_writable : page_readable)) + if (vm::check_addr(addr, is_write ? page_writable : page_readable, size)) { void* src = vm::g_sudo_addr + addr; void* dst = ptr; diff --git a/rpcs3/Emu/Memory/vm.h b/rpcs3/Emu/Memory/vm.h index 16a9ab09cd..efdf894e1a 100644 --- a/rpcs3/Emu/Memory/vm.h +++ b/rpcs3/Emu/Memory/vm.h @@ -52,11 +52,32 @@ namespace vm // Address type enum addr_t : u32 {}; + // Page information + struct memory_page + { + // Memory flags + atomic_t flags; + }; + // Change memory protection of specified memory region bool page_protect(u32 addr, u32 size, u8 flags_test = 0, u8 flags_set = 0, u8 flags_clear = 0); // Check flags for specified memory range (unsafe) - bool check_addr(u32 addr, u32 size = 1, u8 flags = page_readable); + bool check_addr(u32 addr, u8 flags, u32 size); + + template + bool check_addr(u32 addr, u8 flags = page_readable) + { + extern std::array g_pages; + + if (Size - 1 >= 4095u || Size & (Size - 1) || addr % Size) + { + // TODO + return check_addr(addr, flags, Size); + } + + return !(~g_pages[addr / 4096].flags & (flags | page_allocated)); + } // Search and map memory in specified memory location (min alignment is 0x10000) u32 alloc(u32 size, memory_location_t location, u32 align = 0x10000); diff --git a/rpcs3/Emu/Memory/vm_locking.h b/rpcs3/Emu/Memory/vm_locking.h index 6594ca35f1..2467a5063e 100644 --- a/rpcs3/Emu/Memory/vm_locking.h +++ b/rpcs3/Emu/Memory/vm_locking.h @@ -52,6 +52,13 @@ namespace vm // Old-style conditional constexpr const u32 size = Size ? Size : _size; + if (size <= 4096u && !((begin | size) & (size - 1)) ? !vm::check_addr(begin) : !vm::check_addr(begin, vm::page_readable, size)) + { + range_lock->release(0); + range_lock_internal(range_lock, begin, _size); + return; + } + const u64 lock_val = g_range_lock.load(); const u64 is_share = g_shmem[begin >> 16].load(); diff --git a/rpcs3/Emu/RSX/Common/texture_cache.h b/rpcs3/Emu/RSX/Common/texture_cache.h index d7ac7d7ca4..c97083bf40 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache.h +++ b/rpcs3/Emu/RSX/Common/texture_cache.h @@ -2115,7 +2115,7 @@ namespace rsx } else { - if (!vm::check_addr(write_end, (heuristic_end - write_end), vm::page_info_t::page_allocated)) + if (!vm::check_addr(write_end, vm::page_readable, (heuristic_end - write_end))) { // Enforce strict allocation size! return false; diff --git a/rpcs3/rpcs3qt/breakpoint_list.cpp b/rpcs3/rpcs3qt/breakpoint_list.cpp index 31a52c816c..227bf3ecbc 100644 --- a/rpcs3/rpcs3qt/breakpoint_list.cpp +++ b/rpcs3/rpcs3qt/breakpoint_list.cpp @@ -96,7 +96,7 @@ void breakpoint_list::HandleBreakpointRequest(u32 loc) { const auto cpu = this->cpu.lock(); - if (cpu->id_type() == 1 && vm::check_addr(loc, 1, vm::page_allocated | vm::page_executable)) + if (cpu->id_type() == 1 && vm::check_addr(loc, vm::page_allocated | vm::page_executable)) { AddBreakpoint(loc); } diff --git a/rpcs3/rpcs3qt/cheat_manager.cpp b/rpcs3/rpcs3qt/cheat_manager.cpp index 0040f8a1e3..23ba7bafff 100644 --- a/rpcs3/rpcs3qt/cheat_manager.cpp +++ b/rpcs3/rpcs3qt/cheat_manager.cpp @@ -325,7 +325,7 @@ std::vector cheat_engine::search(const T value, const std::vector& to_ { for (const auto& off : to_filter) { - if (vm::check_addr(off, sizeof(T))) + if (vm::check_addr(off)) { if (*vm::get_super_ptr(off) == value_swapped) results.push_back(off); @@ -364,7 +364,7 @@ T cheat_engine::get_value(const u32 offset, bool& success) return cpu_thread::suspend_all(nullptr, {}, [&]() -> T { - if (!vm::check_addr(offset, sizeof(T))) + if (!vm::check_addr(offset)) { success = false; return 0; @@ -381,21 +381,21 @@ bool cheat_engine::set_value(const u32 offset, const T value) if (Emu.IsStopped()) return false; - if (!vm::check_addr(offset, sizeof(T))) + if (!vm::check_addr(offset)) { return false; } return cpu_thread::suspend_all(nullptr, {}, [&] { - if (!vm::check_addr(offset, sizeof(T))) + if (!vm::check_addr(offset)) { return false; } *vm::get_super_ptr(offset) = value; - const bool exec_code_at_start = vm::check_addr(offset, 1, vm::page_executable); + const bool exec_code_at_start = vm::check_addr(offset, vm::page_executable); const bool exec_code_at_end = [&]() { if constexpr (sizeof(T) == 1) @@ -404,7 +404,7 @@ bool cheat_engine::set_value(const u32 offset, const T value) } else { - return vm::check_addr(offset + sizeof(T) - 1, 1, vm::page_executable); + return vm::check_addr(offset + sizeof(T) - 1, vm::page_executable); } }(); diff --git a/rpcs3/rpcs3qt/debugger_list.cpp b/rpcs3/rpcs3qt/debugger_list.cpp index 3d1a0429ee..4cba188967 100644 --- a/rpcs3/rpcs3qt/debugger_list.cpp +++ b/rpcs3/rpcs3qt/debugger_list.cpp @@ -126,14 +126,14 @@ void debugger_list::ShowAddress(u32 addr, bool force) item(i)->setBackground(default_background); } - if (!is_spu && !vm::check_addr(pc, 4)) + if (!is_spu && !vm::check_addr(pc)) { item(i)->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(fmt::format("[%08x] ?? ?? ?? ??:", pc))); count = 4; continue; } - if (!is_spu && !vm::check_addr(pc, 4, vm::page_executable)) + if (!is_spu && !vm::check_addr(pc, vm::page_executable)) { const u32 data = *vm::get_super_ptr>(pc); item(i)->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(fmt::format("[%08x] %02x %02x %02x %02x:", pc, diff --git a/rpcs3/rpcs3qt/kernel_explorer.cpp b/rpcs3/rpcs3qt/kernel_explorer.cpp index 60cd5a21e8..f377652fc9 100644 --- a/rpcs3/rpcs3qt/kernel_explorer.cpp +++ b/rpcs3/rpcs3qt/kernel_explorer.cpp @@ -514,7 +514,7 @@ void kernel_explorer::Update() if (!pspurs) { - if (arg < UINT32_MAX && arg % 0x80 == 0 && vm::check_addr(arg, pspurs.size())) + if (arg < UINT32_MAX && arg % 0x80 == 0 && vm::check_addr(arg, vm::page_readable, pspurs.size())) { pspurs.set(static_cast(arg)); } diff --git a/rpcs3/rpcs3qt/register_editor_dialog.cpp b/rpcs3/rpcs3qt/register_editor_dialog.cpp index 35dfdcf2d4..eaf94874f2 100644 --- a/rpcs3/rpcs3qt/register_editor_dialog.cpp +++ b/rpcs3/rpcs3qt/register_editor_dialog.cpp @@ -324,7 +324,7 @@ void register_editor_dialog::OnOkay(const std::shared_ptr& _cpu) if (reg == PPU_CR) ppu.cr.unpack(reg_value); else if (reg == PPU_VRSAVE) ppu.vrsave = reg_value; else if (reg == PPU_PRIO && !sys_ppu_thread_set_priority(ppu, ppu.id, reg_value)) {} - else if (reg == PC && reg_value % 4 == 0 && vm::check_addr(reg_value, 4, vm::page_executable)) ppu.cia = reg_value & -4; + else if (reg == PC && reg_value % 4 == 0 && vm::check_addr(reg_value, vm::page_executable)) ppu.cia = reg_value & -4; else ok = false; if (ok) return; } diff --git a/rpcs3/rpcs3qt/rsx_debugger.cpp b/rpcs3/rpcs3qt/rsx_debugger.cpp index e4c4db2847..620c28c77f 100644 --- a/rpcs3/rpcs3qt/rsx_debugger.cpp +++ b/rpcs3/rpcs3qt/rsx_debugger.cpp @@ -662,7 +662,7 @@ void rsx_debugger::GetBuffers() const u32 width = buffers[bufferId].width; const u32 height = buffers[bufferId].height; - if(!vm::check_addr(RSXbuffer_addr, width * height * 4)) + if (!vm::check_addr(RSXbuffer_addr, vm::page_readable, width * height * 4)) continue; const auto RSXbuffer = vm::get_super_ptr(RSXbuffer_addr); @@ -908,7 +908,7 @@ void rsx_debugger::SetPC(const uint pc) void rsx_debugger::PerformJump(u32 address) { - if (!vm::check_addr(address, 4)) + if (!vm::check_addr(address)) return; u32 cmd = *vm::get_super_ptr(address);