diff --git a/Core/Font/PGF.cpp b/Core/Font/PGF.cpp index 20a30f4339..b36864bdba 100644 --- a/Core/Font/PGF.cpp +++ b/Core/Font/PGF.cpp @@ -746,6 +746,9 @@ void PGF::SetFontPixel(u32 base, int bpl, int bufWidth, int bufHeight, int x, in } int framebufferAddr = base + (y * bpl) + (pixelBytes == 0 ? x / 2 : x * pixelBytes); + if (!Memory::IsValidAddress(framebufferAddr)) { + return; + } switch (pixelformat) { case PSP_FONT_PIXELFORMAT_4: @@ -753,29 +756,29 @@ void PGF::SetFontPixel(u32 base, int bpl, int bufWidth, int bufHeight, int x, in { // The two pixels share a byte, so the neighbour's nibble is left alone. const int shift = ((x & 1) != pixelformat) ? 4 : 0; - const int oldColor = Memory::Read_U8(framebufferAddr); + const int oldColor = Memory::ReadUnchecked_U8(framebufferAddr); const int newPix = std::min(((oldColor >> shift) & 0xF) + pixelColor, 15); - Memory::Write_U8((u8)((oldColor & ~(0xF << shift)) | (newPix << shift)), framebufferAddr); + Memory::WriteUnchecked_U8((u8)((oldColor & ~(0xF << shift)) | (newPix << shift)), framebufferAddr); break; } case PSP_FONT_PIXELFORMAT_8: { - const int newPix = std::min((int)Memory::Read_U8(framebufferAddr) + pixelColor, 255); - Memory::Write_U8((u8)newPix, framebufferAddr); + const int newPix = std::min((int)Memory::ReadUnchecked_U8(framebufferAddr) + pixelColor, 255); + Memory::WriteUnchecked_U8((u8)newPix, framebufferAddr); break; } case PSP_FONT_PIXELFORMAT_24: { // Each channel gets the same value. for (int i = 0; i < 3; ++i) { - const int newPix = std::min((int)Memory::Read_U8(framebufferAddr + i) + pixelColor, 255); - Memory::Write_U8((u8)newPix, framebufferAddr + i); + const int newPix = std::min((int)Memory::ReadUnchecked_U8(framebufferAddr + i) + pixelColor, 255); + Memory::WriteUnchecked_U8((u8)newPix, framebufferAddr + i); } break; } case PSP_FONT_PIXELFORMAT_32: { - const u32 oldColor = Memory::Read_U32(framebufferAddr); + const u32 oldColor = Memory::ReadUnchecked_U32(framebufferAddr); u32 pix32 = 0; for (int i = 0; i < 4; ++i) { const int newPix = std::min((int)((oldColor >> (i * 8)) & 0xFF) + pixelColor, 255); diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp index 165d5451bd..3404753e62 100644 --- a/Core/HLE/HLE.cpp +++ b/Core/HLE/HLE.cpp @@ -721,7 +721,8 @@ void HLEReturnFromMipsCall() { currentMIPS->pc = stackData->func; currentMIPS->r[MIPS_REG_RA] = HLEMipsCallReturnAddress(); for (int i = 0; i < (int)stackData->argc; i++) { - currentMIPS->r[MIPS_REG_A0 + i] = Memory::Read_U32(sp + sizeof(HLEMipsCallStack) + i * sizeof(u32)); + // The check at the start of the function should be enough to use an unchecked read (well, kinda..) + currentMIPS->r[MIPS_REG_A0 + i] = Memory::ReadUnchecked_U32(sp + sizeof(HLEMipsCallStack) + i * sizeof(u32)); } DEBUG_LOG(Log::HLE, "Executing next HLE mips call at %08x, sp=%08x", currentMIPS->pc, sp); hleNoLogVoid(); diff --git a/Core/HLE/ReplaceTables.cpp b/Core/HLE/ReplaceTables.cpp index d2f73f62ea..5e56c3d2b6 100644 --- a/Core/HLE/ReplaceTables.cpp +++ b/Core/HLE/ReplaceTables.cpp @@ -1846,6 +1846,9 @@ void RestoreReplacedInstructions(u32 startAddr, u32 endAddr) { std::map SaveAndClearReplacements() { std::map saved; for (const auto &[addr, instr] : replacedInstructions) { + if (!Memory::IsValid4AlignedAddress(addr)) { + continue; + } // This will not retain jit blocks. const u32 curInstr = Memory::Read_Opcode_JIT(addr).encoding; if (MIPS_IS_REPLACEMENT(curInstr)) { diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 6d8e9c3961..cfe367ddbb 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -238,7 +238,7 @@ static MpegContext *getMpegCtx(u32 mpegAddr) { if (!Memory::IsValidAddress(mpegAddr)) return nullptr; - u32 mpeg = Memory::Read_U32(mpegAddr); + u32 mpeg = Memory::ReadUnchecked_U32(mpegAddr); auto found = g_mpegCtxs.find(mpeg); if (found == g_mpegCtxs.end()) return nullptr; @@ -560,7 +560,7 @@ static int sceMpegDelete(u32 mpeg) { } delete ctx; - g_mpegCtxs.erase(Memory::Read_U32(mpeg)); + g_mpegCtxs.erase(Memory::ReadUnchecked_U32(mpeg)); return hleDelayResult(hleLogDebug(Log::Mpeg, 0), "mpeg delete", 40000); } @@ -568,7 +568,7 @@ static int sceMpegDelete(u32 mpeg) { static int sceMpegAvcDecodeMode(u32 mpeg, u32 modeAddr) { - if (!Memory::IsValidAddress(modeAddr)) { + if (!Memory::IsValidRange(modeAddr, 8)) { return hleLogWarning(Log::Mpeg, -1, "invalid addresses"); } @@ -579,8 +579,8 @@ static int sceMpegAvcDecodeMode(u32 mpeg, u32 modeAddr) DEBUG_LOG(Log::Mpeg, "sceMpegAvcDecodeMode(%08x, %08x)", mpeg, modeAddr); - int mode = Memory::Read_U32(modeAddr); - int pixelMode = Memory::Read_U32(modeAddr + 4); + int mode = Memory::ReadUnchecked_U32(modeAddr); + int pixelMode = Memory::ReadUnchecked_U32(modeAddr + 4); if (pixelMode >= GE_CMODE_16BIT_BGR5650 && pixelMode <= GE_CMODE_32BIT_ABGR8888) { ctx->videoPixelMode = pixelMode; } else { @@ -1071,8 +1071,7 @@ void __VideoPmpDoState(PointerWrap &p){ } } -static u32 sceMpegAvcDecode(u32 mpeg, u32 auAddr, u32 frameWidth, u32 bufferAddr, u32 initAddr) -{ +static u32 sceMpegAvcDecode(u32 mpeg, u32 auAddr, u32 frameWidth, u32 bufferAddr, u32 initAddr) { MpegContext *ctx = getMpegCtx(mpeg); if (!ctx) { return hleLogWarning(Log::Mpeg, -1, "bad mpeg handle"); @@ -1094,8 +1093,12 @@ static u32 sceMpegAvcDecode(u32 mpeg, u32 auAddr, u32 frameWidth, u32 bufferAddr return hleLogError(Log::Mpeg, -1, "Bogus mpegringbufferaddr"); } - u32 buffer = Memory::Read_U32(bufferAddr); - u32 init = Memory::Read_U32(initAddr); + if (!Memory::IsValidRange(bufferAddr, 4) || !Memory::IsValidRange(initAddr, 4)) { + return hleLogError(Log::Mpeg, -1, "invalid addresses"); + } + + u32 buffer = Memory::ReadUnchecked_U32(bufferAddr); + u32 init = Memory::ReadUnchecked_U32(initAddr); DEBUG_LOG(Log::Mpeg, "video: bufferAddr = %08x, *buffer = %08x, *init = %08x", bufferAddr, buffer, init); // check and decode pmp video @@ -1244,7 +1247,7 @@ static int sceMpegAvcDecodeDetail(u32 mpeg, u32 detailAddr) { } static u32 sceMpegAvcDecodeStopYCbCr(u32 mpeg, u32 bufferAddr, u32 statusAddr) { - if (!Memory::IsValidAddress(bufferAddr) || !Memory::IsValidAddress(statusAddr)) { + if (!Memory::IsValidAddress(bufferAddr) || !Memory::IsValid4AlignedAddress(statusAddr)) { return hleLogError(Log::Mpeg, -1, "UNIMPL + invalid addresses"); } @@ -1254,7 +1257,7 @@ static u32 sceMpegAvcDecodeStopYCbCr(u32 mpeg, u32 bufferAddr, u32 statusAddr) { } ERROR_LOG(Log::Mpeg, "UNIMPL sceMpegAvcDecodeStopYCbCr(%08x, %08x, %08x)", mpeg, bufferAddr, statusAddr); - Memory::Write_U32(0, statusAddr); + Memory::WriteUnchecked_U32(0, statusAddr); return hleNoLog(0); } @@ -1282,8 +1285,12 @@ static int sceMpegAvcDecodeYCbCr(u32 mpeg, u32 auAddr, u32 bufferAddr, u32 initA // We stored the video stream id here in sceMpegGetAvcAu(). ctx->mediaengine->setVideoStream(avcAu.esBuffer); - u32 buffer = Memory::Read_U32(bufferAddr); - u32 init = Memory::Read_U32(initAddr); + if (!Memory::IsValidRange(bufferAddr, 4) || !Memory::IsValidRange(initAddr, 4)) { + return hleLogError(Log::Mpeg, -1, "invalid addresses"); + } + + u32 buffer = Memory::ReadUnchecked_U32(bufferAddr); + u32 init = Memory::ReadUnchecked_U32(initAddr); DEBUG_LOG(Log::Mpeg, "*buffer = %08x, *init = %08x", buffer, init); if (ctx->mediaengine->stepVideo(ctx->videoPixelMode)) { @@ -1309,10 +1316,10 @@ static int sceMpegAvcDecodeYCbCr(u32 mpeg, u32 auAddr, u32 bufferAddr, u32 initA if (mpegLibVersion >= 0x010A) { // Sunday Vs Magazine Shuuketsu! Choujou Daikessen expect, issue #11060 - Memory::Write_U32(1, initAddr); + Memory::WriteUnchecked_U32(1, initAddr); } else { // Save the current frame's status to initAddr - Memory::Write_U32(ctx->avc.avcFrameStatus, initAddr); + Memory::WriteUnchecked_U32(ctx->avc.avcFrameStatus, initAddr); } ctx->avc.avcDecodeResult = MPEG_AVC_DECODE_SUCCESS; @@ -1928,7 +1935,7 @@ static u32 sceMpegAtracDecode(u32 mpeg, u32 auAddr, u32 bufferAddr, int init) // YCbCr -> RGB color space conversion static u32 sceMpegAvcCsc(u32 mpeg, u32 sourceAddr, u32 rangeAddr, int frameWidth, u32 destAddr) { - if (!Memory::IsValidAddress(sourceAddr) || !Memory::IsValidAddress(rangeAddr) || !Memory::IsValidAddress(destAddr)) { + if (!Memory::IsValidAddress(sourceAddr) || !Memory::IsValidRange(rangeAddr, 16) || !Memory::IsValidAddress(destAddr)) { return hleLogError(Log::Mpeg, -1, "invalid addresses"); } @@ -1945,10 +1952,10 @@ static u32 sceMpegAvcCsc(u32 mpeg, u32 sourceAddr, u32 rangeAddr, int frameWidth } } - int x = Memory::Read_U32(rangeAddr); - int y = Memory::Read_U32(rangeAddr + 4); - int width = Memory::Read_U32(rangeAddr + 8); - int height = Memory::Read_U32(rangeAddr + 12); + int x = Memory::ReadUnchecked_U32(rangeAddr); + int y = Memory::ReadUnchecked_U32(rangeAddr + 4); + int width = Memory::ReadUnchecked_U32(rangeAddr + 8); + int height = Memory::ReadUnchecked_U32(rangeAddr + 12); if (x < 0 || y < 0 || width < 0 || height < 0) { WARN_LOG(Log::Mpeg, "sceMpegAvcCsc(%08x, %08x, %08x, %i, %08x) returning ERROR_INVALID_VALUE", mpeg, sourceAddr, rangeAddr, frameWidth, destAddr); diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index cfeb4ddad5..d54520b499 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -1552,10 +1552,10 @@ int NetApctl_GetBSSDescIDListUser(u32 sizeAddr, u32 bufAddr) { const int userInfoSize = 8; // 8 bytes per entry (next address + entry id) // Faking 4 entries, games like MGS:PW Recruit will need to have a different AP for each entry int entries = 4; - if (!Memory::IsValidAddress(sizeAddr) || !Memory::IsValidAddress(bufAddr)) + if (!Memory::IsValid4AlignedAddress(sizeAddr) || !Memory::IsValidAddress(bufAddr)) return hleLogError(Log::sceNet, -1, "apctl invalid arg"); // 0x8002013A or ERROR_NET_WLAN_INVALID_ARG ? - int size = Memory::Read_U32(sizeAddr); + int size = Memory::ReadUnchecked_U32(sizeAddr); // Return size required Memory::Write_U32(entries * userInfoSize, sizeAddr); diff --git a/Core/MIPS/IR/IRJit.cpp b/Core/MIPS/IR/IRJit.cpp index a4fbf17d80..db179d1b97 100644 --- a/Core/MIPS/IR/IRJit.cpp +++ b/Core/MIPS/IR/IRJit.cpp @@ -547,7 +547,7 @@ bool IRBlock::RestoreOriginalFirstOp(int cookie) { void IRBlock::Finalize(int cookie) { // Check it wasn't invalidated, in case this is after preload. // TODO: Allow reusing blocks when the code matches hash_ again, instead. - if (origAddr_) { + if (origAddr_ && Memory::IsValid4AlignedAddress(origAddr_)) { origFirstOpcode_ = Memory::Read_Opcode_JIT(origAddr_); MIPSOpcode opcode = MIPSOpcode(MIPS_EMUHACK_OPCODE | cookie); Memory::Write_Opcode_JIT(origAddr_, opcode); @@ -557,7 +557,7 @@ void IRBlock::Finalize(int cookie) { } void IRBlock::Destroy(int cookie) { - if (origAddr_) { + if (origAddr_ && Memory::IsValid4AlignedAddress(origAddr_)) { MIPSOpcode opcode = MIPSOpcode(MIPS_EMUHACK_OPCODE | cookie); u32 memOp = Memory::ReadUnchecked_U32(origAddr_); if (memOp == opcode.encoding) { diff --git a/Core/MIPS/MIPSDebugInterface.cpp b/Core/MIPS/MIPSDebugInterface.cpp index 41d1d1c553..c6dbcf3140 100644 --- a/Core/MIPS/MIPSDebugInterface.cpp +++ b/Core/MIPS/MIPSDebugInterface.cpp @@ -293,7 +293,7 @@ bool parseExpression(const DebugInterface *debug, PostfixExpression& exp, u32& d } void DisAsm(u32 pc, char *out, size_t outSize) { - if (Memory::IsValidAddress(pc)) + if (Memory::IsValid4AlignedAddress(pc)) MIPSDisAsm(Memory::Read_Opcode_JIT(pc), pc, out, outSize); else truncate_cpy(out, outSize, "-"); diff --git a/Core/MIPS/MIPSInt.cpp b/Core/MIPS/MIPSInt.cpp index 20c890ce2e..1a74bd8d69 100644 --- a/Core/MIPS/MIPSInt.cpp +++ b/Core/MIPS/MIPSInt.cpp @@ -75,9 +75,12 @@ static inline void SkipLikely() { } } -int MIPS_SingleStep() -{ - MIPSOpcode op = Memory::Read_Opcode_JIT(mipsr4k.pc); +int MIPS_SingleStep() { + if (!Memory::IsValid4AlignedAddress(mipsr4k.pc)) { + Core_ExecException(mipsr4k.pc, mipsr4k.pc, ExecExceptionType::JUMP); + return 0; + } + MIPSOpcode op = Memory::Read_Opcode_JIT(mipsr4k.pc); // now unchecked if (mipsr4k.inDelaySlot) { MIPSInterpret(op); if (mipsr4k.inDelaySlot) { diff --git a/Core/MemMap.cpp b/Core/MemMap.cpp index a7b5caec05..a518e957c1 100644 --- a/Core/MemMap.cpp +++ b/Core/MemMap.cpp @@ -490,9 +490,10 @@ Opcode ReadUnchecked_Instruction(u32 address, bool resolveReplacements) { return Read_Instruction(address, resolveReplacements, inst); } -Opcode Read_Opcode_JIT(u32 address) -{ - Opcode inst = Opcode(Read_U32(address)); +// WARNING! Caller checks that address is valid! +Opcode Read_Opcode_JIT(u32 address) { + _dbg_assert_(Memory::IsValid4AlignedAddress(address)); + Opcode inst = Opcode(ReadUnchecked_U32(address)); // No mutex around jit access here, but we assume caller has if necessary. if (MIPS_IS_RUNBLOCK(inst.encoding) && MIPSComp::jit) { return MIPSComp::jit->GetOriginalOp(inst);