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;
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<u32>(reg), max_str_len))
if (reg <= UINT32_MAX && vm::check_addr<max_str_len>(static_cast<u32>(reg)))
{
bool is_function = false;
u32 toc = 0;
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));
@ -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<std::pair<u32, u32>> ppu_thread::dump_callstack_list() const
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
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_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<std::pair<u32, u32>> ppu_thread::dump_callstack_list() const
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;
}
@ -1140,7 +1140,7 @@ void ppu_trap(ppu_thread& ppu, u64 addr)
u32 add = static_cast<u32>(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;

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
}
@ -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;

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;
}
if (!vm::check_addr(address, size))
{
return CELL_EFAULT;
}
if (!size || !data)
{
return CELL_LV2DBG_ERROR_DEINVALIDARGUMENTS;
}
// 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;
}
@ -47,18 +42,13 @@ error_code sys_dbg_write_process_memory(s32 pid, u32 address, u32 size, vm::cptr
return CELL_LV2DBG_ERROR_DEINVALIDARGUMENTS;
}
if (!vm::check_addr(address, size))
{
return CELL_EFAULT;
}
if (!size || !data)
{
return CELL_LV2DBG_ERROR_DEINVALIDARGUMENTS;
}
// 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;
}

View File

@ -179,7 +179,7 @@ error_code sys_memory_get_page_attribute(cpu_thread& cpu, u32 addr, vm::ptr<sys_
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;
}
@ -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->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;
}

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)
{
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;
}

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;
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);

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));
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) {

View File

@ -77,13 +77,6 @@ namespace vm
// Memory range lock slots (sparse atomics)
atomic_t<u64, 64> g_range_lock_set[64]{};
// Page information
struct memory_page
{
// Memory flags
atomic_t<u8> flags;
};
// Memory pages
std::array<memory_page, 0x100000000 / 4096> 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;

View File

@ -52,11 +52,32 @@ namespace vm
// Address type
enum addr_t : u32 {};
// Page information
struct memory_page
{
// Memory flags
atomic_t<u8> 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 <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)
u32 alloc(u32 size, memory_location_t location, u32 align = 0x10000);

View File

@ -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();

View File

@ -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;

View File

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

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)
{
if (vm::check_addr(off, sizeof(T)))
if (vm::check_addr<sizeof(T)>(off))
{
if (*vm::get_super_ptr<T>(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<sizeof(T)>(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<sizeof(T)>(offset))
{
return false;
}
return cpu_thread::suspend_all(nullptr, {}, [&]
{
if (!vm::check_addr(offset, sizeof(T)))
if (!vm::check_addr<sizeof(T)>(offset))
{
return false;
}
*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 = [&]()
{
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);
}
}();

View File

@ -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<atomic_be_t<u32>>(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 (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));
}

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);
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;
}

View File

@ -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<const u8>(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<u32>(address);