diff --git a/Core/HLE/AtracCtx2.cpp b/Core/HLE/AtracCtx2.cpp index 32f353971c..afa9ea9ef2 100644 --- a/Core/HLE/AtracCtx2.cpp +++ b/Core/HLE/AtracCtx2.cpp @@ -875,9 +875,13 @@ u32 Atrac2::DecodeInternal(u32 outbufAddr, int *SamplesNum, int *finish) { int16_t *outPtr; _dbg_assert_(samplesToDecode <= info.SamplesPerFrame()); - if (samplesToDecode != info.SamplesPerFrame()) { + // info.codec is in guest memory and can be changed after SetData, so what the decoder actually + // writes is decided by the decoder. + const int decoderSamples = decoder_->GetAudioType() == PSP_CODEC_AT3PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES; + if (samplesToDecode != decoderSamples) { if (!decodeTemp_) { - decodeTemp_ = new int16_t[info.SamplesPerFrame() * outputChannels_]; + // Big enough for any frame, in any channel count. + decodeTemp_ = new int16_t[ATRAC3PLUS_MAX_SAMPLES * 2](); } outPtr = decodeTemp_; } else { @@ -887,8 +891,9 @@ u32 Atrac2::DecodeInternal(u32 outbufAddr, int *SamplesNum, int *finish) { context_->codec.inBuf = inAddr; context_->codec.outBuf = outbufAddr; - if (!Memory::IsValidAddress(inAddr)) { - ERROR_LOG(Log::Atrac, "DecodeInternal: Bad inAddr %08x", inAddr); + // The decoder reads a whole frame. + if (!Memory::IsValidRange(inAddr, info.sampleSize)) { + ERROR_LOG(Log::Atrac, "DecodeInternal: Bad inAddr %08x (frame size %d)", inAddr, info.sampleSize); return SCE_ERROR_ATRAC_API_FAIL; } @@ -918,8 +923,8 @@ u32 Atrac2::DecodeInternal(u32 outbufAddr, int *SamplesNum, int *finish) { *finish = 0; } u8 *outBuf = outbufAddr ? Memory::GetPointerWriteOrException(outbufAddr) : nullptr; - if (samplesToDecode != info.SamplesPerFrame() && samplesToDecode != 0 && outBuf) { - memcpy(outBuf, decodeTemp_, samplesToDecode * outputChannels_ * sizeof(int16_t)); + if (outPtr == decodeTemp_ && samplesToDecode != 0 && outBuf) { + memcpy(outBuf, decodeTemp_, std::min(samplesToDecode, (int)ATRAC3PLUS_MAX_SAMPLES) * outputChannels_ * sizeof(int16_t)); } // Handle increments and looping. @@ -976,9 +981,11 @@ u32 Atrac2::DecodeInternal(u32 outbufAddr, int *SamplesNum, int *finish) { size_t copyLen = info.secondBufferByte % info.sampleSize; if (copyLen > info.bufferByte) copyLen = info.bufferByte; - memcpy(Memory::GetPointerWriteOrException(info.buffer), - Memory::GetPointerOrException(info.secondBuffer + (info.secondBufferByte - info.secondBufferByte % info.sampleSize)), - copyLen); + const u32 copySrc = info.secondBuffer + (info.secondBufferByte - info.secondBufferByte % info.sampleSize); + // Both addresses come from the context in guest memory. + if (Memory::IsValidRange(info.buffer, (u32)copyLen) && Memory::IsValidRange(copySrc, (u32)copyLen)) { + memcpy(Memory::GetPointerWriteOrException(info.buffer), Memory::GetPointerOrException(copySrc), copyLen); + } } } } diff --git a/Core/HLE/sceAtrac.cpp b/Core/HLE/sceAtrac.cpp index dd414c63a8..61b9dd26c4 100644 --- a/Core/HLE/sceAtrac.cpp +++ b/Core/HLE/sceAtrac.cpp @@ -420,7 +420,8 @@ static u32 sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32 int numSamplesWritten = 0; int finish = 0; int remains = 0; - if (outAddr != 0 && !Memory::IsValidAddress(outAddr)) { + // A whole frame is written. + if (outAddr != 0 && !Memory::IsValidRange(outAddr, atrac->GetOutputChannels() * 2 * atrac->SamplesPerFrame())) { // Dunno what error code to return here, but we have to bail in this case to avoid crashing PPSSPP. return hleLogError(Log::Atrac, SCE_ERROR_ATRAC_SIZE_TOO_SMALL); } @@ -757,6 +758,13 @@ static u32 sceAtracResetPlayPosition(int atracID, int sample, int bytesWrittenFi return hleDelayResult(res, "reset play pos", 3000); } + +// The track parsers read up to the size they're given, which the game picks. Bound it by what's +// actually mapped, but keep it for invalid pointers so those are still reported as such. +static u32 GuestReadableSize(u32 addr, u32 size) { + return Memory::IsValidAddress(addr) ? Memory::ClampValidSizeAt(addr, size) : size; +} + static u32 sceAtracSetHalfwayBuffer(int atracID, u32 buffer, u32 readSize, u32 bufferSize) { AtracBase *atrac = getAtrac(atracID); // Don't use AtracValidateManaged here. @@ -770,7 +778,7 @@ static u32 sceAtracSetHalfwayBuffer(int atracID, u32 buffer, u32 readSize, u32 b Track track; std::string error; - int ret = AnalyzeAtracTrack(Memory::GetPointerOrNull(buffer), readSize, &track, &error); + int ret = AnalyzeAtracTrack(Memory::GetPointerOrNull(buffer), GuestReadableSize(buffer, readSize), &track, &error); if (ret < 0) { return hleLogError(Log::Atrac, ret, "%s", error.c_str()); } @@ -807,7 +815,7 @@ static u32 sceAtracSetData(int atracID, u32 buffer, u32 bufferSize) { Track track; std::string error; - int ret = AnalyzeAtracTrack(Memory::GetPointerOrNull(buffer), bufferSize, &track, &error); + int ret = AnalyzeAtracTrack(Memory::GetPointerOrNull(buffer), GuestReadableSize(buffer, bufferSize), &track, &error); if (ret < 0) { return hleLogError(Log::Atrac, ret, "%s", error.c_str()); } @@ -838,7 +846,7 @@ static int sceAtracSetDataAndGetID(u32 buffer, int bufferSize) { std::string error; // We let zero-pointer through as zeroes, so the small size check can kick in before it's accessed. // This is needed in Tomb Raider Legend (#20145). - int ret = AnalyzeAtracTrack(Memory::GetPointerOrNull(buffer), bufferSize, &track, &error); + int ret = AnalyzeAtracTrack(Memory::GetPointerOrNull(buffer), GuestReadableSize(buffer, bufferSize), &track, &error); if (ret < 0) { return hleLogError(Log::Atrac, ret, "%s", error.c_str()); } @@ -864,7 +872,7 @@ static int sceAtracSetHalfwayBufferAndGetID(u32 buffer, u32 readSize, u32 buffer Track track; std::string error; - int ret = AnalyzeAtracTrack(Memory::GetPointerOrNull(buffer), readSize, &track, &error); + int ret = AnalyzeAtracTrack(Memory::GetPointerOrNull(buffer), GuestReadableSize(buffer, readSize), &track, &error); if (ret < 0) { return hleLogError(Log::Atrac, ret, "%s", error.c_str()); } @@ -990,7 +998,7 @@ static int sceAtracSetMOutHalfwayBuffer(int atracID, u32 buffer, u32 readSize, u Track track; std::string error; - int ret = AnalyzeAtracTrack(Memory::GetPointerOrNull(buffer), readSize, &track, &error); + int ret = AnalyzeAtracTrack(Memory::GetPointerOrNull(buffer), GuestReadableSize(buffer, readSize), &track, &error); if (ret < 0) { return hleLogError(Log::Atrac, ret, "%s", error.c_str()); } @@ -1016,7 +1024,7 @@ static u32 sceAtracSetMOutData(int atracID, u32 buffer, u32 bufferSize) { Track track; std::string error; - int ret = AnalyzeAtracTrack(Memory::GetPointerOrNull(buffer), bufferSize, &track, &error); + int ret = AnalyzeAtracTrack(Memory::GetPointerOrNull(buffer), GuestReadableSize(buffer, bufferSize), &track, &error); if (ret < 0) { return hleLogError(Log::Atrac, ret, "%s", error.c_str()); } @@ -1036,7 +1044,7 @@ static u32 sceAtracSetMOutData(int atracID, u32 buffer, u32 bufferSize) { static int sceAtracSetMOutDataAndGetID(u32 buffer, u32 bufferSize) { Track track; std::string error; - int ret = AnalyzeAtracTrack(Memory::GetPointerOrNull(buffer), bufferSize, &track, &error); + int ret = AnalyzeAtracTrack(Memory::GetPointerOrNull(buffer), GuestReadableSize(buffer, bufferSize), &track, &error); if (ret < 0) { return hleLogError(Log::Atrac, ret, "%s", error.c_str()); } @@ -1065,7 +1073,7 @@ static int sceAtracSetMOutHalfwayBufferAndGetID(u32 buffer, u32 readSize, u32 bu Track track; std::string error; - int ret = AnalyzeAtracTrack(Memory::GetPointerOrNull(buffer), readSize, &track, &error); + int ret = AnalyzeAtracTrack(Memory::GetPointerOrNull(buffer), GuestReadableSize(buffer, readSize), &track, &error); if (ret < 0) { return hleLogError(Log::Atrac, ret, "%s", error.c_str()); } @@ -1090,7 +1098,7 @@ static int sceAtracSetMOutHalfwayBufferAndGetID(u32 buffer, u32 readSize, u32 bu static int sceAtracSetAA3DataAndGetID(u32 buffer, u32 bufferSize, u32 fileSize, u32 metadataSizeAddr) { Track track; std::string error; - int ret = AnalyzeAA3Track(Memory::GetPointerOrNull(buffer), bufferSize, fileSize, &track, &error); + int ret = AnalyzeAA3Track(Memory::GetPointerOrNull(buffer), GuestReadableSize(buffer, bufferSize), fileSize, &track, &error); if (ret < 0) { return hleLogError(Log::Atrac, ret, "%s", error.c_str()); } @@ -1116,7 +1124,7 @@ static int sceAtracSetAA3HalfwayBufferAndGetID(u32 buffer, u32 readSize, u32 buf Track track; std::string error; - int ret = AnalyzeAA3Track(Memory::GetPointerOrNull(buffer), readSize, fileSize, &track, &error); + int ret = AnalyzeAA3Track(Memory::GetPointerOrNull(buffer), GuestReadableSize(buffer, readSize), fileSize, &track, &error); if (ret < 0) { return hleLogError(Log::Atrac, ret, "%s", error.c_str()); } diff --git a/Core/HLE/sceAudiocodec.cpp b/Core/HLE/sceAudiocodec.cpp index c8f346cc3c..5e5514ab7e 100644 --- a/Core/HLE/sceAudiocodec.cpp +++ b/Core/HLE/sceAudiocodec.cpp @@ -640,6 +640,12 @@ static int sceAudiocodecDecode(u32 ctxPtr, int codec) { } else if (codec == PSP_CODEC_AT3PLUS && ctx->fmt.at3.outputChannels == 1) { outputChannels = 1; } + // The decoder writes a whole frame, but only the first byte of outBuf was checked. + const u32 maxFrameSamples = codec == PSP_CODEC_AT3PLUS ? 2048 : (codec == PSP_CODEC_MP3 ? 1152 : 1024); + if (!Memory::IsValidRange(ctx->outBuf, maxFrameSamples * outputChannels * 2)) { + ctx->err = 0x20b; + return hleLogError(Log::ME, 0, "Output buffer at %08x can't hold a frame", ctx->outBuf); + } bool result = decoder->Decode(inBuf, bytesPerFrame, &inDataConsumed, outputChannels, outBuf, &outSamples); if (!result && (codec == PSP_CODEC_AT3PLUS || codec == PSP_CODEC_AT3)) { // What the hardware reports for a frame that doesn't decode (0x208 is also possible for diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index acd4e9edec..b180b0bd11 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -420,9 +420,10 @@ static int CalculateMp3SamplesPerFrame(int versionBits, int layerBits) { static int FindMp3Header(AuCtx *ctx, int &header, int end) { u32 addr = ctx->AuBuf + ctx->AuStreamWorkareaSize(); - if (Memory::IsValidRange(addr, end)) { + // A header is four bytes, read at each offset. + if (end >= 4 && Memory::IsValidRange(addr, end)) { const u8 *ptr = Memory::GetPointerUnchecked(addr); - for (int offset = 0; offset < end; ++offset) { + for (int offset = 0; offset + 4 <= end; ++offset) { // If we hit valid sync bits, then we've found a header. if (ptr[offset] == 0xFF && (ptr[offset + 1] & 0xC0) == 0xC0) { header = bswap32(Memory::ReadUnchecked_U32(addr + offset)); @@ -758,12 +759,17 @@ static u32 sceMp3LowLevelDecode(u32 mp3, u32 sourceAddr, u32 sourceBytesConsumed return hleLogError(Log::ME, -1, "invalid address in args"); } + // A frame is at most 1152 stereo samples, and the decoder is handed up to 4096 bytes. + if (!Memory::IsValidRange(samplesAddr, 1152 * 2 * 2) || !Memory::IsValidRange(sourceBytesConsumedAddr, 4) || !Memory::IsValidRange(sampleBytesAddr, 4)) { + return hleLogError(Log::ME, -1, "invalid address in args"); + } const u8 *inbuff = Memory::GetPointerWriteUnchecked(sourceAddr); int16_t *outbuf = (int16_t *)Memory::GetPointerWriteUnchecked(samplesAddr); + const int inBytes = (int)Memory::ClampValidSizeAt(sourceAddr, 4096); int outSamples = 0; int inbytesConsumed = 0; - if (!ctx->decoder->Decode(inbuff, 4096, &inbytesConsumed, 2, outbuf, &outSamples)) { + if (!ctx->decoder->Decode(inbuff, inBytes, &inbytesConsumed, 2, outbuf, &outSamples)) { WARN_LOG(Log::ME, "sceMp3LowLevelDecode: Decode failed"); } int outBytes = outSamples * sizeof(int16_t) * 2; diff --git a/Core/HLE/scePsmf.cpp b/Core/HLE/scePsmf.cpp index f0c8070d27..d234f8ec41 100644 --- a/Core/HLE/scePsmf.cpp +++ b/Core/HLE/scePsmf.cpp @@ -295,9 +295,15 @@ public: // the unwrapped count. const u64 epMapBytes = EP_MAP_STRIDE * (u64)psmf->EPMapEntriesNum; if (psmf->headerOffset != 0) { - if (epMapBytes > 0xFFFFFFFFull - psmf->EPMapOffset || !Memory::IsValidRange(psmf->headerOffset, psmf->EPMapOffset + (u32)epMapBytes)) { + // The entries are read from data + EPMapOffset, so the map has to start inside the + // address space (the sum used to wrap around to a valid address) and be mapped. + const u64 epMapAddr = (u64)psmf->headerOffset + psmf->EPMapOffset; + if (epMapAddr > 0xFFFFFFFFull || !Memory::IsValidAddress((u32)epMapAddr)) { + ERROR_LOG(Log::ME, "Invalid PSMF EP map offset: %08x", psmf->EPMapOffset); + psmf->EPMapEntriesNum = 0; + } else if (epMapBytes > 0xFFFFFFFFull - psmf->EPMapOffset || !Memory::IsValidRange(psmf->headerOffset, psmf->EPMapOffset + (u32)epMapBytes)) { ERROR_LOG(Log::ME, "Invalid PSMF EP map entry count: %d", psmf->EPMapEntriesNum); - psmf->EPMapEntriesNum = Memory::ClampValidSizeAt(psmf->headerOffset + psmf->EPMapOffset, (u32)epMapBytes) / EP_MAP_STRIDE; + psmf->EPMapEntriesNum = Memory::ClampValidSizeAt((u32)epMapAddr, (u32)epMapBytes) / EP_MAP_STRIDE; } } else { // No guest address to validate against (player tempbuf path, @@ -770,7 +776,8 @@ static void DelayPsmfStateChange(u32 psmfPlayer, u32 newState, s64 delayUs) { } static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) { - if (!Memory::IsValidAddress(psmfStruct) || !Memory::IsValidAddress(psmfData)) { + // The header fields Psmf reads go up to 0x82. + if (!Memory::IsValidAddress(psmfStruct) || !Memory::IsValidRange(psmfData, 0x82)) { // Crashes on a PSP. return hleReportError(Log::ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address"); }