1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-22 18:53:28 +01:00

Fix vm::range_lock, imporve vm::check_addr

This commit is contained in:
Eladash 2020-11-10 19:09:28 +02:00 committed by Ivan
parent 6e27ab60ca
commit fefab50e06
17 changed files with 95 additions and 68 deletions

View File

@ -390,13 +390,13 @@ extern bool ppu_patch(u32 addr, u32 value)
vm::reader_lock rlock; 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); ppu_log.fatal("Patch failed at 0x%x: invalid memory address.", addr);
return false; 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()) 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); fmt::append(ret, "r%d%s: 0x%-8llx", i, i <= 9 ? " " : "", reg);
const u32 max_str_len = 32; constexpr u32 max_str_len = 32;
const u32 hex_count = 8; constexpr u32 hex_count = 8;
if (reg <= UINT32_MAX && vm::check_addr(static_cast<u32>(reg), max_str_len)) if (reg <= UINT32_MAX && vm::check_addr<max_str_len>(static_cast<u32>(reg)))
{ {
bool is_function = false; bool is_function = false;
u32 toc = 0; u32 toc = 0;
if (const u32 reg_ptr = *vm::get_super_ptr<u32>(static_cast<u32>(reg)); if (const u32 reg_ptr = *vm::get_super_ptr<u32>(static_cast<u32>(reg));
vm::check_addr(reg_ptr, max_str_len)) vm::check_addr<max_str_len>(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<u32>(static_cast<u32>(reg + 4)); toc = *vm::get_super_ptr<u32>(static_cast<u32>(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; is_function = true;
} }
@ -577,7 +577,7 @@ std::vector<std::pair<u32, u32>> ppu_thread::dump_callstack_list() const
const u32 stack_ptr = static_cast<u32>(r1); const u32 stack_ptr = static_cast<u32>(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 // Normally impossible unless the code does not follow ABI rules
return {}; return {};
@ -586,12 +586,12 @@ std::vector<std::pair<u32, u32>> ppu_thread::dump_callstack_list() const
u32 stack_min = stack_ptr & ~0xfff; u32 stack_min = stack_ptr & ~0xfff;
u32 stack_max = stack_min + 4096; 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; 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; stack_max += 4096;
} }
@ -610,7 +610,7 @@ std::vector<std::pair<u32, u32>> ppu_thread::dump_callstack_list() const
auto is_invalid = [](u64 addr) auto is_invalid = [](u64 addr)
{ {
if (addr > UINT32_MAX || addr % 4 || !vm::check_addr(static_cast<u32>(addr), 1, vm::page_executable)) if (addr > UINT32_MAX || addr % 4 || !vm::check_addr(static_cast<u32>(addr), vm::page_executable))
{ {
return true; return true;
} }
@ -1140,7 +1140,7 @@ void ppu_trap(ppu_thread& ppu, u64 addr)
u32 add = static_cast<u32>(g_cfg.core.stub_ppu_traps) * 4; u32 add = static_cast<u32>(g_cfg.core.stub_ppu_traps) * 4;
// If stubbing is enabled, check current instruction and the following // 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); 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) if (raddr / 128 != addr / 128 || !ppu.use_full_rdata)
{ {
// Even when the reservation address does not match the target address must be valid // 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 // Access violate
data += 0; data += 0;

View File

@ -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<atomic_t<u8>>(addr) += 0; // Access violate vm::_ref<atomic_t<u8>>(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)) if ((lock_val >> vm::range_pos) == (vm::range_locked >> vm::range_pos))
{ {
// All page flags are untouched and can be read safely // 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 // Assume our memory is being (de)allocated
range_lock->release(0); 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) if (lock_addr + lock_size <= addr || lock_addr >= addr + 128)
{ {
// We are outside locked range, so page flags are unaffected // 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); range_lock->release(0);
break; break;

View File

@ -16,18 +16,13 @@ error_code sys_dbg_read_process_memory(s32 pid, u32 address, u32 size, vm::ptr<v
return CELL_LV2DBG_ERROR_DEINVALIDARGUMENTS; return CELL_LV2DBG_ERROR_DEINVALIDARGUMENTS;
} }
if (!vm::check_addr(address, size))
{
return CELL_EFAULT;
}
if (!size || !data) if (!size || !data)
{ {
return CELL_LV2DBG_ERROR_DEINVALIDARGUMENTS; return CELL_LV2DBG_ERROR_DEINVALIDARGUMENTS;
} }
// Check if data destination is writable // Check if data destination is writable
if (!vm::check_addr(data.addr(), size, vm::page_writable)) if (!vm::check_addr(data.addr(), vm::page_writable, size))
{ {
return CELL_EFAULT; return CELL_EFAULT;
} }
@ -47,18 +42,13 @@ error_code sys_dbg_write_process_memory(s32 pid, u32 address, u32 size, vm::cptr
return CELL_LV2DBG_ERROR_DEINVALIDARGUMENTS; return CELL_LV2DBG_ERROR_DEINVALIDARGUMENTS;
} }
if (!vm::check_addr(address, size))
{
return CELL_EFAULT;
}
if (!size || !data) if (!size || !data)
{ {
return CELL_LV2DBG_ERROR_DEINVALIDARGUMENTS; return CELL_LV2DBG_ERROR_DEINVALIDARGUMENTS;
} }
// Check if data source is readable // Check if data source is readable
if (!vm::check_addr(data.addr(), size, vm::page_readable)) if (!vm::check_addr(data.addr(), vm::page_readable, size))
{ {
return CELL_EFAULT; return CELL_EFAULT;
} }

View File

@ -179,7 +179,7 @@ error_code sys_memory_get_page_attribute(cpu_thread& cpu, u32 addr, vm::ptr<sys_
return CELL_EINVAL; return CELL_EINVAL;
} }
if (!vm::check_addr(attr.addr(), attr.size())) if (!vm::check_addr(attr.addr(), vm::page_readable, attr.size()))
{ {
return CELL_EFAULT; return CELL_EFAULT;
} }
@ -187,11 +187,11 @@ error_code sys_memory_get_page_attribute(cpu_thread& cpu, u32 addr, vm::ptr<sys_
attr->attribute = 0x40000ull; // SYS_MEMORY_PROT_READ_WRITE (TODO) attr->attribute = 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) 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; 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; attr->page_size = 0x10000;
} }

View File

@ -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) 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; return CELL_EINVAL;
} }

View File

@ -92,7 +92,7 @@ error_code sys_tty_write(s32 ch, vm::cptr<char> buf, u32 len, vm::ptr<u32> pwrit
std::string msg; std::string msg;
if (static_cast<s32>(len) > 0 && vm::check_addr(buf.addr(), len)) if (static_cast<s32>(len) > 0 && vm::check_addr(buf.addr(), vm::page_readable, len))
{ {
msg.resize(len); msg.resize(len);

View File

@ -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)); u32 len = hex_to_u32(cmd.data.substr(s + 1, s2 - s - 1));
const char* data_ptr = (cmd.data.c_str()) + s2 + 1; const char* data_ptr = (cmd.data.c_str()) + s2 + 1;
for (u32 i = 0; i < len; ++i) { 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; u8 val;
int res = sscanf_s(data_ptr, "%02hhX", &val); int res = sscanf_s(data_ptr, "%02hhX", &val);
if (!res) { if (!res) {

View File

@ -77,13 +77,6 @@ namespace vm
// Memory range lock slots (sparse atomics) // Memory range lock slots (sparse atomics)
atomic_t<u64, 64> g_range_lock_set[64]{}; atomic_t<u64, 64> g_range_lock_set[64]{};
// Page information
struct memory_page
{
// Memory flags
atomic_t<u8> flags;
};
// Memory pages // Memory pages
std::array<memory_page, 0x100000000 / 4096> g_pages{}; std::array<memory_page, 0x100000000 / 4096> g_pages{};
@ -192,7 +185,7 @@ namespace vm
{ {
const u64 new_lock_val = g_range_lock.load(); 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; break;
} }
@ -960,10 +953,15 @@ namespace vm
return size; 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 // Overflow checking
if (addr + size < addr && (addr + size) != 0) if (0x10000'0000ull - addr < size)
{ {
return false; return false;
} }
@ -971,12 +969,28 @@ namespace vm
// Always check this flag // Always check this flag
flags |= page_allocated; 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; 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; return true;
@ -1534,12 +1548,7 @@ namespace vm
{ {
vm::reader_lock lock; vm::reader_lock lock;
if (size == 0) if (vm::check_addr(addr, is_write ? page_writable : page_readable, size))
{
return true;
}
if (vm::check_addr(addr, size, is_write ? page_writable : page_readable))
{ {
void* src = vm::g_sudo_addr + addr; void* src = vm::g_sudo_addr + addr;
void* dst = ptr; void* dst = ptr;

View File

@ -52,11 +52,32 @@ namespace vm
// Address type // Address type
enum addr_t : u32 {}; enum addr_t : u32 {};
// Page information
struct memory_page
{
// Memory flags
atomic_t<u8> flags;
};
// Change memory protection of specified memory region // 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); 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) // 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 <u32 Size = 1>
bool check_addr(u32 addr, u8 flags = page_readable)
{
extern std::array<memory_page, 0x100000000 / 4096> 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) // Search and map memory in specified memory location (min alignment is 0x10000)
u32 alloc(u32 size, memory_location_t location, u32 align = 0x10000); u32 alloc(u32 size, memory_location_t location, u32 align = 0x10000);

View File

@ -52,6 +52,13 @@ namespace vm
// Old-style conditional constexpr // Old-style conditional constexpr
const u32 size = Size ? Size : _size; 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 lock_val = g_range_lock.load();
const u64 is_share = g_shmem[begin >> 16].load(); const u64 is_share = g_shmem[begin >> 16].load();

View File

@ -2115,7 +2115,7 @@ namespace rsx
} }
else 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! // Enforce strict allocation size!
return false; return false;

View File

@ -96,7 +96,7 @@ void breakpoint_list::HandleBreakpointRequest(u32 loc)
{ {
const auto cpu = this->cpu.lock(); 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); AddBreakpoint(loc);
} }

View File

@ -325,7 +325,7 @@ std::vector<u32> cheat_engine::search(const T value, const std::vector<u32>& to_
{ {
for (const auto& off : to_filter) for (const auto& off : to_filter)
{ {
if (vm::check_addr(off, sizeof(T))) if (vm::check_addr<sizeof(T)>(off))
{ {
if (*vm::get_super_ptr<T>(off) == value_swapped) if (*vm::get_super_ptr<T>(off) == value_swapped)
results.push_back(off); 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 return cpu_thread::suspend_all(nullptr, {}, [&]() -> T
{ {
if (!vm::check_addr(offset, sizeof(T))) if (!vm::check_addr<sizeof(T)>(offset))
{ {
success = false; success = false;
return 0; return 0;
@ -381,21 +381,21 @@ bool cheat_engine::set_value(const u32 offset, const T value)
if (Emu.IsStopped()) if (Emu.IsStopped())
return false; return false;
if (!vm::check_addr(offset, sizeof(T))) if (!vm::check_addr<sizeof(T)>(offset))
{ {
return false; return false;
} }
return cpu_thread::suspend_all(nullptr, {}, [&] return cpu_thread::suspend_all(nullptr, {}, [&]
{ {
if (!vm::check_addr(offset, sizeof(T))) if (!vm::check_addr<sizeof(T)>(offset))
{ {
return false; return false;
} }
*vm::get_super_ptr<T>(offset) = value; *vm::get_super_ptr<T>(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 = [&]() const bool exec_code_at_end = [&]()
{ {
if constexpr (sizeof(T) == 1) if constexpr (sizeof(T) == 1)
@ -404,7 +404,7 @@ bool cheat_engine::set_value(const u32 offset, const T value)
} }
else else
{ {
return vm::check_addr(offset + sizeof(T) - 1, 1, vm::page_executable); return vm::check_addr(offset + sizeof(T) - 1, vm::page_executable);
} }
}(); }();

View File

@ -126,14 +126,14 @@ void debugger_list::ShowAddress(u32 addr, bool force)
item(i)->setBackground(default_background); 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))); item(i)->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(fmt::format("[%08x] ?? ?? ?? ??:", pc)));
count = 4; count = 4;
continue; 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<atomic_be_t<u32>>(pc); const u32 data = *vm::get_super_ptr<atomic_be_t<u32>>(pc);
item(i)->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(fmt::format("[%08x] %02x %02x %02x %02x:", pc, item(i)->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(fmt::format("[%08x] %02x %02x %02x %02x:", pc,

View File

@ -514,7 +514,7 @@ void kernel_explorer::Update()
if (!pspurs) 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<u32>(arg)); pspurs.set(static_cast<u32>(arg));
} }

View File

@ -324,7 +324,7 @@ void register_editor_dialog::OnOkay(const std::shared_ptr<cpu_thread>& _cpu)
if (reg == PPU_CR) ppu.cr.unpack(reg_value); if (reg == PPU_CR) ppu.cr.unpack(reg_value);
else if (reg == PPU_VRSAVE) ppu.vrsave = 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 == 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; else ok = false;
if (ok) return; if (ok) return;
} }

View File

@ -662,7 +662,7 @@ void rsx_debugger::GetBuffers()
const u32 width = buffers[bufferId].width; const u32 width = buffers[bufferId].width;
const u32 height = buffers[bufferId].height; 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; continue;
const auto RSXbuffer = vm::get_super_ptr<const u8>(RSXbuffer_addr); const auto RSXbuffer = vm::get_super_ptr<const u8>(RSXbuffer_addr);
@ -908,7 +908,7 @@ void rsx_debugger::SetPC(const uint pc)
void rsx_debugger::PerformJump(u32 address) void rsx_debugger::PerformJump(u32 address)
{ {
if (!vm::check_addr(address, 4)) if (!vm::check_addr(address))
return; return;
u32 cmd = *vm::get_super_ptr<u32>(address); u32 cmd = *vm::get_super_ptr<u32>(address);