diff --git a/Common/Data/Random/Rng.h b/Common/Data/Random/Rng.h index b72c073124..0ddca96f52 100644 --- a/Common/Data/Random/Rng.h +++ b/Common/Data/Random/Rng.h @@ -55,9 +55,11 @@ public: // Read first, then regenerate that word in place - done in index order this is the // same as twisting the whole array in one go, and it matches what hardware leaves // behind in the context after each draw. - uint32_t y = mt_[index_]; - twist(index_); - index_ = (index_ + 1) % MT_SIZE; + // The context can live in emulated memory, so don't trust the index. + const uint32_t i = index_ % MT_SIZE; + uint32_t y = mt_[i]; + twist(i); + index_ = (i + 1) % MT_SIZE; y ^= y >> 11; y ^= (y << 7) & 2636928640UL; y ^= (y << 15) & 4022730752UL; diff --git a/Core/HLE/ErrorCodes.h b/Core/HLE/ErrorCodes.h index 35e18fc256..9af26b28df 100644 --- a/Core/HLE/ErrorCodes.h +++ b/Core/HLE/ErrorCodes.h @@ -4,6 +4,7 @@ enum PSPErrorCode : u32 { SCE_KERNEL_ERROR_OK = 0, + SCE_KERNEL_ERROR_NOT_INITIALIZED = 0x80000001, // Name guessed, from sceAudioInput before init. SCE_KERNEL_ERROR_BAD_ARGUMENT = 0x80000004, // Maybe SCE_KERNEL_ERROR_ALREADY = 0x80000020, SCE_KERNEL_ERROR_BUSY = 0x80000021, @@ -373,6 +374,7 @@ enum PSPErrorCode : u32 { SCE_ERROR_AUDIO_INVALID_FREQUENCY = 0x8026000A, SCE_ERROR_AUDIO_INVALID_VOLUME = 0x8026000B, SCE_ERROR_AUDIO_CHANNEL_ALREADY_RESERVED = 0x80268002, + SCE_ERROR_AUDIO_ROUTING_INVALID_MODE = 0x802C01FE, SCE_ERROR_USBMIC_INVALID_MAX_SAMPLES = 0x80243806, SCE_ERROR_USBMIC_INVALID_SAMPLERATE = 0x8024380A, diff --git a/Core/HLE/ReplaceTables.cpp b/Core/HLE/ReplaceTables.cpp index a158d1de3b..8398564c68 100644 --- a/Core/HLE/ReplaceTables.cpp +++ b/Core/HLE/ReplaceTables.cpp @@ -294,10 +294,15 @@ static int Replace_memcpy_swizzled(MIPSState *mips) { gpu->PerformReadbackToMemory(srcPtr, pitch * h); } } - u8 *dstp = Memory::GetPointerWriteRangeOrException(destPtr, pitch * h); - const u8 *srcp = Memory::GetPointerRangeOrException(srcPtr, pitch * h); + // The loops go in whole 16x8 blocks, so they can touch more than pitch * h. + const u64 rows = ((u64)h + 7) & ~7ULL; + const u64 cols = ((u64)pitch + 15) & ~15ULL; + const u64 dstSize = rows * cols; + const u64 srcSize = rows ? (rows - 1) * pitch + cols : 0; + u8 *dstp = dstSize <= 0xFFFFFFFF ? Memory::GetPointerWriteRangeOrException(destPtr, (u32)dstSize) : nullptr; + const u8 *srcp = srcSize <= 0xFFFFFFFF ? Memory::GetPointerRangeOrException(srcPtr, (u32)srcSize) : nullptr; - if (dstp && srcp) { + if (dstp && srcp && h != 0 && pitch != 0) { const u8 *ysrcp = srcp; for (u32 y = 0; y < h; y += 8) { const u8 *xsrcp = ysrcp; @@ -761,12 +766,15 @@ static int Hook_godseaterburst_depthmask_5551(MIPSState *mips) { uint32_t depthBuffer = mips->r[MIPS_REG_T2]; uint32_t byteStride = mips->r[MIPS_REG_A2]; uint32_t height = mips->r[MIPS_REG_T1]; - uint32_t size = byteStride * height; + const u64 size64 = (u64)byteStride * height; + if (size64 > 0xFFFFFFFF) + return 0; + uint32_t size = (uint32_t)size64; if (!Memory::IsVRAMAddress(colorBuffer) || !Memory::IsValidRange(colorBuffer, size)) return 0; if (depthBuffer != 0) { - if (!Memory::IsVRAMAddress(colorBuffer) || !Memory::IsValidRange(depthBuffer, size)) + if (!Memory::IsVRAMAddress(depthBuffer) || !Memory::IsValidRange(depthBuffer, size)) return 0; // This is added to read from the linearized mirror. diff --git a/Core/HLE/__sceAudio.cpp b/Core/HLE/__sceAudio.cpp index 1f35e600b9..e5a0b95566 100644 --- a/Core/HLE/__sceAudio.cpp +++ b/Core/HLE/__sceAudio.cpp @@ -112,6 +112,8 @@ void __AudioInit() { g_audioChans[i].clear(); } g_audioSRC.clear(); + __AudioRoutingInit(); + audioInputInited = false; mixBuffer = new s32[hwBlockSize * 2]; clampedMixBuffer = new s16[hwBlockSize * 2]; @@ -121,7 +123,7 @@ void __AudioInit() { } void __AudioDoState(PointerWrap &p) { - auto s = p.Section("sceAudio", 1, 4); + auto s = p.Section("sceAudio", 1, 5); if (!s) return; @@ -197,6 +199,12 @@ void __AudioDoState(PointerWrap &p) { } else { mixerDMABusy = false; } + if (s >= 5) { + Do(p, audioInputInited); + } else { + // A game that was already capturing had initialized it. + audioInputInited = true; + } // For older states the routing modes were read back once per channel, above. __AudioCPUMHzChange(); diff --git a/Core/HLE/sceAudio.cpp b/Core/HLE/sceAudio.cpp index 5a02ca265b..81b3b0b8c7 100644 --- a/Core/HLE/sceAudio.cpp +++ b/Core/HLE/sceAudio.cpp @@ -33,10 +33,11 @@ const u32 PSP_AUDIO_SAMPLE_MAX = 65536 - 64; const int PSP_AUDIO_ERROR_SRC_FORMAT_4 = 0x80000003; -const int AUDIO_ROUTING_SPEAKER_OFF = 0; -const int AUDIO_ROUTING_SPEAKER_ON = 1; -int defaultRoutingMode = AUDIO_ROUTING_SPEAKER_ON; -int defaultRoutingVolMode = AUDIO_ROUTING_SPEAKER_ON; +// Both read 0 at boot on a PSP. +int defaultRoutingMode = 0; +int defaultRoutingVolMode = 0; +// sceAudioInput fails until sceAudioInputInit. +bool audioInputInited = false; // Only still here to read savestates written before the channels held buffer pointers. struct AudioChannelWaitInfo { @@ -157,6 +158,11 @@ void AudioSRCChannel::DoState(PointerWrap &p) { Do(p, waitingThreads); } +void __AudioRoutingInit() { + defaultRoutingMode = 0; + defaultRoutingVolMode = 0; +} + void __AudioRoutingDoState(PointerWrap &p) { auto s = p.Section("sceAudioRouting", 1); if (!s) @@ -543,7 +549,25 @@ static u32 sceAudioSRCOutputBlocking(u32 vol, u32 buf) { return hleLogDebug(Log::sceAudio, __AudioSRCEnqueueBlocking(g_audioSRC, buf, vol)); } +// From pspautotests audio/sceaudio/input. The size is checked first. +static int AudioInputCheck(u32 maxSamples, u32 sampleRate) { + if (!audioInputInited) { + return SCE_KERNEL_ERROR_NOT_INITIALIZED; + } + if (maxSamples == 0 || (maxSamples & 63) != 0 || maxSamples >= 0x80000000) { + return SCE_ERROR_AUDIO_OUTPUT_SAMPLE_DATA_SIZE_NOT_ALIGNED; + } + if (sampleRate != 44100 && sampleRate != 22050 && sampleRate != 11025) { + return SCE_ERROR_AUDIO_INVALID_FREQUENCY; + } + return 0; +} + static int sceAudioInputBlocking(u32 maxSamples, u32 sampleRate, u32 bufAddr) { + int error = AudioInputCheck(maxSamples, sampleRate); + if (error != 0) { + return hleLogError(Log::HLE, error); + } if (!Memory::IsValidAddress(bufAddr)) { return hleLogError(Log::HLE, -1, "invalid address"); } @@ -551,6 +575,13 @@ static int sceAudioInputBlocking(u32 maxSamples, u32 sampleRate, u32 bufAddr) { } static int sceAudioInput(u32 maxSamples, u32 sampleRate, u32 bufAddr) { + int error = AudioInputCheck(maxSamples, sampleRate); + if (error != 0) { + return hleLogError(Log::HLE, error); + } + if (bufAddr == 0) { + return hleLogWarning(Log::HLE, 0, "NULL buffer"); + } if (!Memory::IsValidAddress(bufAddr)) { return hleLogError(Log::HLE, -1, "invalid address"); } @@ -561,11 +592,14 @@ static int sceAudioInput(u32 maxSamples, u32 sampleRate, u32 bufAddr) { static int sceAudioInputInit(int unknown1, int gain, int unknown2) { ERROR_LOG(Log::HLE, "UNIMPL sceAudioInputInit: unknown1: %d, gain: %d, unknown2: %d", unknown1, gain, unknown2); - return 0; + audioInputInited = true; + // Takes about 3ms on a PSP. + return hleDelayResult(0, "audio input init", 3000); } static int sceAudioInputInitEx(u32 paramAddr) { ERROR_LOG(Log::HLE, "UNIMPL sceAudioInputInitEx: paramAddr: %08x", paramAddr); + audioInputInited = true; return 0; } @@ -583,28 +617,31 @@ static int sceAudioGetInputLength() { return ret; } -static u32 sceAudioRoutingSetMode(u32 mode) { - ERROR_LOG(Log::sceAudio, "sceAudioRoutingSetMode(%08x)", mode); +// Ranges and return values from pspautotests audio/sceaudio/routing. Also exported as sceAudioRouting. +int sceAudioRoutingSetMode(int mode) { + if (mode < 0 || mode > 3) { + return hleLogError(Log::sceAudio, SCE_ERROR_AUDIO_ROUTING_INVALID_MODE, "invalid mode"); + } int previousMode = defaultRoutingMode; defaultRoutingMode = mode; - return previousMode; + return hleLogInfo(Log::sceAudio, previousMode); } -static u32 sceAudioRoutingGetMode() { - ERROR_LOG(Log::sceAudio, "sceAudioRoutingGetMode()"); - return defaultRoutingMode; +int sceAudioRoutingGetMode() { + return hleLogDebug(Log::sceAudio, defaultRoutingMode); } -static u32 sceAudioRoutingSetVolumeMode(u32 mode) { - ERROR_LOG(Log::sceAudio, "sceAudioRoutingSetVolumeMode(%08x)", mode); - int previousMode = defaultRoutingVolMode; +// Unlike SetMode, this returns 0 rather than the previous mode. +int sceAudioRoutingSetVolumeMode(int mode) { + if (mode < 0 || mode > 1) { + return hleLogError(Log::sceAudio, SCE_ERROR_AUDIO_ROUTING_INVALID_MODE, "invalid mode"); + } defaultRoutingVolMode = mode; - return previousMode; + return hleLogInfo(Log::sceAudio, 0); } -static u32 sceAudioRoutingGetVolumeMode() { - ERROR_LOG(Log::sceAudio, "sceAudioRoutingGetVolumeMode()"); - return defaultRoutingVolMode; +int sceAudioRoutingGetVolumeMode() { + return hleLogDebug(Log::sceAudio, defaultRoutingVolMode); } const HLEFunction sceAudio[] = @@ -653,10 +690,10 @@ const HLEFunction sceAudio[] = {0XA633048E, &WrapI_V, "sceAudioPollInputEnd", 'i', "" }, {0X87B2E651, &WrapI_V, "sceAudioWaitInputEnd", 'i', "" }, - {0X36FD8AA9, &WrapU_U, "sceAudioRoutingSetMode", 'x', "x" }, - {0X39240E7D, &WrapU_V, "sceAudioRoutingGetMode", 'x', "" }, - {0XBB548475, &WrapU_U, "sceAudioRoutingSetVolumeMode", 'x', "x" }, - {0X28235C56, &WrapU_V, "sceAudioRoutingGetVolumeMode", 'x', "" }, + {0X36FD8AA9, &WrapI_I, "sceAudioRoutingSetMode", 'i', "i" }, + {0X39240E7D, &WrapI_V, "sceAudioRoutingGetMode", 'i', "" }, + {0XBB548475, &WrapI_I, "sceAudioRoutingSetVolumeMode", 'i', "i" }, + {0X28235C56, &WrapI_V, "sceAudioRoutingGetVolumeMode", 'i', "" }, }; diff --git a/Core/HLE/sceAudio.h b/Core/HLE/sceAudio.h index 097b91ffee..eb53988e3d 100644 --- a/Core/HLE/sceAudio.h +++ b/Core/HLE/sceAudio.h @@ -114,7 +114,16 @@ extern AudioSRCChannel g_audioSRC; bool SRCFrequencyAllowed(int freq); // The two routing modes are globals rather than per-channel; they get their own little block. +void __AudioRoutingInit(); + +extern bool audioInputInited; void __AudioRoutingDoState(PointerWrap &p); +// sceAudioRouting exports the same four functions. +int sceAudioRoutingSetMode(int mode); +int sceAudioRoutingGetMode(); +int sceAudioRoutingSetVolumeMode(int mode); +int sceAudioRoutingGetVolumeMode(); + void Register_sceAudio(); diff --git a/Core/HLE/sceAudioRouting.cpp b/Core/HLE/sceAudioRouting.cpp index 35e4320539..d941b25aa0 100644 --- a/Core/HLE/sceAudioRouting.cpp +++ b/Core/HLE/sceAudioRouting.cpp @@ -17,39 +17,7 @@ #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" -#include "Core/HLE/sceAudiocodec.h" - -enum { - AUDIO_ROUTING_SPEAKER_OFF = 0, - AUDIO_ROUTING_SPEAKER_ON = 1, -}; - -u32 audioRoutingMode = AUDIO_ROUTING_SPEAKER_ON; -u32 audioRoutineVolumeMode = AUDIO_ROUTING_SPEAKER_ON; - -static int sceAudioRoutingGetMode() { - INFO_LOG(Log::sceAudio, "sceAudioRoutingGetMode"); - return 0; -} - -static int sceAudioRoutingSetVolumeMode(int mode) { - INFO_LOG(Log::sceAudio, "sceAudioRoutingSetVolumeMode %d", mode); - int previousMode = audioRoutineVolumeMode; - audioRoutineVolumeMode = audioRoutingMode; - return previousMode; -} - -static int sceAudioRoutingGetVolumeMode() { - INFO_LOG(Log::sceAudio, "sceAudioRoutingGetMode"); - return 0; -} - -static int sceAudioRoutingSetMode(int mode) { - INFO_LOG(Log::sceAudio, "sceAudioRoutingSetMode %d", mode); - int previousMode = audioRoutingMode; - audioRoutingMode = mode; - return previousMode; -} +#include "Core/HLE/sceAudio.h" const HLEFunction sceAudioRouting[] = { diff --git a/Core/HLE/sceCcc.cpp b/Core/HLE/sceCcc.cpp index f74636b0cb..b8cf497a03 100644 --- a/Core/HLE/sceCcc.cpp +++ b/Core/HLE/sceCcc.cpp @@ -373,7 +373,7 @@ static void sceCccEncodeUTF16(u32 dstAddrAddr, u32 ucs) } DEBUG_LOG(Log::sceMisc, "sceCccEncodeUTF16(%08x, U+%04x)", dstAddrAddr, ucs); // Anything above 0x10FFFF is unencodable, and 0xD800 - 0xDFFF are reserved for surrogate pairs. - if (ucs > 0x10FFFF || (ucs & 0xD800) == 0xD800) + if (ucs > 0x10FFFF || (ucs & 0xF800) == 0xD800) ucs = errorUTF16; *dstp += UTF16LE::encode(*dstp, ucs); } diff --git a/Core/HLE/sceHeap.cpp b/Core/HLE/sceHeap.cpp index ae91ec8632..c076653ee0 100644 --- a/Core/HLE/sceHeap.cpp +++ b/Core/HLE/sceHeap.cpp @@ -140,8 +140,15 @@ static u32 sceHeapAllocHeapMemoryWithOption(u32 heapAddr, u32 memSize, u32 param } // There's 8 bytes at the end of every block, reserved. + if (memSize > 0xFFFFFF00) { + return hleLogError(Log::HLE, 0, "invalid size %08x", memSize); + } memSize += 8; u32 addr = heap->alloc.AllocAligned(memSize, grain, grain, true); + // This returns a pointer, so failure is null rather than the allocator's -1 (like sceKernelAllocHeapMemory). + if (addr == (u32)-1) { + return hleLogError(Log::HLE, 0, "failed to allocate %d bytes", memSize); + } return hleLogDebug(Log::HLE, addr); } @@ -179,6 +186,7 @@ static int sceHeapDeleteHeap(u32 heapAddr) { } heapList.erase(heapAddr); + userMemory.Free(heap->address); delete heap; return hleLogDebug(Log::HLE, 0); } @@ -222,9 +230,15 @@ static u32 sceHeapAllocHeapMemory(u32 heapAddr, u32 memSize) { } // There's 8 bytes at the end of every block, reserved. + if (memSize > 0xFFFFFF00) { + return hleLogError(Log::HLE, 0, "invalid size %08x", memSize); + } memSize += 8; // Always goes down, regardless of whether the heap is high or low. u32 addr = heap->alloc.Alloc(memSize, true); + if (addr == (u32)-1) { + return hleLogError(Log::HLE, 0, "failed to allocate %d bytes", memSize); + } return hleLogDebug(Log::HLE, addr); } diff --git a/Core/HLE/sceP3da.cpp b/Core/HLE/sceP3da.cpp index f225bfaee0..4293f2f40e 100644 --- a/Core/HLE/sceP3da.cpp +++ b/Core/HLE/sceP3da.cpp @@ -43,7 +43,9 @@ static inline int getScaleValue(u32 channelsNum) { // What is this?? static u32 sceP3daBridgeCore(u32 p3daCoreAddr, u32 channelsNum, u32 samplesNum, u32 inputAddr, u32 outputAddr) { - if (Memory::IsValidRange(inputAddr, channelsNum * 4) && Memory::IsValidRange(outputAddr, samplesNum * sizeof(s16) * 2)) { + // The sizes could wrap in 32 bits. + if ((u64)channelsNum * 4 <= 0xFFFFFFFF && (u64)samplesNum * sizeof(s16) * 2 <= 0xFFFFFFFF && + Memory::IsValidRange(inputAddr, channelsNum * 4) && Memory::IsValidRange(outputAddr, samplesNum * sizeof(s16) * 2)) { int scaleval = getScaleValue(channelsNum); s16_le *outbuf = (s16_le *)Memory::GetPointerWriteUnchecked(outputAddr); memset(outbuf, 0, samplesNum * sizeof(s16) * 2); diff --git a/Core/HLE/sceRtc.cpp b/Core/HLE/sceRtc.cpp index 89314c8316..420cb863ca 100644 --- a/Core/HLE/sceRtc.cpp +++ b/Core/HLE/sceRtc.cpp @@ -23,6 +23,8 @@ #include #endif +#include +#include #include #include "Common/Serialize/Serializer.h" @@ -998,7 +1000,8 @@ static int __RtcFormatRFC2822(u32 outPtr, u32 srcTickPtr, int tz) { local.tm_year += 400; while (local.tm_year >= 470) local.tm_year -= 400; - local.tm_min += tz; + // tz is from the game, keep the sum from overflowing. + local.tm_min = (int)std::clamp((s64)local.tm_min + tz, INT_MIN / 2, INT_MAX / 2); rtc_timegm(&local); char *out = (char *)Memory::GetPointerWriteUnchecked(outPtr); @@ -1029,7 +1032,8 @@ static int __RtcFormatRFC3339(u32 outPtr, u32 srcTickPtr, int tz) { local.tm_year += 400; while (local.tm_year >= 470) local.tm_year -= 400; - local.tm_min += tz; + // tz is from the game, keep the sum from overflowing. + local.tm_min = (int)std::clamp((s64)local.tm_min + tz, INT_MIN / 2, INT_MAX / 2); rtc_timegm(&local); char *out = (char *)Memory::GetPointerWriteUnchecked(outPtr); diff --git a/Core/HLE/sceSas.cpp b/Core/HLE/sceSas.cpp index d617444143..bf7d901847 100644 --- a/Core/HLE/sceSas.cpp +++ b/Core/HLE/sceSas.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -77,7 +78,8 @@ static std::mutex sasWakeMutex; static std::mutex sasDoneMutex; static std::condition_variable sasWake; static std::condition_variable sasDone; -static volatile int sasThreadState = SasThreadState::DISABLED; +// Changed under sasWakeMutex (and sasDoneMutex for READY), but also read outside them. +static std::atomic sasThreadState{ SasThreadState::DISABLED }; static SasThreadParams sasThreadParams; static int sasMixEvent = -1; @@ -92,7 +94,10 @@ int __SasThread() { std::unique_lock guard(sasWakeMutex); while (sasThreadState != SasThreadState::DISABLED) { - sasWake.wait(guard); + // Check before sleeping, the mix may have been queued before we got here. + while (sasThreadState == SasThreadState::READY) { + sasWake.wait(guard); + } if (sasThreadState == SasThreadState::QUEUED) { const bool mute = g_sasMuteFlag; sas->Mix(sasThreadParams.outAddr, sasThreadParams.inAddr, sasThreadParams.leftVol, sasThreadParams.rightVol, mute); diff --git a/Core/HLE/sceSfmt19937.cpp b/Core/HLE/sceSfmt19937.cpp index f05aa5138a..ff1414a923 100644 --- a/Core/HLE/sceSfmt19937.cpp +++ b/Core/HLE/sceSfmt19937.cpp @@ -26,6 +26,12 @@ #include "Core/HLE/sceSfmt19937.h" +// The library indexes its state with idx, which lives in game memory. +static bool ValidSfmtIndex(u32 sfmt) { + const sfmt_t *psfmt = (const sfmt_t *)Memory::GetPointerUnchecked(sfmt); + return psfmt->idx >= 0 && psfmt->idx <= SFMT_N32; +} + static int sceSfmt19937InitGenRand(u32 sfmt, u32 seed) { if (!Memory::IsValidAddress(sfmt)) { ERROR_LOG(Log::HLE, "sceSfmt19937InitGenRand(sfmt=%08x, seed=%08x) - bad address(es)", sfmt, seed); @@ -54,7 +60,7 @@ static int sceSfmt19937InitByArray(u32 sfmt, u32 seeds, int seedslen) { } static u32 sceSfmt19937GenRand32(u32 sfmt) { - if (!Memory::IsValidAddress(sfmt)) { + if (!Memory::IsValidAddress(sfmt) || !ValidSfmtIndex(sfmt)) { ERROR_LOG(Log::HLE, "sceSfmt19937GenRand32(sfmt=%08x) - bad address(es)", sfmt); return -1; } @@ -67,7 +73,7 @@ static u32 sceSfmt19937GenRand32(u32 sfmt) { } static u64 sceSfmt19937GenRand64(u32 sfmt) { - if (!Memory::IsValidAddress(sfmt)) { + if (!Memory::IsValidAddress(sfmt) || !ValidSfmtIndex(sfmt)) { ERROR_LOG(Log::HLE, "sceSfmt19937GenRand64(sfmt=%08x) - bad address(es)", sfmt); return -1; } @@ -80,7 +86,8 @@ static u64 sceSfmt19937GenRand64(u32 sfmt) { } static int sceSfmt19937FillArray32(u32 sfmt, u32 array, int arraylen) { - if (!Memory::IsValidAddress(sfmt) || !Memory::IsValidAddress(array) || !Memory::IsValidAddress(array + 4 * (arraylen - 1))) { + // The library fills whole blocks, and needs at least a state's worth of them. + if (arraylen < SFMT_N32 || (arraylen % 4) != 0 || !Memory::IsValidAddress(sfmt) || !Memory::IsValidAddress(array) || !Memory::IsValidAddress(array + 4 * (arraylen - 1))) { ERROR_LOG(Log::HLE, "sceSfmt19937FillArray32(sfmt=%08x, ar=%08x, arlen=%08x) - bad address(es)", sfmt, array, arraylen); return -1; } @@ -94,7 +101,8 @@ static int sceSfmt19937FillArray32(u32 sfmt, u32 array, int arraylen) { } static int sceSfmt19937FillArray64(u32 sfmt, u32 array, int arraylen) { - if (!Memory::IsValidAddress(sfmt) || !Memory::IsValidAddress(array) || !Memory::IsValidAddress(array + 8 * (arraylen - 1))) { + // The library fills whole blocks, and needs at least a state's worth of them. + if (arraylen < SFMT_N64 || (arraylen % 2) != 0 || !Memory::IsValidAddress(sfmt) || !Memory::IsValidAddress(array) || !Memory::IsValidAddress(array + 8 * (arraylen - 1))) { ERROR_LOG(Log::HLE, "sceSfmt19937FillArray64(sfmt=%08x, ar=%08x, arlen=%08x) - bad address(es)", sfmt, array, arraylen); return -1; } diff --git a/Core/HLE/sceSysreg.cpp b/Core/HLE/sceSysreg.cpp index 34b776e846..045ab149f7 100644 --- a/Core/HLE/sceSysreg.cpp +++ b/Core/HLE/sceSysreg.cpp @@ -168,7 +168,9 @@ static int sceKernelUnregisterSysEventHandler(u32 handler) { if (!MeCore::g_meState) return hleLogWarning(Log::ME, 0, "no handlers"); u32 *link = &MeCore::g_meState->sysEventHandlers; - while (*link && Memory::IsValidRange(*link, 0x40)) { + // The list is in guest memory, so a loop in it shouldn't hang us. + int count = 0; + while (*link && Memory::IsValid4AlignedRange(*link, 0x40) && count++ < 0x10000) { if (*link == handler) { *link = Memory::ReadUnchecked_U32(handler + 0x18); return hleLogDebug(Log::ME, 0); diff --git a/Core/HLE/sceUsb.cpp b/Core/HLE/sceUsb.cpp index e7960325de..91479483d5 100644 --- a/Core/HLE/sceUsb.cpp +++ b/Core/HLE/sceUsb.cpp @@ -76,6 +76,12 @@ static void UsbWaitExecTimeout(u64 userdata, int cycleslate) { u32 error; SceUID threadID = (SceUID)userdata; + // The thread may have stopped waiting on USB since (for example, released), keeping the event. + if (__KernelGetWaitID(threadID, WAITTYPE_USB, error) == 0 || error != 0) { + HLEKernel::RemoveWaitingThread(waitingThreads, threadID); + return; + } + PSPPointer timeout = PSPPointer::Create(__KernelGetWaitTimeoutPtr(threadID, error)); if (timeout.IsValid()) *timeout = 0; @@ -91,7 +97,8 @@ static void UsbUpdateState() { for (size_t i = 0; i < waitingThreads.size(); ++i) { SceUID threadID = waitingThreads[i]; int state = __KernelGetWaitID(threadID, WAITTYPE_USB, error); - if (error != 0) + // 0 means it's not in a USB wait anymore. Waits always have a nonzero state. + if (error != 0 || state == 0) continue; u32 mode = __KernelGetWaitValue(threadID, error); @@ -109,6 +116,7 @@ static void UsbUpdateState() { } __KernelResumeThreadFromWait(threadID, UsbCurrentState()); + wokeThreads = true; } } diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index 4ba4a09326..a819f6f6c1 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -94,6 +94,8 @@ void __UsbCamShutdown() { if (config->mode == Camera::Mode::Video) { // stillImage? TBD Camera::stopCapture(); } + // Capture can still deliver a frame after stopCapture(), on its own thread. + std::lock_guard lock(videoBufferMutex); delete[] videoBuffer; videoBuffer = nullptr; delete config; @@ -422,7 +424,9 @@ void Camera::onCameraDeviceChange() { } } +// Called from the capture thread. int Camera::getMaxFrameSize() { + std::lock_guard lock(videoBufferMutex); int framesize = 0; if (config) { if (config->type == Camera::ConfigType::CfVideoEx) { diff --git a/Core/HLE/sceUsbGps.cpp b/Core/HLE/sceUsbGps.cpp index f97d430e16..ad39306736 100644 --- a/Core/HLE/sceUsbGps.cpp +++ b/Core/HLE/sceUsbGps.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include #include "Common/System/System.h" #include "Common/System/Request.h" @@ -39,6 +40,8 @@ GpsStatus gpsStatus = GPS_STATE_OFF; GpsData gpsData; SatData satData; time_t lastGPSTime; +// The platform's location callbacks set these from their own thread. +static std::recursive_mutex gpsLock; void __UsbGpsInit() { gpsStatus = GPS_STATE_OFF; @@ -99,6 +102,7 @@ static int sceUsbGpsClose() { } static int sceUsbGpsGetData(u32 gpsDataAddr, u32 satDataAddr) { + std::lock_guard guard(gpsLock); time_t currentTime; time(¤tTime); if (difftime(currentTime, lastGPSTime) > AUTO_UPDATE_GPSTIME) @@ -116,7 +120,7 @@ static int sceUsbGpsGetData(u32 gpsDataAddr, u32 satDataAddr) { auto satData = PSPPointer::Create(satDataAddr); if (satData.IsValid()) { *satData = *GPS::getSatData(); - gpsData.NotifyWrite("UsbGpsGetData"); + satData.NotifyWrite("UsbGpsGetData"); } return 0; } @@ -167,6 +171,7 @@ void GPS::init() { } void GPS::setGpsTime(time_t *time) { + std::lock_guard guard(gpsLock); struct tm *gpsTime; gpsTime = gmtime(time); @@ -179,8 +184,9 @@ void GPS::setGpsTime(time_t *time) { } void GPS::setGpsData(long long gpsTime, float hdop, float latitude, float longitude, float altitude, float speed, float bearing) { - lastGPSTime = gpsTime; - setGpsTime((time_t*)&gpsTime); + std::lock_guard guard(gpsLock); + lastGPSTime = (time_t)gpsTime; + setGpsTime(&lastGPSTime); gpsData.hdop = hdop; gpsData.latitude = latitude; @@ -191,6 +197,9 @@ void GPS::setGpsData(long long gpsTime, float hdop, float latitude, float longit } void GPS::setSatInfo(short index, unsigned char id, unsigned char elevation, short azimuth, unsigned char snr, unsigned char good) { + if (index < 0 || index >= (short)ARRAY_SIZE(satData.satInfo)) + return; + std::lock_guard guard(gpsLock); satData.satInfo[index].id = id; satData.satInfo[index].elevation = elevation; satData.satInfo[index].azimuth = azimuth; diff --git a/Core/HLE/sceUsbMic.cpp b/Core/HLE/sceUsbMic.cpp index 2567876078..499596fd26 100644 --- a/Core/HLE/sceUsbMic.cpp +++ b/Core/HLE/sceUsbMic.cpp @@ -168,6 +168,10 @@ void __UsbMicDoState(PointerWrap &p) { Do(p, waitingThreads); Do(p, isNeedInput); Do(p, curSampleRate); + if (curSampleRate == 0) { + // Old states could have one, from before __MicInput checked it. + curSampleRate = 44100; + } Do(p, curChannels); Do(p, micState); if (s > 1) { @@ -433,6 +437,14 @@ void Microphone::onMicDeviceChange() { } u32 __MicInput(u32 maxSamples, u32 sampleRate, u32 bufAddr, MICTYPE type, bool block) { + // The camera mic passes these straight through (sceAudioInput checks them first). The wait below + // divides by the rate, and the size sizes a host buffer. + if (sampleRate == 0) { + return SCE_ERROR_USBMIC_INVALID_SAMPLERATE; + } + if (maxSamples == 0 || maxSamples > 0x100000) { + return SCE_ERROR_USBMIC_INVALID_MAX_SAMPLES; + } if (micPolling && curSampleRate != sampleRate) { Microphone::stopMic(); } @@ -472,9 +484,11 @@ u32 __MicInput(u32 maxSamples, u32 sampleRate, u32 bufAddr, MICTYPE type, bool b MicWaitInfo waitInfo = { __KernelGetCurThread(), bufAddr, size, sampleRate }; waitingThreads.push_back(waitInfo); DEBUG_LOG(Log::HLE, "MicInputBlocking: blocking thread(%d)", (int)__KernelGetCurThread()); - __KernelWaitCurThread(WAITTYPE_MICINPUT, 1, size, 0, false, "blocking microphone"); + // The wait value is what the thread gets back when it's woken. + const u32 result = type == CAMERAMIC ? size : maxSamples; + __KernelWaitCurThread(WAITTYPE_MICINPUT, 1, result, 0, false, "blocking microphone"); - return type == CAMERAMIC ? size : maxSamples; + return result; } // A non-blocking input keeps filling its buffer from addAudioData() after it returns. diff --git a/Core/HW/GpioMMIO.cpp b/Core/HW/GpioMMIO.cpp index c2568890c7..ab24b2f4bb 100644 --- a/Core/HW/GpioMMIO.cpp +++ b/Core/HW/GpioMMIO.cpp @@ -123,7 +123,8 @@ public: switch (offset) { case 0x008: { - *value = (data_[dataIndex_] << 8) | data_[dataIndex_ + 1]; + // Writes can move the index anywhere. + *value = (dataIndex_ >= 0 && dataIndex_ + 1 < DATA_SIZE) ? (data_[dataIndex_] << 8) | data_[dataIndex_ + 1] : 0; dataIndex_ += 2; if (dataIndex_ > responseLen_) { dataIndex_ = 0; @@ -162,7 +163,8 @@ public: data_[dataIndex_] = (value >> 8) & 0xFF; data_[dataIndex_ + 1] = value & 0xFF; } - dataIndex_ += 2; + if (dataIndex_ < DATA_SIZE) + dataIndex_ += 2; break; default: break; @@ -184,6 +186,9 @@ public: return; DoArray(p, data_, DATA_SIZE); Do(p, dataIndex_); + if (p.mode == PointerWrap::MODE_READ && (dataIndex_ < 0 || dataIndex_ > DATA_SIZE)) { + dataIndex_ = 0; + } Do(p, responseLen_); Do(p, dataExhausted_); Do(p, alarm_); diff --git a/Core/HW/GranularMixer.cpp b/Core/HW/GranularMixer.cpp index 3f2374e177..2bf1404a02 100644 --- a/Core/HW/GranularMixer.cpp +++ b/Core/HW/GranularMixer.cpp @@ -77,7 +77,8 @@ void GranularMixer::Mix(s16 *samples, u32 num_samples, int outSampleRate, float if (!samples) return; memset(samples, 0, num_samples * 2 * sizeof(s16)); - frameTimeEstimate_ = 1.0f / fpsEstimate; + // The estimate is 0 until a second has gone by. + frameTimeEstimate_ = 1.0f / std::max(fpsEstimate, 1.0f); smoothedReadSize_ = smoothedReadSize_ == 0 ? num_samples : (smoothedReadSize_ * 0.95f + num_samples * 0.05f); @@ -262,7 +263,8 @@ void GranularMixer::Dequeue(Granule *granule) { /*if (!looping && !smoothedQueueSize_ < granule_queue_size / 2) { // Repeat a single block occasionally to make sure we have a reasonably sized queue. next_tail = tail; - } else*/ if (next_tail == head) { + } else*/ if (next_tail == head || tail == head) { + // Empty (or about to be). tail == head would read the granule being written next. // Only fill gaps when running to prevent stutter on pause. CoreState state = coreState; const bool is_running = state == CORE_RUNNING_CPU || state == CORE_RUNNING_GE; diff --git a/Core/HW/MeMMIO.cpp b/Core/HW/MeMMIO.cpp index bf3cf36e39..318e1859a8 100644 --- a/Core/HW/MeMMIO.cpp +++ b/Core/HW/MeMMIO.cpp @@ -289,7 +289,8 @@ private: const bool separate = (output & 1) != 0; d.csc[(0x160 - 0x120) / 4] &= ~1; d.intTCStatus |= 2; - if (width == 0 || height == 0 || stride < width) { + // A wider stride than any framebuffer would only overflow the size math below. + if (width == 0 || height == 0 || stride < width || stride > 4096) { ERROR_LOG(Log::ME, "DMACplus: bad colour space conversion %dx%d, stride %d", width, height, stride); return; } diff --git a/Core/HW/SasAudio.cpp b/Core/HW/SasAudio.cpp index d2147e0884..0d5170cb97 100644 --- a/Core/HW/SasAudio.cpp +++ b/Core/HW/SasAudio.cpp @@ -671,7 +671,9 @@ void SasInstance::Mix(u32 outAddr, u32 inAddr, int leftVol, int rightVol, bool m // Then mix the send buffer in with the rest. // Alright, all voices mixed. Let's convert and clip, and at the same time, wipe mixBuffer for next time. Could also dither. - s16 *outp = (s16 *)Memory::GetPointerWriteRangeOrException(outAddr, 4 * grainSize); + // Raw mode writes four planes of grainSize samples, mixed mode two interleaved. + const u32 outBytes = (outputMode == PSP_SAS_OUTPUTMODE_MIXED ? 4 : 8) * grainSize; + s16 *outp = (s16 *)Memory::GetPointerWriteRangeOrException(outAddr, outBytes); const s16 *inp = inAddr ? (const s16 *)Memory::GetPointerRangeOrException(inAddr, 4 * grainSize) : 0; if (!outp) { WARN_LOG_REPORT(Log::sceSas, "Bad SAS Mix output address: %08x, grain=%d", outAddr, grainSize); diff --git a/pspautotests b/pspautotests index fab33ea469..e2d098fcf3 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit fab33ea469ae6d82980064f3e29299e6920bf2e4 +Subproject commit e2d098fcf3c3396729a915094195b15a8ce8171e diff --git a/test.py b/test.py index f8cc384c5e..1bd99b35e5 100755 --- a/test.py +++ b/test.py @@ -173,6 +173,8 @@ tests_good = [ "audio/blocking/vaudio", "audio/sceaudio/reserve", "audio/sceaudio/datalen", + "audio/sceaudio/input", + "audio/sceaudio/routing", "audio/output2/changelength", "audio/output2/release", "audio/output2/reserve",