diff --git a/Core/Core.cpp b/Core/Core.cpp index 95767ddede..fbe0b3d8a9 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -602,9 +602,11 @@ const char *MemoryExceptionTypeAsString(MemoryExceptionType type) { switch (type) { case MemoryExceptionType::UNKNOWN: return "Unknown"; case MemoryExceptionType::READ_WORD: return "Read Word"; - case MemoryExceptionType::WRITE_WORD: return "Write Word"; case MemoryExceptionType::READ_BLOCK: return "Read Block"; + case MemoryExceptionType::WRITE_WORD: return "Write Word"; case MemoryExceptionType::WRITE_BLOCK: return "Read/Write Block"; + case MemoryExceptionType::HLE_READ: return "HLE Read"; + case MemoryExceptionType::HLE_WRITE: return "HLE Write"; case MemoryExceptionType::ALIGNMENT: return "Alignment"; default: return "N/A"; @@ -638,14 +640,13 @@ static std::string ModuleAddressSuffix(u32 address) { } } -void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionType type, std::string_view additionalInfo, bool forceReport) { - const char *desc = MemoryExceptionTypeAsString(type); +void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionType type, std::string_view additionalInfo) { // In jit, we only flush PC when bIgnoreBadMemAccess is off. char pcDetails[128]; pcDetails[0] = 0; if ((CPUCore)g_Config.iCpuCore == CPUCore::INTERPRETER) { - snprintf(pcDetails, sizeof(pcDetails), " PC %08x%s LR %08x%s", currentMIPS->pc, ModuleAddressSuffix(currentMIPS->pc).c_str(), currentMIPS->r[MIPS_REG_RA], ModuleAddressSuffix(currentMIPS->r[MIPS_REG_RA]).c_str()); + snprintf(pcDetails, sizeof(pcDetails), " PC %08x%s RA %08x%s", currentMIPS->pc, ModuleAddressSuffix(currentMIPS->pc).c_str(), currentMIPS->r[MIPS_REG_RA], ModuleAddressSuffix(currentMIPS->r[MIPS_REG_RA]).c_str()); } const std::string addressSuffix = ModuleAddressSuffix(address); @@ -663,6 +664,7 @@ void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionTy break; } + const char *desc = MemoryExceptionTypeAsString(type); if (action == ExceptionAction::Ignore) { // Simplest logging and continue. WARN_LOG(Log::MemMap, "%s: Invalid access at %08x%s (size %08x) %s%.*s", desc, address, addressSuffix.c_str(), accessSize, pcDetails, (int)additionalInfo.length(), additionalInfo.data()); @@ -686,6 +688,72 @@ void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionTy } } +void Core_MemoryExceptionHLE(MIPSState *mips, u32 address, u32 accessSize, MemoryExceptionType type) { + ExceptionAction action; + switch (type) { + case MemoryExceptionType::HLE_WRITE: + action = ResolveExceptionAction((ExceptionAction)g_Config.iExceptionActionMemWrite); + break; + case MemoryExceptionType::HLE_READ: + action = ResolveExceptionAction((ExceptionAction)g_Config.iExceptionActionMemRead); + break; + default: + _dbg_assert_(false); + action = ExceptionAction::Break; + break; + } + + const HLEFunction *func = HLEGetFunctionBeingCalled(); + const char *funcName = func ? func->name : "unknown"; + + char args[512] = ""; + if (func) { + HLEFormatLogArgs(mips, args, sizeof(args), func->argmask); + } + + const char *extra = ""; + // We do report some unaligned addresses. There are probably more that should report. + // We try to derive the reason here, though maybe it should be passed in explicitly? + // TODO: This check should probably be added to regular memory accesses too. + if (Memory::IsValidAddress(address)) { + if (accessSize == 2 || accessSize == 4 || accessSize == 8 || (address & (accessSize - 1))) { + extra = " (unaligned)"; + } else if (accessSize > 8 && (accessSize & 3)) { + extra = " (unaligned struct)"; + } + } + + const u32 pc = mips->pc; + char msg[512]; + snprintf(msg, sizeof(msg), "Invalid access in %s(%s) %s at %08x%s (size %08x) PC %08x%s RA %08x%s", + funcName, args, + extra, address, ModuleAddressSuffix(address).c_str(), accessSize, + pc, ModuleAddressSuffix(pc).c_str(), + mips->r[MIPS_REG_RA], ModuleAddressSuffix(mips->r[MIPS_REG_RA]).c_str()); + + const char *desc = MemoryExceptionTypeAsString(type); + if (action == ExceptionAction::Ignore) { + // Simplest logging and continue. + WARN_LOG(Log::MemMap, "HLE %s: %s", MemoryExceptionTypeAsString(type), msg); + return; + } + + const std::string stackTrace = FormatStackTrace(WalkCurrentStack(-1)); + ERROR_LOG(Log::MemMap, "%s: %s\n%s", desc, msg, stackTrace.c_str()); + if (action == ExceptionAction::Break) { + MIPSExceptionInfo &e = g_exceptionInfo; + e = {}; + e.type = MIPSExceptionType::MEMORY; + e.info.clear(); + e.memory_type = type; + e.address = address; + e.accessSize = accessSize; + e.stackTrace = stackTrace; + e.pc = pc; + Core_Break(BreakReason::MemoryException, address); + } +} + // Can't be ignored, must break. Not sure we can get a meaningful stack trace here (since the PC is invalid). void Core_ExecException(u32 address, u32 pc, ExecExceptionType type) { const char *desc = ExecExceptionTypeAsString(type); diff --git a/Core/Core.h b/Core/Core.h index 61f6c48693..c43689aed5 100644 --- a/Core/Core.h +++ b/Core/Core.h @@ -20,7 +20,6 @@ #include #include #include -#include #include #include "Common/CommonTypes.h" @@ -199,6 +198,8 @@ enum class MemoryExceptionType { UNKNOWN, READ_WORD, WRITE_WORD, + HLE_READ, + HLE_WRITE, READ_BLOCK, WRITE_BLOCK, ALIGNMENT, @@ -208,12 +209,16 @@ enum class ExecExceptionType { THREAD, }; -void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionType type, std::string_view additionalInfo = "", bool forceReport = false); +void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionType type, std::string_view additionalInfo = ""); void Core_ExecException(u32 address, u32 pc, ExecExceptionType type); void Core_BreakException(u32 pc); // Call when loading save states, etc. void Core_ResetException(); +class MIPSState; +// Shortcut, just calls Core_MemoryException with automatically determined parameters (function name, etc). +void Core_MemoryExceptionHLE(MIPSState *mips, u32 address, u32 accessSize, MemoryExceptionType type); + enum class MIPSExceptionType { NONE, MEMORY, diff --git a/Core/CwCheat.cpp b/Core/CwCheat.cpp index 597ea5d4e3..1968f23d57 100644 --- a/Core/CwCheat.cpp +++ b/Core/CwCheat.cpp @@ -919,7 +919,7 @@ void CWCheatEngine::ExecuteOp(const CheatOperation &op, const CheatCode &cheat, float f; uint32_t u; } value; - value.u = Memory::Read_U32(op.addr); + value.u = Memory::ReadUnchecked_U32(op.addr); // we check the range above std::string shaderName = shaderChain[op.PostShaderUniform.shader]->section; switch (op.PostShaderUniform.format) { case 0: @@ -1035,8 +1035,11 @@ void CWCheatEngine::ExecuteOp(const CheatOperation &op, const CheatCode &cheat, case CheatOp::CwCheatPointerCommands: { + if (!Memory::IsValidAddress(op.addr + op.pointerCommands.baseOffset)) { + break; + } InvalidateICache(op.addr + op.pointerCommands.baseOffset, 4); // See note at top of file - u32 base = Memory::Read_U32(op.addr + op.pointerCommands.baseOffset); + u32 base = Memory::ReadUnchecked_U32(op.addr + op.pointerCommands.baseOffset); u32 val = op.val; int type = op.pointerCommands.type; for (int a = 0; a < op.pointerCommands.count; ++a) { diff --git a/Core/Debugger/DisassemblyManager.cpp b/Core/Debugger/DisassemblyManager.cpp index 39266230fc..3f7165d163 100644 --- a/Core/Debugger/DisassemblyManager.cpp +++ b/Core/Debugger/DisassemblyManager.cpp @@ -790,7 +790,12 @@ void DisassemblyData::createLines() lineAddresses.clear(); u32 pos = address; - const u32 end = address+size; + const u32 end = address + size; + + if (!Memory::IsValidRange(address, size)) { + ERROR_LOG(Log::CPU, "DisassemblyData can't create lines for invalid range 0x%08X-0x%08X", address, end); + } + const u32 maxChars = g_disassemblyManager.getMaxParamChars(); std::string currentLine; @@ -802,7 +807,7 @@ void DisassemblyData::createLines() bool inString = false; while (pos < end) { - u8 b = Memory::Read_U8(pos++); + u8 b = Memory::ReadUnchecked_U8(pos++); if (b >= 0x20 && b <= 0x7F) { if (currentLine.size()+1 >= maxChars) @@ -879,18 +884,18 @@ void DisassemblyData::createLines() switch (type) { case DATATYPE_BYTE: - value = Memory::Read_U8(pos); + value = Memory::ReadUnchecked_U8(pos); snprintf(buffer, sizeof(buffer), "0x%02X", value); pos++; break; case DATATYPE_HALFWORD: - value = Memory::Read_U16(pos); + value = Memory::ReadUnchecked_U16(pos); snprintf(buffer, sizeof(buffer), "0x%04X", value); pos += 2; break; case DATATYPE_WORD: { - value = Memory::Read_U32(pos); + value = Memory::ReadUnchecked_U32(pos); const std::string label = g_symbolMap->GetLabelString(value); if (!label.empty()) snprintf(buffer, sizeof(buffer), "%s", label.c_str()); diff --git a/Core/Debugger/WebSocket/MemorySubscriber.cpp b/Core/Debugger/WebSocket/MemorySubscriber.cpp index 7d61ee5d14..cbbca543ac 100644 --- a/Core/Debugger/WebSocket/MemorySubscriber.cpp +++ b/Core/Debugger/WebSocket/MemorySubscriber.cpp @@ -182,7 +182,7 @@ void WebSocketMemoryReadU32(DebuggerRequest &req) { Core_RunOnCPUThread([&] { AutoDisabledReplacements memLock = LockMemory(true); JsonWriter &json = req.Respond(); - json.writeUint("value", Memory::Read_U32(addr)); + json.writeUint("value", Memory::ReadUnchecked_U32(addr)); }); } @@ -334,7 +334,7 @@ void WebSocketMemoryWriteU8(DebuggerRequest &req) { // Write two bytes to memory (memory.write_u16) // // Parameters: -// - address: unsigned integer +// - address: unsigned integer (can be unaligned! But not recommended. Should maybe disallow). // - value: unsigned integer // // Response (same event name): @@ -352,7 +352,7 @@ void WebSocketMemoryWriteU16(DebuggerRequest &req) { return req.Fail("CPU not started"); // This only depends on addr, not on anything CPU-thread-owned, so fail fast here rather than // making a round trip through the queue for a request we already know is invalid. - if (!Memory::IsValidAddress(addr)) + if (!Memory::IsValidRange(addr, 2)) return req.Fail("Invalid address"); // Route the actual memory write to the CPU thread instead of poking at it directly @@ -360,7 +360,7 @@ void WebSocketMemoryWriteU16(DebuggerRequest &req) { Core_RunOnCPUThread([&] { AutoDisabledReplacements memLock = LockMemory(true); currentMIPS->InvalidateICache(addr, 2); - Memory::Write_U16(val, addr); + Memory::WriteUnchecked_U16(val, addr); Reporting::NotifyDebugger(); JsonWriter &json = req.Respond(); @@ -371,7 +371,7 @@ void WebSocketMemoryWriteU16(DebuggerRequest &req) { // Write four bytes to memory (memory.write_u32) // // Parameters: -// - address: unsigned integer +// - address: unsigned integer (can be unaligned! But not recommended. Should maybe disallow). // - value: unsigned integer // // Response (same event name): @@ -389,7 +389,7 @@ void WebSocketMemoryWriteU32(DebuggerRequest &req) { return req.Fail("CPU not started"); // This only depends on addr, not on anything CPU-thread-owned, so fail fast here rather than // making a round trip through the queue for a request we already know is invalid. - if (!Memory::IsValidAddress(addr)) + if (!Memory::IsValidRange(addr, 4)) return req.Fail("Invalid address"); // Route the actual memory write to the CPU thread instead of poking at it directly @@ -397,11 +397,11 @@ void WebSocketMemoryWriteU32(DebuggerRequest &req) { Core_RunOnCPUThread([&] { AutoDisabledReplacements memLock = LockMemory(true); currentMIPS->InvalidateICache(addr, 4); - Memory::Write_U32(val, addr); + Memory::WriteUnchecked_U32(val, addr); Reporting::NotifyDebugger(); JsonWriter &json = req.Respond(); - json.writeUint("value", Memory::Read_U32(addr)); + json.writeUint("value", Memory::ReadUnchecked_U32(addr)); }); } diff --git a/Core/Dialog/PSPGamedataInstallDialog.cpp b/Core/Dialog/PSPGamedataInstallDialog.cpp index c72fa3b1af..8985cfc462 100644 --- a/Core/Dialog/PSPGamedataInstallDialog.cpp +++ b/Core/Dialog/PSPGamedataInstallDialog.cpp @@ -43,25 +43,16 @@ const u32 PSP_UTILITY_GAMEDATA_MODE_SHOW_PROGRESS = 1; static const std::string SFO_FILENAME = "PARAM.SFO"; -namespace -{ - std::vector GetPSPFileList (const std::string &dirpath) { - std::vector FileList; - auto Fileinfos = pspFileSystem.GetDirListing(dirpath); - FileList.reserve(Fileinfos.size()); +static std::vector GetPSPFileList(std::string_view dirpath) { + std::vector FileList; + auto Fileinfos = pspFileSystem.GetDirListing(dirpath); + FileList.reserve(Fileinfos.size()); - for (auto it = Fileinfos.begin(); it != Fileinfos.end(); ++it) { - std::string info = (*it).name; - FileList.push_back(info); - } - return FileList; + for (auto it = Fileinfos.begin(); it != Fileinfos.end(); ++it) { + std::string info = (*it).name; + FileList.push_back(info); } -} - -PSPGamedataInstallDialog::PSPGamedataInstallDialog(UtilityDialogType type) : PSPDialog(type) { -} - -PSPGamedataInstallDialog::~PSPGamedataInstallDialog() { + return FileList; } int PSPGamedataInstallDialog::Init(u32 paramAddr) { @@ -70,6 +61,12 @@ int PSPGamedataInstallDialog::Init(u32 paramAddr) { return SCE_ERROR_UTILITY_INVALID_STATUS; } + if (!Memory::IsValidRange(paramAddr, sizeof(SceUtilityGamedataInstallParam))) { + // This should probably crash + ERROR_LOG(Log::sceUtility, "sceGamedataInstallInitStart: invalid param address 0x%08X", paramAddr); + return SCE_KERNEL_ERROR_INVALID_POINTER; + } + param.ptr = paramAddr; inFileNames = GetPSPFileList("disc0:/PSP_GAME/INSDIR"); numFiles = (int)inFileNames.size(); @@ -90,7 +87,7 @@ int PSPGamedataInstallDialog::Init(u32 paramAddr) { return -1; } - int size = Memory::Read_U32(paramAddr); + const int size = Memory::ReadUnchecked_U32(paramAddr); if (size != 1424 && size != 1432) { ERROR_LOG_REPORT(Log::sceUtility, "sceGamedataInstallInitStart: invalid param size %d", size); return SCE_ERROR_UTILITY_INVALID_PARAM_SIZE; diff --git a/Core/Dialog/PSPGamedataInstallDialog.h b/Core/Dialog/PSPGamedataInstallDialog.h index eb11708526..f3161f2f4f 100644 --- a/Core/Dialog/PSPGamedataInstallDialog.h +++ b/Core/Dialog/PSPGamedataInstallDialog.h @@ -35,8 +35,7 @@ struct SceUtilityGamedataInstallParam { class PSPGamedataInstallDialog: public PSPDialog { public: - PSPGamedataInstallDialog(UtilityDialogType type); - ~PSPGamedataInstallDialog(); + PSPGamedataInstallDialog(UtilityDialogType type) : PSPDialog(type) {} int Init(u32 paramAddr); int Update(int animSpeed) override; diff --git a/Core/Dialog/PSPMsgDialog.cpp b/Core/Dialog/PSPMsgDialog.cpp index 4375cd144c..c22f81396b 100755 --- a/Core/Dialog/PSPMsgDialog.cpp +++ b/Core/Dialog/PSPMsgDialog.cpp @@ -57,12 +57,14 @@ int PSPMsgDialog::Init(unsigned int paramAddr) { } messageDialogAddr = paramAddr; - if (!Memory::IsValidAddress(messageDialogAddr)) - { - return 0; + + if (!Memory::IsValid4AlignedAddress(paramAddr)) { + // What to do? + return SCE_KERNEL_ERROR_BAD_ARGUMENT; } - int size = Memory::Read_U32(paramAddr); - memset(&messageDialog,0,sizeof(messageDialog)); + + int size = Memory::ReadUnchecked_U32(paramAddr); + memset(&messageDialog, 0, sizeof(messageDialog)); // Only copy the right size to support different request format Memory::Memcpy(&messageDialog,paramAddr,size); diff --git a/Core/Dialog/PSPNetconfDialog.cpp b/Core/Dialog/PSPNetconfDialog.cpp index e00721b5ff..9c4248c00a 100644 --- a/Core/Dialog/PSPNetconfDialog.cpp +++ b/Core/Dialog/PSPNetconfDialog.cpp @@ -59,16 +59,21 @@ int PSPNetconfDialog::Init(u32 paramAddr) { if (ReadStatus() != SCE_UTILITY_STATUS_NONE) return SCE_ERROR_UTILITY_INVALID_STATUS; + if (!Memory::IsValid4AlignedRange(paramAddr, sizeof(request))) { + // What to do? + return SCE_KERNEL_ERROR_BAD_ARGUMENT; + } + NOTICE_LOG(Log::sceUtility, "PSPNetConfDialog Init"); jsonReady_ = false; // Kick off a request to the infra-dns.json since we'll need it later. StartInfraJsonDownload(); requestAddr = paramAddr; - int size = Memory::Read_U32(paramAddr); + const u32 size = Memory::ReadUnchecked_U32(paramAddr); memset(&request, 0, sizeof(request)); - // Only copy the right size to support different request format - Memory::Memcpy(&request, paramAddr, size); + // Only copy the right size (bounded by the struct) to support different request format + Memory::Memcpy(&request, paramAddr, std::min(size, (u32)sizeof(request))); ChangeStatusInit(NET_INIT_DELAY_US); diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp index 7dbfc29608..165d5451bd 100644 --- a/Core/HLE/HLE.cpp +++ b/Core/HLE/HLE.cpp @@ -1009,7 +1009,7 @@ void hlePushFuncDesc(std::string_view module, std::string_view funcName) { } // TODO: Also add support for argument names. -size_t hleFormatLogArgs(char *message, size_t sz, const char *argmask) { +size_t HLEFormatLogArgs(const MIPSState *mips, char *message, size_t sz, const char *argmask) { char *p = message; size_t used = 0; @@ -1026,9 +1026,9 @@ size_t hleFormatLogArgs(char *message, size_t sz, const char *argmask) { for (size_t i = 0, n = strlen(argmask); i < n; ++i, ++reg) { u32 regval; if (reg < 8) { - regval = PARAM(reg); + regval = PARAM_MIPS(mips, reg); } else { - u32 sp = currentMIPS->r[MIPS_REG_SP]; + u32 sp = mips->r[MIPS_REG_SP]; // Goes upward on stack. // NOTE: Currently we only support > 8 for 32-bit integer args. regval = Memory::Read_U32(sp + (reg - 8) * 4); @@ -1036,16 +1036,16 @@ size_t hleFormatLogArgs(char *message, size_t sz, const char *argmask) { switch (argmask[i]) { case 'p': - if (Memory::IsValidAddress(regval)) { - APPEND_FMT("%08x[%08x]", regval, Memory::Read_U32(regval)); + if (Memory::IsValidRange(regval, 4)) { + APPEND_FMT("%08x[%08x]", regval, Memory::ReadUnchecked_U32(regval)); } else { APPEND_FMT("%08x[invalid]", regval); } break; case 'P': - if (Memory::IsValidAddress(regval)) { - APPEND_FMT("%08x[%016llx]", regval, Memory::Read_U64(regval)); + if (Memory::IsValidRange(regval, 8)) { + APPEND_FMT("%08x[%016llx]", regval, Memory::ReadUnchecked_U64(regval)); } else { APPEND_FMT("%08x[invalid]", regval); } @@ -1088,6 +1088,7 @@ size_t hleFormatLogArgs(char *message, size_t sz, const char *argmask) { --reg; break; + // TODO: Double? Does it ever happen? default: @@ -1118,6 +1119,14 @@ void hleLeave() { } // else warn? } +const HLEFunction *HLEGetFunctionBeingCalled() { + int stackSize = g_stackSize; + if (stackSize > 0) { + return g_stack[stackSize - 1]; + } + return nullptr; +} + void hleDoLogInternal(Log t, LogLevel level, u64 res, const char *file, int line, const char *reportTag, const char *reason, const char *formatted_reason) { char formatted_args[2048]; const char *funcName = "?"; @@ -1139,7 +1148,7 @@ void hleDoLogInternal(Log t, LogLevel level, u64 res, const char *file, int line // Need to do something smart in hleCall. But it's better than printing function name and args from the wrong function. if (stackSize == 1) { - hleFormatLogArgs(formatted_args, sizeof(formatted_args), hleFunc->argmask); + HLEFormatLogArgs(currentMIPS, formatted_args, sizeof(formatted_args), hleFunc->argmask); } else { truncate_cpy(formatted_args, "...N/A..."); } diff --git a/Core/HLE/HLE.h b/Core/HLE/HLE.h index 10d1af1ec8..d66a065e5c 100644 --- a/Core/HLE/HLE.h +++ b/Core/HLE/HLE.h @@ -98,6 +98,8 @@ struct Syscall { #define RETURN64(n) {u64 RETURN64_tmp = n; currentMIPS->r[MIPS_REG_V0] = RETURN64_tmp & 0xFFFFFFFF; currentMIPS->r[MIPS_REG_V1] = RETURN64_tmp >> 32;} #define RETURNF(fl) currentMIPS->f[0] = fl +#define PARAM_MIPS(mips, n) mips->r[MIPS_REG_A0 + n] + struct HLEModuleMeta { // This is the modname (name from the PRX header). Probably, we should really blacklist on the module names of the exported symbol metadata. const char *modname; @@ -174,6 +176,8 @@ inline s64 hleDelayResult(s64 result, const char *reason, int usec) { void HLEInit(); void HLEDoState(PointerWrap &p); void HLEShutdown(); +const HLEFunction *HLEGetFunctionBeingCalled(); +size_t HLEFormatLogArgs(const MIPSState *mips, char *message, size_t sz, const char *argmask); u32 GetSyscallOp(std::string_view module, u32 nib); bool WriteHLESyscall(std::string_view module, u32 nib, u32 address); void CallSyscall(MIPSOpcode op); diff --git a/Core/HLE/ReplaceTables.cpp b/Core/HLE/ReplaceTables.cpp index c97b384e4a..d2f73f62ea 100644 --- a/Core/HLE/ReplaceTables.cpp +++ b/Core/HLE/ReplaceTables.cpp @@ -728,16 +728,21 @@ static bool GetMIPSGPAddress(u32 &addr, s32 offset) { static int Hook_godseaterburst_blit_texture() { u32 texaddr; // Only if there's no texture. - if (!GetMIPSStaticAddress(texaddr, 0x000c, 0x0030)) { - return 0; - } - u32 fb_infoaddr; - if (Memory::Read_U32(texaddr) != 0 || !GetMIPSStaticAddress(fb_infoaddr, 0x01d0, 0x01d4)) { + if (!GetMIPSStaticAddress(texaddr, 0x000c, 0x0030) || !Memory::IsValid4AlignedAddress(texaddr)) { return 0; } - const u32 fb_info = Memory::Read_U32(fb_infoaddr); - const u32 fb_address = Memory::Read_U32(fb_info); + u32 fb_infoaddr; + if (Memory::ReadUnchecked_U32(texaddr) != 0 || !GetMIPSStaticAddress(fb_infoaddr, 0x01d0, 0x01d4) || !Memory::IsValid4AlignedAddress(fb_infoaddr)) { + return 0; + } + + const u32 fb_info = Memory::ReadUnchecked_U32(fb_infoaddr); + if (!Memory::IsValid4AlignedAddress(fb_info)) { + return 0; + } + + const u32 fb_address = Memory::ReadUnchecked_U32(fb_info); if (Memory::IsVRAMAddress(fb_address)) { gpu->PerformReadbackToMemory(fb_address, 0x00044000); NotifyMemInfo(MemBlockFlags::WRITE, fb_address, 0x00044000, "godseaterburst_blit_texture"); diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 469722c306..92b986d545 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -18,11 +18,13 @@ #include #include #include + #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" #include "Common/Serialize/SerializeMap.h" #include "Core/MemMapHelpers.h" #include "Core/HLE/HLE.h" +#include "Core/Core.h" #include "Core/HLE/ErrorCodes.h" #include "Core/MIPS/MIPS.h" #include "Core/CoreTiming.h" @@ -317,9 +319,13 @@ int sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optio } if (optionsPtr != 0) { - u32 size = Memory::Read_U32(optionsPtr); - if (size > 4) - WARN_LOG_REPORT(Log::sceKernel, "sceKernelCreateMutex(%s) unsupported options parameter, size = %d", name, size); + if (Memory::IsValid4AlignedAddress(optionsPtr)) { + u32 size = Memory::ReadUnchecked_U32(optionsPtr); + if (size > 4) + WARN_LOG_REPORT(Log::sceKernel, "sceKernelCreateMutex(%s) unsupported options parameter, size = %d", name, size); + } else { + Core_MemoryExceptionHLE(currentMIPS, optionsPtr, 4, MemoryExceptionType::HLE_READ); + } } if ((attr & ~PSP_MUTEX_ATTR_KNOWN) != 0) WARN_LOG_REPORT(Log::sceKernel, "sceKernelCreateMutex(%s) unsupported attr parameter: %08x", name, attr); @@ -327,8 +333,7 @@ int sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optio return hleLogDebug(Log::sceKernel, id); } -int sceKernelDeleteMutex(SceUID id) -{ +int sceKernelDeleteMutex(SceUID id) { u32 error; PSPMutex *mutex = kernelObjects.Get(id, error); if (!mutex) { @@ -365,8 +370,7 @@ static bool __KernelLockMutexCheck(PSPMutex *mutex, int count, u32 &error) { else if (count + mutex->nm.lockLevel < 0) error = SCE_MUTEX_ERROR_LOCK_OVERFLOW; // Only a recursive mutex can re-lock. - else if (mutex->nm.lockThread == __KernelGetCurThread()) - { + else if (mutex->nm.lockThread == __KernelGetCurThread()) { if (mutexIsRecursive) return true; @@ -422,14 +426,12 @@ static bool __KernelUnlockMutex(PSPMutex *mutex, u32 &error) { return wokeThreads; } -void __KernelMutexTimeout(u64 userdata, int cyclesLate) -{ +void __KernelMutexTimeout(u64 userdata, int cyclesLate) { SceUID threadID = (SceUID)userdata; HLEKernel::WaitExecTimeout(threadID); } -void __KernelMutexThreadEnd(SceUID threadID) -{ +void __KernelMutexThreadEnd(SceUID threadID) { u32 error; // If it was waiting on the mutex, it should finish now. @@ -457,11 +459,14 @@ void __KernelMutexThreadEnd(SceUID threadID) } } +// The timeoutPtr is assumed to be checked by the caller to either be 0 or valid. static void __KernelWaitMutex(PSPMutex *mutex, u32 timeoutPtr) { + _dbg_assert_(mutexWaitTimer != -1); // this could only come from extremely old savestates. + if (timeoutPtr == 0 || mutexWaitTimer == -1) return; - int micro = (int) Memory::Read_U32(timeoutPtr); + int micro = (int) Memory::ReadUnchecked_U32(timeoutPtr); // This happens to be how the hardware seems to time things. if (micro <= 3) @@ -473,8 +478,7 @@ static void __KernelWaitMutex(PSPMutex *mutex, u32 timeoutPtr) { CoreTiming::ScheduleEvent(usToCycles(micro), mutexWaitTimer, __KernelGetCurThread()); } -int sceKernelCancelMutex(SceUID uid, int count, u32 numWaitThreadsPtr) -{ +int sceKernelCancelMutex(SceUID uid, int count, u32 numWaitThreadsPtr) { u32 error; PSPMutex *mutex = kernelObjects.Get(uid, error); if (!mutex) { @@ -520,15 +524,20 @@ int sceKernelCancelMutex(SceUID uid, int count, u32 numWaitThreadsPtr) } } -// int sceKernelLockMutex(SceUID id, int count, int *timeout) -int sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr) -{ +int sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr) { // Tekken 6 hack: Let's avoid the unnecessary logspam. It does this on hardware too. // This ID is always invalid. if (id == 0x80020001 && timeoutPtr == 0) { return hleNoLog(0); } + if (timeoutPtr != 0) { + if (!Memory::IsValid4AlignedAddress(timeoutPtr)) { + Core_MemoryExceptionHLE(currentMIPS, timeoutPtr, 4, MemoryExceptionType::HLE_READ); + return hleNoLog(0); + } + } + u32 error; PSPMutex *mutex = kernelObjects.Get(id, error); @@ -554,9 +563,14 @@ int sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr) return hleLogDebug(Log::sceKernel, 0); } -// int sceKernelLockMutexCB(SceUID id, int count, int *timeout) -int sceKernelLockMutexCB(SceUID id, int count, u32 timeoutPtr) -{ +int sceKernelLockMutexCB(SceUID id, int count, u32 timeoutPtr) { + if (timeoutPtr != 0) { + if (!Memory::IsValid4AlignedAddress(timeoutPtr)) { + Core_MemoryExceptionHLE(currentMIPS, timeoutPtr, 4, MemoryExceptionType::HLE_READ); + return hleNoLog(0); + } + } + u32 error; PSPMutex *mutex = kernelObjects.Get(id, error); @@ -592,7 +606,6 @@ int sceKernelLockMutexCB(SceUID id, int count, u32 timeoutPtr) } } -// int sceKernelTryLockMutex(SceUID id, int count) int sceKernelTryLockMutex(SceUID id, int count) { u32 error; PSPMutex *mutex = kernelObjects.Get(id, error); @@ -605,9 +618,7 @@ int sceKernelTryLockMutex(SceUID id, int count) { return hleLogDebug(Log::sceKernel, SCE_MUTEX_ERROR_TRYLOCK_FAILED); } -// int sceKernelUnlockMutex(SceUID id, int count) -int sceKernelUnlockMutex(SceUID id, int count) -{ +int sceKernelUnlockMutex(SceUID id, int count) { // Tekken 6 hack: Let's avoid the unnecessary logspam. It does this on hardware too. // This ID is always invalid. if (id == 0x80020001) { @@ -698,11 +709,14 @@ int sceKernelCreateLwMutex(u32 workareaPtr, const char *name, u32 attr, int init workarea->attr = attr; workarea->uid = id; - if (optionsPtr != 0) - { - u32 size = Memory::Read_U32(optionsPtr); - if (size > 4) - WARN_LOG_REPORT(Log::sceKernel, "sceKernelCreateLwMutex(%s) unsupported options parameter, size = %d", name, size); + if (optionsPtr != 0) { + if (Memory::IsValid4AlignedAddress(optionsPtr)) { + u32 size = Memory::ReadUnchecked_U32(optionsPtr); + if (size > 4) + WARN_LOG_REPORT(Log::sceKernel, "sceKernelCreateLwMutex(%s) unsupported options parameter, size = %d", name, size); + } else { + Core_MemoryExceptionHLE(currentMIPS, optionsPtr, 4, MemoryExceptionType::HLE_READ); + } } if ((attr & ~PSP_MUTEX_ATTR_KNOWN) != 0) WARN_LOG_REPORT(Log::sceKernel, "sceKernelCreateLwMutex(%s) unsupported attr parameter: %08x", name, attr); @@ -711,8 +725,7 @@ int sceKernelCreateLwMutex(u32 workareaPtr, const char *name, u32 attr, int init } template -bool __KernelUnlockLwMutexForThread(LwMutex *mutex, T workarea, SceUID threadID, u32 &error, int result) -{ +bool __KernelUnlockLwMutexForThread(LwMutex *mutex, T workarea, SceUID threadID, u32 &error, int result) { if (!HLEKernel::VerifyWait(threadID, WAITTYPE_LWMUTEX, mutex->GetUID())) return false; @@ -723,9 +736,8 @@ bool __KernelUnlockLwMutexForThread(LwMutex *mutex, T workarea, SceUID threadID, workarea->lockThread = threadID; } - u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); - if (timeoutPtr != 0 && lwMutexWaitTimer != -1) - { + const u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); + if (timeoutPtr != 0 && lwMutexWaitTimer != -1) { // Remove any event for this thread. s64 cyclesLeft = CoreTiming::UnscheduleEvent(lwMutexWaitTimer, threadID); Memory::Write_U32((u32) cyclesToUs(cyclesLeft), timeoutPtr); @@ -763,8 +775,7 @@ int sceKernelDeleteLwMutex(u32 workareaPtr) { } } -static bool __KernelLockLwMutex(NativeLwMutexWorkarea *workarea, int count, u32 &error) -{ +static bool __KernelLockLwMutex(NativeLwMutexWorkarea *workarea, int count, u32 &error) { if (!error) { if (count <= 0) @@ -815,11 +826,9 @@ static bool __KernelLockLwMutex(NativeLwMutexWorkarea *workarea, int count, u32 } template -bool __KernelUnlockLwMutex(T workarea, u32 &error) -{ +bool __KernelUnlockLwMutex(T workarea, u32 &error) { LwMutex *mutex = kernelObjects.Get(workarea->uid, error); - if (error) - { + if (error) { workarea->lockThread = 0; return false; } @@ -849,12 +858,12 @@ void __KernelLwMutexTimeout(u64 userdata, int cyclesLate) HLEKernel::WaitExecTimeout(threadID); } -static void __KernelWaitLwMutex(LwMutex *mutex, u32 timeoutPtr) -{ +// timeoutPtr is assumed to be checked by the caller to either be 0 or valid. +static void __KernelWaitLwMutex(LwMutex *mutex, u32 timeoutPtr) { if (timeoutPtr == 0 || lwMutexWaitTimer == -1) return; - int micro = (int) Memory::Read_U32(timeoutPtr); + int micro = (int) Memory::ReadUnchecked_U32(timeoutPtr); // This happens to be how the hardware seems to time things. if (micro <= 3) @@ -889,8 +898,7 @@ void __KernelLwMutexEndCallback(SceUID threadID, SceUID prevCallbackId) DEBUG_LOG(Log::sceKernel, "sceKernelLockLwMutexCB: Resuming lock wait for callback"); } -int sceKernelTryLockLwMutex(u32 workareaPtr, int count) -{ +int sceKernelTryLockLwMutex(u32 workareaPtr, int count) { if (!Memory::IsValidAddress(workareaPtr)) { return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ACCESS_ERROR, "Bad workarea pointer for LwMutex"); } @@ -908,8 +916,7 @@ int sceKernelTryLockLwMutex(u32 workareaPtr, int count) return hleLogDebug(Log::sceKernel, SCE_MUTEX_ERROR_TRYLOCK_FAILED); } -int sceKernelTryLockLwMutex_600(u32 workareaPtr, int count) -{ +int sceKernelTryLockLwMutex_600(u32 workareaPtr, int count) { if (!Memory::IsValidAddress(workareaPtr)) { return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ACCESS_ERROR, "Bad workarea pointer for LwMutex"); } @@ -926,22 +933,27 @@ int sceKernelTryLockLwMutex_600(u32 workareaPtr, int count) return hleLogDebug(Log::sceKernel, SCE_LWMUTEX_ERROR_TRYLOCK_FAILED); } -int sceKernelLockLwMutex(u32 workareaPtr, int count, u32 timeoutPtr) -{ +int sceKernelLockLwMutex(u32 workareaPtr, int count, u32 timeoutPtr) { if (!Memory::IsValidAddress(workareaPtr)) { return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ACCESS_ERROR, "Bad workarea pointer for LwMutex"); } + if (timeoutPtr) { + if (!Memory::IsValid4AlignedAddress(timeoutPtr)) { + Core_MemoryExceptionHLE(currentMIPS, timeoutPtr, 4, MemoryExceptionType::HLE_READ); + return hleNoLog(0); + } + } + auto workarea = PSPPointer::Create(workareaPtr); hleEatCycles(48); u32 error = 0; - if (__KernelLockLwMutex(workarea, count, error)) + if (__KernelLockLwMutex(workarea, count, error)) { return hleLogVerbose(Log::sceKernel, 0); - else if (error) + } else if (error) { return hleLogVerbose(Log::sceKernel, error); - else - { + } else { LwMutex *mutex = kernelObjects.Get(workarea->uid, error); if (!mutex) { return hleLogError(Log::sceKernel, error); @@ -959,24 +971,27 @@ int sceKernelLockLwMutex(u32 workareaPtr, int count, u32 timeoutPtr) } } -int sceKernelLockLwMutexCB(u32 workareaPtr, int count, u32 timeoutPtr) -{ - VERBOSE_LOG(Log::sceKernel, "sceKernelLockLwMutexCB(%08x, %i, %08x)", workareaPtr, count, timeoutPtr); - +int sceKernelLockLwMutexCB(u32 workareaPtr, int count, u32 timeoutPtr) { if (!Memory::IsValidAddress(workareaPtr)) { return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ACCESS_ERROR, "Bad workarea pointer for LwMutex"); } + if (timeoutPtr) { + if (!Memory::IsValid4AlignedAddress(timeoutPtr)) { + Core_MemoryExceptionHLE(currentMIPS, timeoutPtr, 4, MemoryExceptionType::HLE_READ); + return hleNoLog(0); + } + } + auto workarea = PSPPointer::Create(workareaPtr); hleEatCycles(48); u32 error = 0; - if (__KernelLockLwMutex(workarea, count, error)) + if (__KernelLockLwMutex(workarea, count, error)) { return hleLogVerbose(Log::sceKernel, 0); - else if (error) + } else if (error) { return hleLogVerbose(Log::sceKernel, error); - else - { + } else { LwMutex *mutex = kernelObjects.Get(workarea->uid, error); if (!mutex) { return hleLogError(Log::sceKernel, error); @@ -994,8 +1009,7 @@ int sceKernelLockLwMutexCB(u32 workareaPtr, int count, u32 timeoutPtr) } } -int sceKernelUnlockLwMutex(u32 workareaPtr, int count) -{ +int sceKernelUnlockLwMutex(u32 workareaPtr, int count) { if (!Memory::IsValidAddress(workareaPtr)) { return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ACCESS_ERROR, "Bad workarea pointer for LwMutex"); } @@ -1018,8 +1032,7 @@ int sceKernelUnlockLwMutex(u32 workareaPtr, int count) workarea->lockLevel -= count; - if (workarea->lockLevel == 0) - { + if (workarea->lockLevel == 0) { u32 error; if (__KernelUnlockLwMutex(workarea, error)) hleReSchedule("lwmutex unlocked"); @@ -1032,16 +1045,17 @@ int sceKernelUnlockLwMutex(u32 workareaPtr, int count) static int __KernelReferLwMutexStatus(SceUID uid, u32 infoPtr) { u32 error; LwMutex *m = kernelObjects.Get(uid, error); - if (!m) + if (!m) { return hleLogError(Log::sceKernel, error, "invalid id"); + } // Should we crash the thread somehow? auto info = PSPPointer::Create(infoPtr); - if (!info.IsValid()) + if (!info.IsValid()) { return hleLogError(Log::sceKernel, -1, "invalid pointer"); + } - if (info->size != 0) - { + if (info->size != 0) { auto workarea = m->nm.workarea; HLEKernel::CleanupWaitingThreads(WAITTYPE_LWMUTEX, uid, m->waitingThreads); diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 567377b624..451201401c 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -28,6 +28,7 @@ #include "Common/Serialize/SerializeFuncs.h" #include "Common/Serialize/SerializeList.h" #include "Common/Serialize/SerializeMap.h" +#include "Core/Core.h" #include "Core/HLE/HLE.h" #include "Core/HLE/ErrorCodes.h" #include "Core/HLE/HLETables.h" @@ -357,8 +358,7 @@ bool PSPThread::AllocateStack(u32 &stackSize) { bool fromTop = (nt.attr & PSP_THREAD_ATTR_LOW_STACK) == 0; currentStack.start = StackAllocator().Alloc(stackSize, fromTop, StringFromFormat("stack/%s", nt.name).c_str()); - if (currentStack.start == (u32)-1) - { + if (currentStack.start == (u32)-1) { currentStack.start = 0; nt.initialStack = 0; ERROR_LOG(Log::sceKernel, "Failed to allocate stack for thread"); @@ -561,19 +561,16 @@ static u64 lastSwitchCycles = 0; //STATE END ////////////////////////////////////////////////////////////////////////// -int __KernelRegisterActionType(ActionCreator creator) -{ +int __KernelRegisterActionType(ActionCreator creator) { return mipsCalls.registerActionType(creator); } -void __KernelRestoreActionType(int actionType, ActionCreator creator) -{ +void __KernelRestoreActionType(int actionType, ActionCreator creator) { _assert_(actionType >= 0); mipsCalls.restoreActionType(actionType, creator); } -PSPAction *__KernelCreateAction(int actionType) -{ +PSPAction *__KernelCreateAction(int actionType) { return mipsCalls.createActionByType(actionType); } @@ -611,13 +608,11 @@ void MipsCall::DoState(PointerWrap &p) } } -void MipsCall::setReturnValue(u32 value) -{ +void MipsCall::setReturnValue(u32 value) { savedV0 = value; } -void MipsCall::setReturnValue(u64 value) -{ +void MipsCall::setReturnValue(u64 value) { savedV0 = value & 0xFFFFFFFF; savedV1 = (value >> 32) & 0xFFFFFFFF; } @@ -670,10 +665,9 @@ static void __KernelDelayEndCallback(SceUID threadID, SceUID prevCallbackId) { // TODO: Don't wake up if __KernelCurHasReadyCallbacks()? s64 cyclesLeft = delayDeadline - CoreTiming::GetTicks(); - if (cyclesLeft < 0) + if (cyclesLeft < 0) { __KernelResumeThreadFromWait(threadID, 0); - else - { + } else { CoreTiming::ScheduleEvent(cyclesLeft, eventScheduledWakeup, __KernelGetCurThread()); DEBUG_LOG(Log::sceKernel, "sceKernelDelayThreadCB: Resuming delay after callback"); } @@ -703,8 +697,7 @@ static void __KernelSleepEndCallback(SceUID threadID, SceUID prevCallbackId) { } } -static void __KernelThreadEndBeginCallback(SceUID threadID, SceUID prevCallbackId) -{ +static void __KernelThreadEndBeginCallback(SceUID threadID, SceUID prevCallbackId) { auto result = HLEKernel::WaitBeginCallback(threadID, prevCallbackId, eventThreadEndTimeout); if (result == HLEKernel::WAIT_CB_SUCCESS) DEBUG_LOG(Log::sceKernel, "sceKernelWaitThreadEndCB: Suspending wait for callback"); @@ -738,11 +731,9 @@ static void __KernelThreadEndEndCallback(SceUID threadID, SceUID prevCallbackId) DEBUG_LOG(Log::sceKernel, "sceKernelWaitThreadEndCB: Resuming wait from callback"); } -u32 __KernelSetThreadRA(SceUID threadID, u32 nid) -{ +u32 __KernelSetThreadRA(SceUID threadID, u32 nid) { u32 newRA; - switch (nid) - { + switch (nid) { case NID_MODULERETURN: newRA = moduleReturnHackAddr; break; @@ -751,10 +742,9 @@ u32 __KernelSetThreadRA(SceUID threadID, u32 nid) return -1; } - if (threadID == currentThread) + if (threadID == currentThread) { currentMIPS->r[MIPS_REG_RA] = newRA; - else - { + } else { u32 error; PSPThread *thread = kernelObjects.Get(threadID, error); if (!thread) @@ -769,8 +759,7 @@ u32 __KernelSetThreadRA(SceUID threadID, u32 nid) void hleScheduledWakeup(u64 userdata, int cyclesLate); void hleThreadEndTimeout(u64 userdata, int cyclesLate); -static void __KernelWriteFakeSysCall(u32 nid, u32 *ptr, u32 &pos) -{ +static void __KernelWriteFakeSysCall(u32 nid, u32 *ptr, u32 &pos) { *ptr = pos; pos += 8; WriteHLESyscall("FakeSysCalls", nid, *ptr); @@ -786,16 +775,14 @@ u32 HLEMipsCallReturnAddress() { return hleReturnHackAddr; } -void __KernelThreadingInit() -{ - struct ThreadHack - { +void __KernelThreadingInit() { + struct ThreadHack { u32 nid; u32 *addr; }; // Yeah, this is straight out of JPCSP, I should be ashamed. - const static u32_le idleThreadCode[] = { + static const u32_le idleThreadCode[] = { MIPS_MAKE_LUI(MIPS_REG_RA, 0x0800), MIPS_MAKE_JR_RA(), MIPS_MAKE_SYSCALL("FakeSysCalls", "_sceKernelIdle"), @@ -803,7 +790,7 @@ void __KernelThreadingInit() }; // If you add another func here, don't forget __KernelThreadingDoState() below. - static ThreadHack threadHacks[] = { + static const ThreadHack threadHacks[] = { {NID_THREADRETURN, &threadReturnHackAddr}, {NID_CALLBACKRETURN, &cbReturnHackAddr}, {NID_INTERRUPTRETURN, &intReturnHackAddr}, @@ -905,32 +892,26 @@ void __KernelThreadingDoState(PointerWrap &p) Do(p, pendingDeleteThreads); } -void __KernelThreadingDoStateLate(PointerWrap &p) -{ +void __KernelThreadingDoStateLate(PointerWrap &p) { // We do this late to give modules time to register actions. mipsCalls.DoState(p); p.DoMarker("sceKernelThread Late"); } -KernelObject *__KernelThreadObject() -{ - return new PSPThread; +KernelObject *__KernelThreadObject() { + return new PSPThread(); } -KernelObject *__KernelCallbackObject() -{ - return new PSPCallback; +KernelObject *__KernelCallbackObject() { + return new PSPCallback(); } -void __KernelListenThreadEnd(ThreadCallback callback) -{ +void __KernelListenThreadEnd(ThreadCallback callback) { threadEndListeners.push_back(callback); } -static void __KernelFireThreadEnd(SceUID threadID) -{ - for (auto iter = threadEndListeners.begin(), end = threadEndListeners.end(); iter != end; ++iter) - { +static void __KernelFireThreadEnd(SceUID threadID) { + for (auto iter = threadEndListeners.begin(), end = threadEndListeners.end(); iter != end; ++iter) { ThreadCallback cb = *iter; cb(threadID); } @@ -957,8 +938,7 @@ static void __KernelChangeReadyState(PSPThread *thread, SceUID threadID, bool re } } -static void __KernelChangeReadyState(SceUID threadID, bool ready) -{ +static void __KernelChangeReadyState(SceUID threadID, bool ready) { u32 error; PSPThread *thread = kernelObjects.Get(threadID, error); if (thread) @@ -967,10 +947,8 @@ static void __KernelChangeReadyState(SceUID threadID, bool ready) WARN_LOG(Log::sceKernel, "Trying to change the ready state of an unknown thread?"); } -void __KernelStartIdleThreads(SceUID moduleId) -{ - for (int i = 0; i < 2; i++) - { +void __KernelStartIdleThreads(SceUID moduleId) { + for (int i = 0; i < 2; i++) { u32 error; PSPThread *t = kernelObjects.Get(threadIdleID[i], error); t->nt.gpreg = __KernelGetModuleGP(moduleId); @@ -987,8 +965,7 @@ void KernelValidateThreadTarget(uint32_t pc) { } } -bool __KernelSwitchOffThread(const char *reason) -{ +bool __KernelSwitchOffThread(const char *reason) { if (!reason) reason = "switch off thread"; @@ -1155,8 +1132,7 @@ SceUID __KernelGetCurrentCallbackID(SceUID threadID, u32 &error) { } } -u32 sceKernelReferThreadStatus(u32 threadID, u32 statusPtr) -{ +u32 sceKernelReferThreadStatus(u32 threadID, u32 statusPtr) { static const u32 THREADINFO_SIZE = 104; static const u32 THREADINFO_SIZE_AFTER_260 = 108; @@ -1171,7 +1147,12 @@ u32 sceKernelReferThreadStatus(u32 threadID, u32 statusPtr) return hleLogError(Log::sceKernel, error, "bad thread"); } - u32 wantedSize = Memory::Read_U32(statusPtr); + if (!Memory::IsValid4AlignedAddress(statusPtr)) { + Core_MemoryExceptionHLE(currentMIPS, statusPtr, 0, MemoryExceptionType::HLE_READ); + return hleNoLog(0); + } + + u32 wantedSize = Memory::ReadUnchecked_U32(statusPtr); if (sceKernelGetCompiledSdkVersion() > 0x02060010) { if (wantedSize > THREADINFO_SIZE_AFTER_260) { @@ -1199,8 +1180,7 @@ u32 sceKernelReferThreadStatus(u32 threadID, u32 statusPtr) } // Thanks JPCSP -u32 sceKernelReferThreadRunStatus(u32 threadID, u32 statusPtr) -{ +u32 sceKernelReferThreadRunStatus(u32 threadID, u32 statusPtr) { if (threadID == 0) threadID = __KernelGetCurThread(); @@ -1210,8 +1190,10 @@ u32 sceKernelReferThreadRunStatus(u32 threadID, u32 statusPtr) return hleLogError(Log::sceKernel, error, "bad thread"); } - if (!Memory::IsValidAddress(statusPtr)) + if (!Memory::IsValidRange(statusPtr, sizeof(SceKernelThreadRunStatus))) { + // Raise exception? return hleLogError(Log::sceKernel, -1); + } auto runStatus = PSPPointer::Create(statusPtr); @@ -2574,7 +2556,7 @@ int sceKernelWaitThreadEnd(SceUID threadID, u32 timeoutPtr) { if (t->nt.status != THREADSTATUS_DORMANT) { if (Memory::IsValidAddress(timeoutPtr)) - __KernelScheduleThreadEndTimeout(currentThread, threadID, Memory::Read_U32(timeoutPtr)); + __KernelScheduleThreadEndTimeout(currentThread, threadID, Memory::ReadUnchecked_U32(timeoutPtr)); if (std::find(t->waitingThreads.begin(), t->waitingThreads.end(), currentThread) == t->waitingThreads.end()) t->waitingThreads.push_back(currentThread); __KernelWaitCurThread(WAITTYPE_THREADEND, threadID, 0, timeoutPtr, false, "thread wait end"); @@ -2601,7 +2583,7 @@ int sceKernelWaitThreadEndCB(SceUID threadID, u32 timeoutPtr) { if (t->nt.status != THREADSTATUS_DORMANT) { if (Memory::IsValidAddress(timeoutPtr)) - __KernelScheduleThreadEndTimeout(currentThread, threadID, Memory::Read_U32(timeoutPtr)); + __KernelScheduleThreadEndTimeout(currentThread, threadID, Memory::ReadUnchecked_U32(timeoutPtr)); if (std::find(t->waitingThreads.begin(), t->waitingThreads.end(), currentThread) == t->waitingThreads.end()) t->waitingThreads.push_back(currentThread); __KernelWaitCurThread(WAITTYPE_THREADEND, threadID, 0, timeoutPtr, true, "thread wait end"); @@ -2724,8 +2706,7 @@ SceUID sceKernelCreateCallback(const char *name, u32 entrypoint, u32 signalArg) return hleLogInfo(Log::sceKernel, id); } -int sceKernelDeleteCallback(SceUID cbId) -{ +int sceKernelDeleteCallback(SceUID cbId) { u32 error; PSPCallback *cb = kernelObjects.Get(cbId, error); if (cb) @@ -2743,8 +2724,7 @@ int sceKernelDeleteCallback(SceUID cbId) } // Generally very rarely used, but Numblast uses it like candy. -int sceKernelNotifyCallback(SceUID cbId, int notifyArg) -{ +int sceKernelNotifyCallback(SceUID cbId, int notifyArg) { u32 error; PSPCallback *cb = kernelObjects.Get(cbId, error); if (cb) { @@ -2755,8 +2735,7 @@ int sceKernelNotifyCallback(SceUID cbId, int notifyArg) } } -int sceKernelCancelCallback(SceUID cbId) -{ +int sceKernelCancelCallback(SceUID cbId) { u32 error; PSPCallback *cb = kernelObjects.Get(cbId, error); if (cb) { @@ -2768,8 +2747,7 @@ int sceKernelCancelCallback(SceUID cbId) } } -int sceKernelGetCallbackCount(SceUID cbId) -{ +int sceKernelGetCallbackCount(SceUID cbId) { u32 error; PSPCallback *cb = kernelObjects.Get(cbId, error); if (cb) { @@ -2796,8 +2774,7 @@ int sceKernelReferCallbackStatus(SceUID cbId, u32 statusAddr) { } } -u32 sceKernelExtendThreadStack(u32 size, u32 entryAddr, u32 entryParameter) -{ +u32 sceKernelExtendThreadStack(u32 size, u32 entryAddr, u32 entryParameter) { if (size < 512) { return hleReportError(Log::sceKernel, SCE_KERNEL_ERROR_ILLEGAL_STACK_SIZE, "stack size too small"); } @@ -2828,25 +2805,28 @@ u32 sceKernelExtendThreadStack(u32 size, u32 entryAddr, u32 entryParameter) return hleLogDebug(Log::sceKernel, 0); } -void __KernelReturnFromExtendStack() -{ +void __KernelReturnFromExtendStack() { hleSkipDeadbeef(); PSPThread *thread = __GetCurrentThread(); - if (!thread) - { + if (!thread) { ERROR_LOG_REPORT(Log::sceKernel, "__KernelReturnFromExtendStack() - not on a thread?"); hleNoLogVoid(); return; } - // Grab the saved regs at the top of the stack. - u32 restoreRA = Memory::Read_U32(thread->currentStack.end - 4); - u32 restoreSP = Memory::Read_U32(thread->currentStack.end - 8); - u32 restorePC = Memory::Read_U32(thread->currentStack.end - 12); + if (!Memory::IsValid4AlignedRange(thread->currentStack.end - 12, 12)) { + Core_MemoryExceptionHLE(currentMIPS, thread->currentStack.end, 12, MemoryExceptionType::HLE_READ); + hleNoLogVoid(); + return; + } - if (!thread->PopExtendedStack()) - { + // Grab the saved regs at the top of the stack. + u32 restoreRA = Memory::ReadUnchecked_U32(thread->currentStack.end - 4); + u32 restoreSP = Memory::ReadUnchecked_U32(thread->currentStack.end - 8); + u32 restorePC = Memory::ReadUnchecked_U32(thread->currentStack.end - 12); + + if (!thread->PopExtendedStack()) { ERROR_LOG_REPORT(Log::sceKernel, "__KernelReturnFromExtendStack() - no stack to restore?"); return; } @@ -2944,16 +2924,15 @@ void __KernelSwitchContext(PSPThread *target, const char *reason) { __KernelChangeReadyState(cur, oldUID, true); } - if (target) - { + if (target) { __SetCurrentThread(target, target->GetUID(), target->nt.name); __KernelChangeReadyState(target, currentThread, false); target->nt.status = (target->nt.status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; __KernelLoadContext(&target->context, (target->nt.attr & PSP_THREAD_ATTR_VFPU) != 0); - } - else + } else { __SetCurrentThread(NULL, 0, NULL); + } const bool fromIdle = oldUID == threadIdleID[0] || oldUID == threadIdleID[1]; const bool toIdle = currentThread == threadIdleID[0] || currentThread == threadIdleID[1]; @@ -3187,14 +3166,21 @@ void __KernelReturnFromMipsCall() { call->doAfter = nullptr; } - u32 &sp = currentMIPS->r[MIPS_REG_SP]; - for (int i = MIPS_REG_A0; i <= MIPS_REG_T7; ++i) { - currentMIPS->r[i] = Memory::Read_U32(sp + i * 4); + u32 sp = currentMIPS->r[MIPS_REG_SP]; + if (!Memory::IsValid4AlignedRange(sp, 32 * 4)) { + // We're really screwed. + Core_MemoryExceptionHLE(currentMIPS, sp, 4, MemoryExceptionType::HLE_READ); + return hleNoLogVoid(); } - currentMIPS->r[MIPS_REG_T8] = Memory::Read_U32(sp + MIPS_REG_T8 * 4); - currentMIPS->r[MIPS_REG_T9] = Memory::Read_U32(sp + MIPS_REG_T9 * 4); - currentMIPS->r[MIPS_REG_RA] = Memory::Read_U32(sp + MIPS_REG_RA * 4); - sp += 32 * 4; + + for (int i = MIPS_REG_A0; i <= MIPS_REG_T7; ++i) { + currentMIPS->r[i] = Memory::ReadUnchecked_U32(sp + i * 4); + } + currentMIPS->r[MIPS_REG_T8] = Memory::ReadUnchecked_U32(sp + MIPS_REG_T8 * 4); + currentMIPS->r[MIPS_REG_T9] = Memory::ReadUnchecked_U32(sp + MIPS_REG_T9 * 4); + currentMIPS->r[MIPS_REG_RA] = Memory::ReadUnchecked_U32(sp + MIPS_REG_RA * 4); + // Increment SP. + currentMIPS->r[MIPS_REG_SP] += 32 * 4; KernelValidateThreadTarget(call->savedPc); @@ -3210,15 +3196,11 @@ void __KernelReturnFromMipsCall() { } currentCallbackThreadID = 0; - if (cur->nt.waitType != WAITTYPE_NONE) - { - if (call->cbId > 0) - { - if (waitTypeFuncs[cur->nt.waitType].endFunc != NULL) - waitTypeFuncs[cur->nt.waitType].endFunc(cur->GetUID(), cur->currentCallbackId); - else - ERROR_LOG_REPORT(Log::HLE, "Missing begin/restore funcs for wait type %d", cur->nt.waitType); - } + if (cur->nt.waitType != WAITTYPE_NONE && call->cbId > 0) { + if (waitTypeFuncs[cur->nt.waitType].endFunc != NULL) + waitTypeFuncs[cur->nt.waitType].endFunc(cur->GetUID(), cur->currentCallbackId); + else + ERROR_LOG_REPORT(Log::HLE, "Missing begin/restore funcs for wait type %d", cur->nt.waitType); } // yeah! back in the real world, let's keep going. Should we process more callbacks? @@ -3506,12 +3488,10 @@ void __KernelChangeThreadState(SceUID threadId, ThreadStatus newStatus) { __KernelChangeThreadState(t, newStatus); } -int sceKernelRegisterExitCallback(SceUID cbId) -{ +int sceKernelRegisterExitCallback(SceUID cbId) { u32 error; PSPCallback *cb = kernelObjects.Get(cbId, error); - if (!cb) - { + if (!cb) { WARN_LOG(Log::sceKernel, "sceKernelRegisterExitCallback(%i): invalid callback id", cbId); if (sceKernelGetCompiledSdkVersion() >= 0x3090500) return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT); @@ -3568,8 +3548,7 @@ bool __KernelIsExitCallbackPending() { } // Update the exit callback status? -int LoadExecForUser_362A956B() -{ +int LoadExecForUser_362A956B() { WARN_LOG_REPORT(Log::sceKernel, "LoadExecForUser_362A956B()"); u32 error; PSPCallback *cb = kernelObjects.Get(registeredExitCbId, error); @@ -3577,24 +3556,23 @@ int LoadExecForUser_362A956B() return hleLogWarning(Log::sceKernel, SCE_KERNEL_ERROR_UNKNOWN_CBID, "registeredExitCbId not found 0x%x", registeredExitCbId); } int cbArg = cb->nc.commonArgument; - if (!Memory::IsValidAddress(cbArg)) { + if (!Memory::IsValidRange(cbArg - 8, 8)) { return hleLogWarning(Log::sceKernel, SCE_KERNEL_ERROR_ILLEGAL_ADDR, "invalid address for cbArg (0x%08X)", cbArg); } - u32 unknown1 = Memory::Read_U32(cbArg - 8); + const u32 unknown1 = Memory::ReadUnchecked_U32(cbArg - 8); if (unknown1 >= 4) { return hleLogWarning(Log::sceKernel, SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, "invalid value unknown1 (0x%08X)", unknown1); } - u32 parameterArea = Memory::Read_U32(cbArg - 4); - if (!Memory::IsValidAddress(parameterArea)) { + const u32 parameterArea = Memory::ReadUnchecked_U32(cbArg - 4); + if (!Memory::IsValid4AlignedRange(parameterArea, 12)) { return hleLogWarning(Log::sceKernel, SCE_KERNEL_ERROR_ILLEGAL_ADDR, "invalid address for parameterArea on userMemory (0x%08X)", parameterArea); } - - u32 size = Memory::Read_U32(parameterArea); + const u32 size = Memory::ReadUnchecked_U32(parameterArea); if (size < 12) { return hleLogWarning(Log::sceKernel, SCE_KERNEL_ERROR_ILLEGAL_SIZE, "invalid parameterArea size %d", size); } - Memory::Write_U32(0, parameterArea + 4); - Memory::Write_U32(-1, parameterArea + 8); + Memory::WriteUnchecked_U32(0, parameterArea + 4); + Memory::WriteUnchecked_U32(-1, parameterArea + 8); return hleLogDebug(Log::sceKernel, 0); } @@ -3630,7 +3608,7 @@ struct ThreadEventHandler : public KernelObject { KernelObject *__KernelThreadEventHandlerObject() { // Default object to load from state. - return new ThreadEventHandler; + return new ThreadEventHandler(); } bool __KernelThreadTriggerEvent(const ThreadEventHandlerList &handlers, SceUID threadID, ThreadEventType type) { diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 10377422af..6d8e9c3961 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -1642,7 +1642,7 @@ static int sceMpegGetAvcAu(u32 mpeg, u32 streamId, u32 auAddr, u32 attrAddr) avcAu.write(auAddr); if (result == 0) { - // Jeanne d'Arc return 00000000 as attrAddr here and cause WriteToHardware error + // Jeanne d'Arc return 00000000 as attrAddr here and cause WriteMemoryOrRaiseException error if (Memory::IsValidAddress(attrAddr)) { Memory::Write_U32(1, attrAddr); } @@ -1742,7 +1742,7 @@ static int sceMpegGetAtracAu(u32 mpeg, u32 streamId, u32 auAddr, u32 attrAddr) atracAu.write(auAddr); if (result == 0) { - // 3rd birthday return 00000000 as attrAddr here and cause WriteToHardware error + // 3rd birthday return 00000000 as attrAddr here and cause WriteMemoryOrRaiseException error if (Memory::IsValidAddress(attrAddr)) { Memory::Write_U32(0, attrAddr); } diff --git a/Core/MIPS/x86/CompBranch.cpp b/Core/MIPS/x86/CompBranch.cpp index 138b11147f..c847c342ed 100644 --- a/Core/MIPS/x86/CompBranch.cpp +++ b/Core/MIPS/x86/CompBranch.cpp @@ -524,7 +524,7 @@ void Jit::Comp_Jump(MIPSOpcode op) { // Might be a stubbed address or something? if (!Memory::IsValidAddress(targetAddr) || (targetAddr & 3) != 0) { if (js.nextExit == 0) { - ERROR_LOG(Log::JIT, "Jump to invalid address: %08x PC %08x LR %08x", targetAddr, GetCompilerPC(), currentMIPS->r[MIPS_REG_RA]); + ERROR_LOG(Log::JIT, "Jump to invalid address: %08x PC %08x RA %08x", targetAddr, GetCompilerPC(), currentMIPS->r[MIPS_REG_RA]); } else { js.compiling = false; } @@ -562,8 +562,7 @@ void Jit::Comp_Jump(MIPSOpcode op) { js.compiling = false; } -void Jit::Comp_JumpReg(MIPSOpcode op) -{ +void Jit::Comp_JumpReg(MIPSOpcode op) { CONDITIONAL_LOG; if (js.inDelaySlot) { ERROR_LOG_REPORT(Log::JIT, "Branch in JumpReg delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart); diff --git a/Core/MemFault.cpp b/Core/MemFault.cpp index 29d5927a55..cc584fbab7 100644 --- a/Core/MemFault.cpp +++ b/Core/MemFault.cpp @@ -320,7 +320,7 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) { uint32_t approximatePC = currentMIPS->pc; // TODO: Determine access size from the disassembled native instruction. We have some partial info already, // just need to clean it up. - Core_MemoryException(guestAddress, 0, approximatePC, type, infoString, true); + Core_MemoryException(guestAddress, 0, approximatePC, type, infoString); // There's a small chance we can resume from this type of crash. g_lastCrashAddress = codePtr; @@ -373,6 +373,10 @@ std::vector WalkCurrentStack(int threadID) { std::string FormatStackTrace(const std::vector &frames) { std::stringstream str; for (const auto &frame : frames) { + if (frame.pc == 0xFFFFFFFF) { + // Bottom of stack, probably. + continue; + } std::string desc = g_symbolMap->GetDescription(frame.entry); char moduleDesc[96]; if (DescribeKernelModuleAddress(frame.entry, moduleDesc, sizeof(moduleDesc))) { diff --git a/Core/MemMapFunctions.cpp b/Core/MemMapFunctions.cpp index f8a57f3f70..756f4957c3 100644 --- a/Core/MemMapFunctions.cpp +++ b/Core/MemMapFunctions.cpp @@ -86,7 +86,7 @@ const u8 *GetPointerRange(const u32 address, const u32 size) { } template -inline void ReadFromHardware(T &var, const u32 address) { +inline void ReadMemoryOrRaiseException(T &var, const u32 address) { if ((address & 0x3E000000) == 0x08000000 || // RAM (address & 0xBF800000) == 0x04000000 || // VRAM (address & 0xBFFFC000) == 0x00010000 || // Scratchpad @@ -99,7 +99,7 @@ inline void ReadFromHardware(T &var, const u32 address) { } template -inline void WriteToHardware(u32 address, const T data) { +inline void WriteMemoryOrRaiseException(u32 address, const T data) { if ((address & 0x3E000000) == 0x08000000 || // RAM (address & 0xBF800000) == 0x04000000 || // VRAM (address & 0xBFFFC000) == 0x00010000 || // Scratchpad @@ -126,25 +126,25 @@ bool IsScratchpadAddress(const u32 address) { u8 Read_U8(const u32 address) { u8 value = 0; - ReadFromHardware(value, address); + ReadMemoryOrRaiseException(value, address); return (u8)value; } u16 Read_U16(const u32 address) { u16_le value = 0; - ReadFromHardware(value, address); + ReadMemoryOrRaiseException(value, address); return (u16)value; } u32 Read_U32(const u32 address) { u32_le value = 0; - ReadFromHardware(value, address); + ReadMemoryOrRaiseException(value, address); return value; } u64 Read_U64(const u32 address) { u64_le value = 0; - ReadFromHardware(value, address); + ReadMemoryOrRaiseException(value, address); return value; } @@ -157,19 +157,19 @@ u32 Read_U16_ZX(const u32 address) { } void Write_U8(const u8 _Data, const u32 address) { - WriteToHardware(address, _Data); + WriteMemoryOrRaiseException(address, _Data); } void Write_U16(const u16 _Data, const u32 address) { - WriteToHardware(address, _Data); + WriteMemoryOrRaiseException(address, _Data); } void Write_U32(const u32 _Data, const u32 address) { - WriteToHardware(address, _Data); + WriteMemoryOrRaiseException(address, _Data); } void Write_U64(const u64 _Data, const u32 address) { - WriteToHardware(address, _Data); + WriteMemoryOrRaiseException(address, _Data); } } // namespace Memory diff --git a/GPU/Debugger/Record.cpp b/GPU/Debugger/Record.cpp index ba85a5c2da..9d92296a28 100644 --- a/GPU/Debugger/Record.cpp +++ b/GPU/Debugger/Record.cpp @@ -628,8 +628,13 @@ void Recorder::NotifyCommand(u32 pc) { return; } + if (!Memory::IsValid4AlignedAddress(pc)) { + ERROR_LOG(Log::G3D, "Bad pc in Recorder: %08x", pc); + return; + } + CheckEdramTrans(); - const u32 op = Memory::Read_U32(pc); + const u32 op = Memory::ReadUnchecked_U32(pc); const GECommand cmd = GECommand(op >> 24); switch (cmd) { diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index f4e4f0992c..43b6819292 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -1576,7 +1576,12 @@ int GPUCommon::GetCurrentPrim(GEPrimitiveType *prim, GECommand *outCmd) const { DisplayList list; u32 cmdWord; if (GetCurrentDisplayList(list)) { - cmdWord = Memory::Read_U32(list.pc); + if (Memory::IsValid4AlignedAddress(list.pc)) { + cmdWord = Memory::ReadUnchecked_U32(list.pc); + } else { + // We are screwed. + return 0; + } } else { // Current prim value. cmdWord = gstate.cmdmem[GE_CMD_PRIM]; diff --git a/UI/ImDebugger/ImDisasmView.cpp b/UI/ImDebugger/ImDisasmView.cpp index a72461953a..b2384761eb 100644 --- a/UI/ImDebugger/ImDisasmView.cpp +++ b/UI/ImDebugger/ImDisasmView.cpp @@ -896,21 +896,21 @@ void ImDisasmView::updateStatusBarText() { } if (line.info.isDataAccess) { - if (!Memory::IsValidAddress(line.info.dataAddress)) { - snprintf(text, sizeof(text), "Invalid address %08X", line.info.dataAddress); + if (!Memory::IsValidRange(line.info.dataAddress, line.info.dataSize)) { + snprintf(text, sizeof(text), "Invalid address range %08X (size %d)", line.info.dataAddress, line.info.dataSize); } else { bool isFloat = MIPSGetInfo(line.info.encodedOpcode) & (IS_FPU | IS_VFPU); switch (line.info.dataSize) { case 1: - snprintf(text, sizeof(text), "[%08X] = %02X", line.info.dataAddress, Memory::Read_U8(line.info.dataAddress)); + snprintf(text, sizeof(text), "[%08X] = %02X", line.info.dataAddress, Memory::ReadUnchecked_U8(line.info.dataAddress)); break; case 2: - snprintf(text, sizeof(text), "[%08X] = %04X", line.info.dataAddress, Memory::Read_U16(line.info.dataAddress)); + snprintf(text, sizeof(text), "[%08X] = %04X", line.info.dataAddress, Memory::ReadUnchecked_U16(line.info.dataAddress)); break; case 4: { - u32 dataInt = Memory::Read_U32(line.info.dataAddress); - u32 dataFloat = Memory::Read_Float(line.info.dataAddress); + u32 dataInt = Memory::ReadUnchecked_U32(line.info.dataAddress); + u32 dataFloat = Memory::ReadUnchecked_Float(line.info.dataAddress); std::string dataString; if (isFloat) dataString = StringFromFormat("%08X / %f", dataInt, dataFloat); @@ -930,8 +930,8 @@ void ImDisasmView::updateStatusBarText() { uint32_t dataInt[4]; float dataFloat[4]; for (int i = 0; i < 4; ++i) { - dataInt[i] = Memory::Read_U32(line.info.dataAddress + i * 4); - dataFloat[i] = Memory::Read_Float(line.info.dataAddress + i * 4); + dataInt[i] = Memory::ReadUnchecked_U32(line.info.dataAddress + i * 4); + dataFloat[i] = Memory::ReadUnchecked_Float(line.info.dataAddress + i * 4); } std::string dataIntString = StringFromFormat("%08X,%08X,%08X,%08X", dataInt[0], dataInt[1], dataInt[2], dataInt[3]); std::string dataFloatString = StringFromFormat("%f,%f,%f,%f", dataFloat[0], dataFloat[1], dataFloat[2], dataFloat[3]); diff --git a/UI/ImDebugger/ImStructViewer.cpp b/UI/ImDebugger/ImStructViewer.cpp index 9cb88e92fb..e45d1a1085 100644 --- a/UI/ImDebugger/ImStructViewer.cpp +++ b/UI/ImDebugger/ImStructViewer.cpp @@ -635,6 +635,10 @@ void ImStructViewer::DrawType( } const u32 address = base + offset; + if (!Memory::IsValidAddress(address)) { + // Bad! + return; + } ImGui::PushID(static_cast(address)); ImGui::PushID(watchId); // We push watch id too as it's possible to have multiple watches on the same address