Merge pull request #22940 from hrydgard/hle-hardening

HLE: Range-check buffers and structs from game arguments
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2026-11-06 10:01:54 +01:00
commit 5327f828ec
26 files changed
+248 -90

No files matched your search

+34 -5
View File
@@ -422,11 +422,21 @@ int SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &saveD
ERROR_LOG_REPORT(Log::sceUtility, "Savedata buffer overflow: %d / %d", param->dataSize, param->dataBufSize);
return SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_PARAMS;
}
// Everything below reads these buffers by their sizes, so they have to be in memory. A size of
// 2^31 or more would also go negative as the int the encryption uses.
if (param->dataBuf.IsValid() && (param->dataSize > 0x7FFFFFF0 || !Memory::IsValidRange(param->dataBuf.ptr, param->dataSize))) {
ERROR_LOG_REPORT(Log::sceUtility, "Savedata buffer %08x (%d bytes) isn't all in memory", param->dataBuf.ptr, param->dataSize);
return SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_PARAMS;
}
auto validateSize = [](const PspUtilitySavedataFileData &data) {
if (data.buf.IsValid() && data.bufSize < data.size) {
ERROR_LOG_REPORT(Log::sceUtility, "Savedata subdata buffer overflow: %d / %d", data.size, data.bufSize);
return false;
}
if (data.buf.IsValid() && !Memory::IsValidRange(data.buf.ptr, data.size)) {
ERROR_LOG_REPORT(Log::sceUtility, "Savedata subdata buffer %08x (%d bytes) isn't all in memory", data.buf.ptr, data.size);
return false;
}
return true;
};
if (!validateSize(param->icon0FileData) || !validateSize(param->icon1FileData) || !validateSize(param->pic1FileData) || !validateSize(param->snd0FileData)) {
@@ -462,7 +472,7 @@ int SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &saveD
cryptedSize = param->dataSize;
if (cryptedSize == 0 || (SceSize)cryptedSize > param->dataBufSize) {
ERROR_LOG(Log::sceUtility, "Bad cryptedSize %d", cryptedSize);
cryptedSize = param->dataBufSize; // fallback, should never use this
cryptedSize = (int)std::min(Memory::ClampValidSizeAt(param->dataBuf.ptr, param->dataBufSize), (u32)0x7FFFFFF0); // fallback, should never use this
}
u8 *data_ = param->dataBuf;
@@ -568,7 +578,7 @@ int SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &saveD
{
saveSize = param->dataSize;
if(saveSize == 0 || saveSize > param->dataBufSize)
saveSize = param->dataBufSize; // fallback, should never use this
saveSize = Memory::ClampValidSizeAt(param->dataBuf.ptr, param->dataBufSize); // fallback, should never use this
data_ = param->dataBuf;
}
@@ -1117,6 +1127,13 @@ inline std::string FmtPspTime(const ScePspDateTime &dt) {
return StringFromFormat("%04d-%02d-%02d %02d:%02d:%02d.%06d", dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond);
}
// Whether entry `index` of a guest array is all in memory (PSPPointer only checks the first).
template <class T>
static bool EntryInRange(const PSPPointer<T> &entries, u32 index) {
const u64 end = (u64)entries.ptr + ((u64)index + 1) * sizeof(T);
return end <= 0x100000000ULL && Memory::IsValidRange(entries.ptr, (u32)(end - entries.ptr));
}
int SavedataParam::GetSizes(SceUtilitySavedataParam *param) {
if (!param) {
return SCE_UTILITY_SAVEDATA_ERROR_SIZES_NO_DATA;
@@ -1246,6 +1263,10 @@ bool SavedataParam::GetList(SceUtilitySavedataParam *param)
sfoFiles.push_back(sfoFile);
}
if (!validDir.empty() && !EntryInRange(param->idList->entries, (u32)validDir.size() - 1)) {
ERROR_LOG(Log::sceUtility, "SavedataParam::GetList(): entries %08x can't hold %d", param->idList->entries.ptr, (int)validDir.size());
return false;
}
SceUtilitySavedataIdListEntry *entries = param->idList->entries;
for (u32 i = 0; i < (u32)validDir.size(); i++)
{
@@ -1361,11 +1382,11 @@ int SavedataParam::GetFilesList(SceUtilitySavedataParam *param, u32 requestAddr,
SceUtilitySavedataFileListEntry *entry = NULL;
int sizeOffset = 0;
if (isSystemFile) {
if (fileList->systemEntries.IsValid() && fileList->resultNumSystemEntries < fileList->maxSystemEntries) {
if (fileList->systemEntries.IsValid() && fileList->resultNumSystemEntries < fileList->maxSystemEntries && EntryInRange(fileList->systemEntries, fileList->resultNumSystemEntries)) {
entry = &fileList->systemEntries[fileList->resultNumSystemEntries++];
}
} else if (secureFilenames.find(file.name) != secureFilenames.end()) {
if (fileList->secureEntries.IsValid() && fileList->resultNumSecureEntries < fileList->maxSecureEntries) {
if (fileList->secureEntries.IsValid() && fileList->resultNumSecureEntries < fileList->maxSecureEntries && EntryInRange(fileList->secureEntries, fileList->resultNumSecureEntries)) {
entry = &fileList->secureEntries[fileList->resultNumSecureEntries++];
}
// Secure files are slightly bigger.
@@ -1373,7 +1394,7 @@ int SavedataParam::GetFilesList(SceUtilitySavedataParam *param, u32 requestAddr,
sizeOffset = -0x10;
}
} else {
if (fileList->normalEntries.IsValid() && fileList->resultNumNormalEntries < fileList->maxNormalEntries) {
if (fileList->normalEntries.IsValid() && fileList->resultNumNormalEntries < fileList->maxNormalEntries && EntryInRange(fileList->normalEntries, fileList->resultNumNormalEntries)) {
entry = &fileList->normalEntries[fileList->resultNumNormalEntries++];
}
}
@@ -1437,6 +1458,14 @@ bool SavedataParam::GetSize(SceUtilitySavedataParam *param) {
auto listing = pspFileSystem.GetDirListing(saveDir, &exists);
const u64 freeBytes = MemoryStick_FreeSpace(GetGameName(param));
// The counts and both arrays come from the game.
if (param->sizeInfo->numNormalEntries < 0 || param->sizeInfo->numSecureEntries < 0 ||
(param->sizeInfo->numNormalEntries > 0 && !EntryInRange(param->sizeInfo->normalEntries, param->sizeInfo->numNormalEntries - 1)) ||
(param->sizeInfo->numSecureEntries > 0 && !EntryInRange(param->sizeInfo->secureEntries, param->sizeInfo->numSecureEntries - 1))) {
ERROR_LOG(Log::sceUtility, "SavedataParam::GetSize(): bad entry lists");
return false;
}
s64 overwriteBytes = 0;
s64 writeBytes = 0;
for (int i = 0; i < param->sizeInfo->numNormalEntries; ++i) {
+1 -1
View File
@@ -779,7 +779,7 @@ void PGF::SetFontPixel(u32 base, int bpl, int bufWidth, int bufHeight, int x, in
// glyph smears across them - see the "Linesize = 1" case in pspautotests font/charglyphimage.
// The bufWidth/bufHeight rectangle above and the address check below are what keep this sane.
int framebufferAddr = base + (y * bpl) + (pixelBytes == 0 ? x / 2 : x * pixelBytes);
if (!Memory::IsValidAddress(framebufferAddr)) {
if (!Memory::IsValidRange(framebufferAddr, pixelBytes == 0 ? 1 : pixelBytes)) {
return;
}
+16 -9
View File
@@ -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);
}
}
}
}
+19 -11
View File
@@ -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());
}
+6
View File
@@ -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
+27 -12
View File
@@ -33,6 +33,21 @@ typedef PSPPointer<char16_t> PSPWCharPointer;
typedef PSPPointer<const char> PSPConstCharPointer;
typedef PSPPointer<const char16_t> PSPConstWCharPointer;
// The converters walk the source to its terminator, so it has to have one in mapped memory.
static bool SourceTerminated(const PSPConstCharPointer &src) {
return Memory::IsValidNullTerminatedString(src.ptr);
}
static bool SourceTerminated(const PSPConstWCharPointer &src) {
const u32 maxChars = Memory::MaxSizeAtAddress(src.ptr) / 2;
const char16_t *p = (const char16_t *)Memory::GetPointerUnchecked(src.ptr);
for (u32 i = 0; i < maxChars; i++) {
if (p[i] == 0)
return true;
}
return false;
}
static u16 errorUTF8;
static u16 errorUTF16;
static u16 errorSJIS;
@@ -95,14 +110,14 @@ static int sceCccUTF8toUTF16(u32 dstAddr, u32 dstSize, u32 srcAddr)
{
const auto src = PSPConstCharPointer::Create(srcAddr);
auto dst = PSPWCharPointer::Create(dstAddr);
if (!dst.IsValid() || !src.IsValid())
if (!dst.IsValid() || !src.IsValid() || !SourceTerminated(src))
{
ERROR_LOG(Log::sceMisc, "sceCccUTF8toUTF16(%08x, %d, %08x): invalid pointers", dstAddr, dstSize, srcAddr);
return 0;
}
// Round dstSize down if it represents half a character.
const auto dstEnd = PSPWCharPointer::Create(dstAddr + (dstSize & ~1));
const auto dstEnd = PSPWCharPointer::Create(dstAddr + (Memory::ClampValidSizeAt(dstAddr, dstSize) & ~1));
DEBUG_LOG(Log::sceMisc, "sceCccUTF8toUTF16(%08x, %d, %08x)", dstAddr, dstSize, srcAddr);
UTF8 utf(src);
@@ -130,7 +145,7 @@ static int sceCccUTF8toSJIS(u32 dstAddr, u32 dstSize, u32 srcAddr)
{
const auto src = PSPConstCharPointer::Create(srcAddr);
auto dst = PSPCharPointer::Create(dstAddr);
if (!dst.IsValid() || !src.IsValid())
if (!dst.IsValid() || !src.IsValid() || !SourceTerminated(src))
{
ERROR_LOG(Log::sceMisc, "sceCccUTF8toSJIS(%08x, %d, %08x): invalid pointers", dstAddr, dstSize, srcAddr);
return 0;
@@ -141,7 +156,7 @@ static int sceCccUTF8toSJIS(u32 dstAddr, u32 dstSize, u32 srcAddr)
return 0;
}
const auto dstEnd = PSPCharPointer::Create(dstAddr + dstSize);
const auto dstEnd = PSPCharPointer::Create(dstAddr + Memory::ClampValidSizeAt(dstAddr, dstSize));
DEBUG_LOG(Log::sceMisc, "sceCccUTF8toSJIS(%08x, %d, %08x)", dstAddr, dstSize, srcAddr);
UTF8 utf(src);
@@ -169,13 +184,13 @@ static int sceCccUTF16toUTF8(u32 dstAddr, u32 dstSize, u32 srcAddr)
{
const auto src = PSPConstWCharPointer::Create(srcAddr);
auto dst = PSPCharPointer::Create(dstAddr);
if (!dst.IsValid() || !src.IsValid())
if (!dst.IsValid() || !src.IsValid() || !SourceTerminated(src))
{
ERROR_LOG(Log::sceMisc, "sceCccUTF16toUTF8(%08x, %d, %08x): invalid pointers", dstAddr, dstSize, srcAddr);
return 0;
}
const auto dstEnd = PSPCharPointer::Create(dstAddr + dstSize);
const auto dstEnd = PSPCharPointer::Create(dstAddr + Memory::ClampValidSizeAt(dstAddr, dstSize));
DEBUG_LOG(Log::sceMisc, "sceCccUTF16toUTF8(%08x, %d, %08x)", dstAddr, dstSize, srcAddr);
UTF16LE utf(src);
@@ -202,7 +217,7 @@ static int sceCccUTF16toSJIS(u32 dstAddr, u32 dstSize, u32 srcAddr)
{
const auto src = PSPConstWCharPointer::Create(srcAddr);
auto dst = PSPCharPointer::Create(dstAddr);
if (!dst.IsValid() || !src.IsValid())
if (!dst.IsValid() || !src.IsValid() || !SourceTerminated(src))
{
ERROR_LOG(Log::sceMisc, "sceCccUTF16toSJIS(%08x, %d, %08x): invalid pointers", dstAddr, dstSize, srcAddr);
return 0;
@@ -213,7 +228,7 @@ static int sceCccUTF16toSJIS(u32 dstAddr, u32 dstSize, u32 srcAddr)
return 0;
}
const auto dstEnd = PSPCharPointer::Create(dstAddr + dstSize);
const auto dstEnd = PSPCharPointer::Create(dstAddr + Memory::ClampValidSizeAt(dstAddr, dstSize));
DEBUG_LOG(Log::sceMisc, "sceCccUTF16toSJIS(%08x, %d, %08x)", dstAddr, dstSize, srcAddr);
UTF16LE utf(src);
@@ -240,7 +255,7 @@ static int sceCccSJIStoUTF8(u32 dstAddr, u32 dstSize, u32 srcAddr)
{
const auto src = PSPConstCharPointer::Create(srcAddr);
auto dst = PSPCharPointer::Create(dstAddr);
if (!dst.IsValid() || !src.IsValid())
if (!dst.IsValid() || !src.IsValid() || !SourceTerminated(src))
{
ERROR_LOG(Log::sceMisc, "sceCccSJIStoUTF8(%08x, %d, %08x): invalid pointers", dstAddr, dstSize, srcAddr);
return 0;
@@ -251,7 +266,7 @@ static int sceCccSJIStoUTF8(u32 dstAddr, u32 dstSize, u32 srcAddr)
return 0;
}
const auto dstEnd = PSPCharPointer::Create(dstAddr + dstSize);
const auto dstEnd = PSPCharPointer::Create(dstAddr + Memory::ClampValidSizeAt(dstAddr, dstSize));
DEBUG_LOG(Log::sceMisc, "sceCccSJIStoUTF8(%08x, %d, %08x)", dstAddr, dstSize, srcAddr);
ShiftJIS sjis(src);
@@ -278,7 +293,7 @@ static int sceCccSJIStoUTF16(u32 dstAddr, u32 dstSize, u32 srcAddr)
{
const auto src = PSPConstCharPointer::Create(srcAddr);
auto dst = PSPWCharPointer::Create(dstAddr);
if (!dst.IsValid() || !src.IsValid())
if (!dst.IsValid() || !src.IsValid() || !SourceTerminated(src))
{
ERROR_LOG(Log::sceMisc, "sceCccSJIStoUTF16(%08x, %d, %08x): invalid pointers", dstAddr, dstSize, srcAddr);
return 0;
@@ -289,7 +304,7 @@ static int sceCccSJIStoUTF16(u32 dstAddr, u32 dstSize, u32 srcAddr)
return 0;
}
const auto dstEnd = PSPWCharPointer::Create(dstAddr + (dstSize & ~1));
const auto dstEnd = PSPWCharPointer::Create(dstAddr + (Memory::ClampValidSizeAt(dstAddr, dstSize) & ~1));
DEBUG_LOG(Log::sceMisc, "sceCccSJIStoUTF16(%08x, %d, %08x)", dstAddr, dstSize, srcAddr);
ShiftJIS sjis(src);
+9 -1
View File
@@ -223,6 +223,9 @@ static int sub_17A8(KirkState *kirk, u8* data)
static int sceSdGetLastIndex(u32 addressCtx, u32 addressHash, u32 addressKey) {
auto ctx = PSPPointer<pspChnnlsvContext1>::Create(addressCtx);
// The hash and key are 16 bytes each.
if (!Memory::IsValidRange(addressHash, 16) || (addressKey && !Memory::IsValidRange(addressKey, 16)))
return hleLogError(Log::sceMisc, 0, "Invalid pointer");
u8 *hash = Memory::GetPointerWriteOrException(addressHash);
if (!ctx.IsValid() || !hash)
return hleLogError(Log::sceMisc, 0, "Invalid pointer");
@@ -354,7 +357,7 @@ int sceSdMacInit(pspChnnlsvContext1& ctx, int value)
static int sceSdRemoveValue(u32 addressCtx, u32 addressData, int length) {
auto ctx = PSPPointer<pspChnnlsvContext1>::Create(addressCtx);
if (!ctx.IsValid() || !Memory::IsValidAddress(addressData))
if (!ctx.IsValid() || !Memory::IsValidAddress(addressData) || length < 0 || !Memory::IsValidRange(addressData, (u32)length))
return hleLogError(Log::sceMisc, 0, "Invalid pointer");
return hleLogDebug(Log::sceMisc, sceSdMacUpdate(*ctx, Memory::GetPointerWriteOrException(addressData), length));
}
@@ -405,6 +408,8 @@ int sceSdMacUpdate(pspChnnlsvContext1& ctx, const u8* data, int length)
static int sceSdCreateList(u32 ctx2Addr, int mode, int unkwn, u32 dataAddr, u32 cryptkeyAddr) {
auto ctx2 = PSPPointer<pspChnnlsvContext2>::Create(ctx2Addr);
if (!Memory::IsValidRange(dataAddr, 16) || (cryptkeyAddr && !Memory::IsValidRange(cryptkeyAddr, 16)))
return hleLogError(Log::sceMisc, 0, "Invalid pointer");
u8* data = Memory::GetPointerWriteOrException(dataAddr);
// Optional, as in sceSdGetLastIndex.
u8* cryptkey = cryptkeyAddr ? Memory::GetPointerWriteOrException(cryptkeyAddr) : nullptr;
@@ -471,6 +476,9 @@ int sceSdCipherInit(pspChnnlsvContext2& ctx2, int mode, int uknw, u8* data, cons
static int sceSdSetMember(u32 ctxAddr, u32 dataAddr, int alignedLen) {
auto ctx = PSPPointer<pspChnnlsvContext2>::Create(ctxAddr);
// The data is decrypted in place.
if (alignedLen < 0 || !Memory::IsValidRange(dataAddr, (u32)alignedLen))
return hleLogError(Log::sceMisc, 0, "Invalid pointer");
u8 *data = Memory::GetPointerWriteOrException(dataAddr);
if (!ctx.IsValid() || !data)
return hleLogError(Log::sceMisc, 0, "Invalid pointer");
+4 -1
View File
@@ -15,6 +15,8 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <algorithm>
#include "zlib.h"
#include "Common/CommonTypes.h"
@@ -41,7 +43,8 @@ static int CommonDecompress(int windowBits, u32 OutBuffer, int OutBufferLength,
// We don't know the available length, just let it use as much as it wants.
stream.avail_in = (uInt)Memory::ClampValidSizeAt(InBuffer, Memory::g_MemorySize);
stream.next_out = outBufferPtr;
stream.avail_out = (uInt)OutBufferLength;
// The game's length isn't checked against memory anywhere else.
stream.avail_out = (uInt)Memory::ClampValidSizeAt(OutBuffer, (u32)std::max(OutBufferLength, 0));
int err = inflateInit2(&stream, windowBits);
if (err != Z_OK) {
+9 -7
View File
@@ -1316,14 +1316,14 @@ static int sceFontFindOptimumFont(u32 libHandle, u32 fontStylePtr, u32 errorCode
return hleReportError(Log::sceFont, 0, "invalid font lib");
}
if (!Memory::IsValidAddress(fontStylePtr)) {
if (!Memory::IsValidRange(fontStylePtr, sizeof(PGFFontStyle))) {
// Yes, actually. Must've been a typo in the library.
*errorCode = SCE_FONT_ERROR_INVALID_LIBID;
return hleReportError(Log::sceFont, 0, "invalid style address");
}
auto requestedStyle = PSPPointer<const PGFFontStyle>::Create(fontStylePtr);
DEBUG_LOG(Log::sceFont, "requestedStyle fontAttributes %i,fontCountry %i,fontExpire %i,fontFamily %i,fontFileName %s,fontH %f,fontHRes %f,fontLanguage %i,fontName %s,fontRegion %i,fontStyle %i,fontStyleSub %i,fontV %f,fontVRes %f,fontWeight %f", requestedStyle->fontAttributes, requestedStyle->fontCountry, requestedStyle->fontExpire, requestedStyle->fontFamily, requestedStyle->fontFileName, requestedStyle->fontH, requestedStyle->fontHRes, requestedStyle->fontLanguage, requestedStyle->fontName, requestedStyle->fontRegion, requestedStyle->fontStyle, requestedStyle->fontStyleSub, requestedStyle->fontV, requestedStyle->fontVRes, requestedStyle->fontWeight);
DEBUG_LOG(Log::sceFont, "requestedStyle fontAttributes %i,fontCountry %i,fontExpire %i,fontFamily %i,fontFileName %.64s,fontH %f,fontHRes %f,fontLanguage %i,fontName %.64s,fontRegion %i,fontStyle %i,fontStyleSub %i,fontV %f,fontVRes %f,fontWeight %f", requestedStyle->fontAttributes, requestedStyle->fontCountry, requestedStyle->fontExpire, requestedStyle->fontFamily, requestedStyle->fontFileName, requestedStyle->fontH, requestedStyle->fontHRes, requestedStyle->fontLanguage, requestedStyle->fontName, requestedStyle->fontRegion, requestedStyle->fontStyle, requestedStyle->fontStyleSub, requestedStyle->fontV, requestedStyle->fontVRes, requestedStyle->fontWeight);
// Find the first nearest match for H/V, OR the last exact match for others.
float hRes = requestedStyle->fontHRes > 0.0f ? (float)requestedStyle->fontHRes : fontLib->FontHRes();
@@ -1383,7 +1383,7 @@ static int sceFontFindFont(u32 libHandle, u32 fontStylePtr, u32 errorCodePtr) {
return hleNoLog(0);
}
if (!Memory::IsValidAddress(fontStylePtr)) {
if (!Memory::IsValidRange(fontStylePtr, sizeof(PGFFontStyle))) {
ERROR_LOG(Log::sceFont, "sceFontFindFont(%08x, %08x, %08x): invalid style address", libHandle, fontStylePtr, errorCodePtr);
*errorCode = SCE_FONT_ERROR_INVALID_PARAMETER;
return hleNoLog(0);
@@ -1392,7 +1392,7 @@ static int sceFontFindFont(u32 libHandle, u32 fontStylePtr, u32 errorCodePtr) {
DEBUG_LOG(Log::sceFont, "sceFontFindFont(%x, %x, %x)", libHandle, fontStylePtr, errorCodePtr);
auto requestedStyle = PSPPointer<const PGFFontStyle>::Create(fontStylePtr);
DEBUG_LOG(Log::sceFont, "requestedStyle fontAttributes %i,fontCountry %i,fontExpire %i,fontFamily %i,fontFileName %s,fontH %f,fontHRes %f,fontLanguage %i,fontName %s,fontRegion %i,fontStyle %i,fontStyleSub %i,fontV %f,fontVRes %f,fontWeight %f", requestedStyle->fontAttributes, requestedStyle->fontCountry, requestedStyle->fontExpire, requestedStyle->fontFamily, requestedStyle->fontFileName, requestedStyle->fontH, requestedStyle->fontHRes, requestedStyle->fontLanguage, requestedStyle->fontName, requestedStyle->fontRegion, requestedStyle->fontStyle, requestedStyle->fontStyleSub, requestedStyle->fontV, requestedStyle->fontVRes, requestedStyle->fontWeight);
DEBUG_LOG(Log::sceFont, "requestedStyle fontAttributes %i,fontCountry %i,fontExpire %i,fontFamily %i,fontFileName %.64s,fontH %f,fontHRes %f,fontLanguage %i,fontName %.64s,fontRegion %i,fontStyle %i,fontStyleSub %i,fontV %f,fontVRes %f,fontWeight %f", requestedStyle->fontAttributes, requestedStyle->fontCountry, requestedStyle->fontExpire, requestedStyle->fontFamily, requestedStyle->fontFileName, requestedStyle->fontH, requestedStyle->fontHRes, requestedStyle->fontLanguage, requestedStyle->fontName, requestedStyle->fontRegion, requestedStyle->fontStyle, requestedStyle->fontStyleSub, requestedStyle->fontV, requestedStyle->fontVRes, requestedStyle->fontWeight);
// Find the closest exact match for the fields specified.
float hRes = requestedStyle->fontHRes > 0.0f ? (float)requestedStyle->fontHRes : fontLib->FontHRes();
@@ -1418,7 +1418,7 @@ static int sceFontFindFont(u32 libHandle, u32 fontStylePtr, u32 errorCodePtr) {
}
static int sceFontGetFontInfo(u32 fontHandle, u32 fontInfoPtr) {
if (!Memory::IsValidAddress(fontInfoPtr)) {
if (!Memory::IsValidRange(fontInfoPtr, sizeof(PGFFontInfo))) {
return hleLogError(Log::sceFont, SCE_FONT_ERROR_INVALID_PARAMETER, "bad fontInfo pointer");
}
LoadedFont *font = GetLoadedFont(fontHandle, true);
@@ -1455,7 +1455,7 @@ static int sceFontGetFontInfoByIndexNumber(u32 libHandle, u32 fontInfoPtr, u32 i
static int sceFontGetCharInfo(u32 fontHandle, u32 charCode, u32 charInfoPtr) {
charCode &= 0xffff;
if (!Memory::IsValidAddress(charInfoPtr)) {
if (!Memory::IsValidRange(charInfoPtr, sizeof(PGFCharInfo))) {
return hleLogError(Log::sceFont, SCE_FONT_ERROR_INVALID_PARAMETER, "bad charInfo pointer");
}
LoadedFont *font = GetLoadedFont(fontHandle, true);
@@ -1494,7 +1494,7 @@ static int sceFontGetCharInfo(u32 fontHandle, u32 charCode, u32 charInfoPtr) {
static int sceFontGetShadowInfo(u32 fontHandle, u32 charCode, u32 charInfoPtr) {
charCode &= 0xffff;
if (!Memory::IsValidAddress(charInfoPtr)) {
if (!Memory::IsValidRange(charInfoPtr, sizeof(PGFCharInfo))) {
return hleLogError(Log::sceFont, SCE_FONT_ERROR_INVALID_PARAMETER, "bad charInfo pointer");
}
LoadedFont *font = GetLoadedFont(fontHandle, true);
@@ -1618,6 +1618,8 @@ static int sceFontGetFontList(u32 fontLibHandle, u32 fontStylePtr, int numFonts)
DEBUG_LOG(Log::sceFont, "sceFontGetFontList(%08x, %08x, %i)", fontLibHandle, fontStylePtr, numFonts);
if (fl->handle() != 0) {
numFonts = std::min(numFonts, (int)internalFonts.size());
// Only the first entry was checked above.
numFonts = std::min(numFonts, (int)(Memory::ClampValidSizeAt(fontStylePtr, (u32)std::max(numFonts, 0) * sizeof(PGFFontStyle)) / sizeof(PGFFontStyle)));
for (int i = 0; i < numFonts; i++)
fontStyles[i] = internalFonts[i]->GetFontStyle();
}
+3 -2
View File
@@ -605,7 +605,8 @@ u32 sceGeSaveContext(u32 ctxAddr) {
}
// Let's just dump gstate.
if (Memory::IsValidAddress(ctxAddr)) {
// A context (SceGeContext) is 512 words.
if (Memory::IsValidRange(ctxAddr, 512 * 4)) {
gstate.Save((u32_le *)Memory::GetPointerOrException(ctxAddr));
}
@@ -619,7 +620,7 @@ u32 sceGeRestoreContext(u32 ctxAddr) {
return hleLogWarning(Log::sceGe, SCE_KERNEL_ERROR_BUSY, "lists in process, aborting");
}
if (Memory::IsValidAddress(ctxAddr)) {
if (Memory::IsValidRange(ctxAddr, 512 * 4)) {
gstate.Restore((u32_le *)Memory::GetPointerOrException(ctxAddr));
}
+15 -7
View File
@@ -1145,6 +1145,11 @@ static bool __IoRead(int &result, int id, u32 data_addr, int size, int &us) {
NotifyMemInfo(MemBlockFlags::WRITE, data_addr, size, tag.c_str(), tag.size());
u8 *data = (u8 *)Memory::GetPointerUnchecked(data_addr);
u32 validSize = Memory::ClampValidSizeAt(data_addr, size);
// Block devices count the size in sectors, so clamp what that many sectors cover.
if ((pspFileSystem.DevType(f->handle) & PSPDevType::BLOCK) != 0) {
const u32 bytes = (u32)std::min<u64>((u64)size * 2048, 0xFFFFFFFF);
validSize = Memory::ClampValidSizeAt(data_addr, bytes) / 2048;
}
if (f->npdrm) {
result = npdrmRead(f, data, validSize);
currentMIPS->InvalidateICacheRangeDeferred(data_addr, validSize);
@@ -2755,21 +2760,23 @@ static u32 sceIoDread(MIPSState *mips, int id, u32 dirent_addr) {
// write d_private for supporting Custom BGM
// ref JPCSP https://code.google.com/p/jpcsp/source/detail?r=3468
if (Memory::IsValidAddress(entry->d_private)){
// What's written depends on the layout, so check each one's whole range below.
if (sceKernelGetCompiledSdkVersion() <= 0x0307FFFF){
// d_private is pointing to an area of unknown size
// - [0..12] "8.3" file name (null-terminated), could be empty.
// - [13..???] long file name (null-terminated)
strcpy_limit((char*)Memory::GetPointerUnchecked(entry->d_private), shortName.c_str(), 13);
strcpy_limit((char*)Memory::GetPointerUnchecked(entry->d_private + 13), (const char*)entry->d_name, ARRAY_SIZE(entry->d_name));
if (Memory::IsValidRange(entry->d_private, 13 + (u32)ARRAY_SIZE(entry->d_name))) {
strcpy_limit((char*)Memory::GetPointerUnchecked(entry->d_private), shortName.c_str(), 13);
strcpy_limit((char*)Memory::GetPointerUnchecked(entry->d_private + 13), (const char*)entry->d_name, ARRAY_SIZE(entry->d_name));
}
}
else {
// d_private is pointing to an area of total size 1044
// - [0..3] size of area
// - [4..19] "8.3" file name (null-terminated), could be empty.
// - [20..???] long file name (null-terminated)
auto size = Memory::ReadUnchecked_U32(entry->d_private);
if (size >= 1044) {
auto size = Memory::IsValidRange(entry->d_private, 4) ? Memory::ReadUnchecked_U32(entry->d_private) : 0;
if (size >= 1044 && Memory::IsValidRange(entry->d_private, 1044)) {
strcpy_limit((char*)Memory::GetPointerUnchecked(entry->d_private + 4), shortName.c_str(), 16);
strcpy_limit((char*)Memory::GetPointerUnchecked(entry->d_private + 20), (const char*)entry->d_name, ARRAY_SIZE(entry->d_name));
}
@@ -3087,7 +3094,7 @@ int __IoIoctl(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 out
u8 pgd_header[0x90]{};
u8 pgd_magic[4] = {0x00, 0x50, 0x47, 0x44};
if (Memory::IsValidAddress(indataPtr) && inlen == 16) {
if (Memory::IsValidRange(indataPtr, 16) && inlen == 16) {
memcpy(keybuf, Memory::GetPointerUnchecked(indataPtr), 16);
key_ptr = keybuf;
}else{
@@ -3285,7 +3292,8 @@ int __IoIoctl(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 out
//if (GetIOTimingMethod() == IOTIMING_REALISTIC) // Need a check for io timing method?
usec += 15000;// Fantasy Golf Pangya Portable(KS) needs a delay over 15000us.
if (Memory::IsValidAddress(indataPtr) && inlen >= 4) {
// The whole struct is read, whatever inlen says.
if (Memory::IsValidRange(indataPtr, 16) && inlen >= 4) {
struct SeekInfo {
u64_le offset;
u32_le unk;
+5 -5
View File
@@ -31,7 +31,7 @@
// Not really sure where these belong - is it worth giving them their own file?
u32 sceKernelUtilsMt19937Init(u32 ctx, u32 seed) {
DEBUG_LOG(Log::HLE, "sceKernelUtilsMt19937Init(%08x, %08x)", ctx, seed);
if (!Memory::IsValidAddress(ctx))
if (!Memory::IsValidRange(ctx, sizeof(MersenneTwister)))
return -1;
void *ptr = Memory::GetPointerWriteOrException(ctx);
// This is made to match the memory layout of a PSP MT structure exactly.
@@ -42,7 +42,7 @@ u32 sceKernelUtilsMt19937Init(u32 ctx, u32 seed) {
u32 sceKernelUtilsMt19937UInt(u32 ctx) {
VERBOSE_LOG(Log::HLE, "sceKernelUtilsMt19937UInt(%08x)", ctx);
if (!Memory::IsValidAddress(ctx))
if (!Memory::IsValidRange(ctx, sizeof(MersenneTwister)))
return -1;
MersenneTwister *mt = (MersenneTwister *)Memory::GetPointerUnchecked(ctx);
return mt->R32();
@@ -161,7 +161,7 @@ static int Md5BlockResult(u32 ctxAddr, u32 digestAddr) {
static int sceMd5Digest(u32 dataAddr, u32 len, u32 digestAddr) {
DEBUG_LOG(Log::HLE, "sceMd5Digest(%08x, %d, %08x)", dataAddr, len, digestAddr);
if (!Memory::IsValidAddress(dataAddr) || !Memory::IsValidAddress(digestAddr))
if (!Memory::IsValidAddress(dataAddr) || !Memory::IsValidRange(dataAddr, (u32)len) || !Memory::IsValidRange(digestAddr, 16))
return -1;
ppsspp_md5(Memory::GetPointerWriteUnchecked(dataAddr), (int)len, Memory::GetPointerWriteUnchecked(digestAddr));
@@ -183,7 +183,7 @@ static int sceMd5BlockResult(u32 ctxAddr, u32 digestAddr) {
int sceKernelUtilsMd5Digest(u32 dataAddr, int len, u32 digestAddr) {
DEBUG_LOG(Log::HLE, "sceKernelUtilsMd5Digest(%08x, %d, %08x)", dataAddr, len, digestAddr);
if (!Memory::IsValidAddress(dataAddr) || !Memory::IsValidAddress(digestAddr))
if (!Memory::IsValidAddress(dataAddr) || !Memory::IsValidRange(dataAddr, (u32)len) || !Memory::IsValidRange(digestAddr, 16))
return -1;
ppsspp_md5(Memory::GetPointerWriteUnchecked(dataAddr), (int)len, Memory::GetPointerWriteUnchecked(digestAddr));
@@ -262,7 +262,7 @@ static int Sha1BlockInit(u32 ctxAddr) {
int sceKernelUtilsSha1Digest(u32 dataAddr, int len, u32 digestAddr) {
DEBUG_LOG(Log::HLE, "sceKernelUtilsSha1Digest(%08x, %d, %08x)", dataAddr, len, digestAddr);
if (!Memory::IsValidAddress(dataAddr) || !Memory::IsValidAddress(digestAddr))
if (!Memory::IsValidAddress(dataAddr) || !Memory::IsValidRange(dataAddr, (u32)len) || !Memory::IsValidRange(digestAddr, 20))
return -1;
sha1(Memory::GetPointerWriteUnchecked(dataAddr), (int)len, Memory::GetPointerWriteUnchecked(digestAddr));
+9 -3
View File
@@ -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;
+3
View File
@@ -962,6 +962,9 @@ static bool decodePmpVideo(PSPPointer<SceMpegRingBuffer> ringbuffer, u32 pmpctxA
// joint all blocks into H264Frames
for (int i = 0; i < pmp_nBlocks; i++){
auto lli = PSPPointer<SceMpegLLI>::Create(pmp_videoSource);
// Each block descriptor is in game memory, after the first.
if (!lli.IsValid())
break;
// add source block into pmpframes
const uint8_t *ptr = Memory::GetPointerRangeOrException(lli->pSrc, lli->iSize);
if (ptr)
+2 -2
View File
@@ -31,7 +31,7 @@
static u32 sceMt19937Init(u32 mt19937Addr, u32 seed)
{
if (!Memory::IsValidAddress(mt19937Addr))
if (!Memory::IsValidRange(mt19937Addr, sizeof(MersenneTwister)))
return hleLogError(Log::HLE, -1);
void *ptr = Memory::GetPointerWriteUnchecked(mt19937Addr);
// This is made to match the memory layout of a PSP MT structure exactly.
@@ -42,7 +42,7 @@ static u32 sceMt19937Init(u32 mt19937Addr, u32 seed)
static u32 sceMt19937UInt(u32 mt19937Addr)
{
if (!Memory::IsValidAddress(mt19937Addr))
if (!Memory::IsValidRange(mt19937Addr, sizeof(MersenneTwister)))
return hleLogError(Log::HLE, -1);
MersenneTwister *mt = (MersenneTwister *)Memory::GetPointerOrException(mt19937Addr);
return hleLogVerbose(Log::HLE, mt->R32());
+6 -2
View File
@@ -3401,6 +3401,9 @@ int NetAdhocctl_CreateEnterGameMode(const char* group_name, int game_type, int n
if (game_type < ADHOCCTL_GAMETYPE_1A || game_type > ADHOCCTL_GAMETYPE_2A || num_members < 2 || num_members > 16 || (game_type == ADHOCCTL_GAMETYPE_1A && num_members > 4))
return SCE_NET_ADHOCCTL_ERROR_INVALID_ARG;
// The whole member list is read below.
if (!Memory::IsValidRange(membersAddr, num_members * (u32)sizeof(SceNetEtherAddr)))
return SCE_NET_ADHOCCTL_ERROR_INVALID_ARG;
// In a normal group, it reports ALREADY_CONNECTED through the handlers and stays in it
// (pspautotests net/adhoc/gamemode).
@@ -4625,7 +4628,8 @@ static int sceNetAdhocGameModeCreateMaster(MIPSState *mips, u32 dataAddr, int si
if (!netAdhocGameModeEntered)
return hleLogError(Log::sceNet, SCE_NET_ADHOCCTL_ERROR_NOT_ENTER_GAMEMODE, "not enter gamemode");
if (size < 0 || !Memory::IsValidAddress(dataAddr))
// The data is copied (and the buffers are sized) by size.
if (size < 0 || !Memory::IsValidAddress(dataAddr) || !Memory::IsValidRange(dataAddr, (u32)size))
return hleLogError(Log::sceNet, SCE_NET_ADHOCCTL_ERROR_INVALID_ARG, "invalid arg");
// pspautotests net/adhoc/pair/gamemode
@@ -4682,7 +4686,7 @@ static int sceNetAdhocGameModeCreateReplica(MIPSState *mips, const char *mac, u3
if (!netAdhocGameModeEntered)
return hleLogError(Log::sceNet, SCE_NET_ADHOCCTL_ERROR_NOT_ENTER_GAMEMODE, "not enter gamemode");
if (mac == nullptr || size < 0 || !Memory::IsValidAddress(dataAddr))
if (mac == nullptr || size < 0 || !Memory::IsValidAddress(dataAddr) || !Memory::IsValidRange(dataAddr, (u32)size))
return hleLogError(Log::sceNet, SCE_NET_ADHOCCTL_ERROR_INVALID_ARG, "invalid arg");
const SceNetEtherAddr &replicaMac = *(const SceNetEtherAddr *)mac;
+5
View File
@@ -1595,6 +1595,11 @@ static int sceNetAdhocMatchingCreate(MIPSState *mips, int mode, int maxnum, int
// Invalid Receive Buffer Size
return hleLogError(Log::sceNet, SCE_NET_ADHOC_MATCHING_ERROR_RXBUF_TOO_SHORT, "adhoc matching rxbuf too short");
}
// We allocate (and clear) this on the host. The PSP takes it from the library's small heap, and
// no packet is anywhere near this big.
if (rxbuflen > 1024 * 1024) {
return hleLogError(Log::sceNet, SCE_NET_ADHOC_MATCHING_ERROR_NO_SPACE, "adhoc matching rxbuf too large");
}
if (mode < 1 || mode > 3) {
return hleLogError(Log::sceNet, SCE_NET_ADHOC_MATCHING_ERROR_INVALID_MODE, "adhoc matching invalid mode");
+10 -3
View File
@@ -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");
}
+8 -8
View File
@@ -451,7 +451,7 @@ static u32 sceRtcIsLeapYear(u32 year)
static int sceRtcConvertLocalTimeToUTC(u32 tickLocalPtr,u32 tickUTCPtr)
{
DEBUG_LOG(Log::sceRtc, "sceRtcConvertLocalTimeToUTC(%d, %d)", tickLocalPtr, tickUTCPtr);
if (Memory::IsValidRange(tickLocalPtr, 8) && Memory::IsValidAddress(tickUTCPtr))
if (Memory::IsValidRange(tickLocalPtr, 8) && Memory::IsValidRange(tickUTCPtr, 8))
{
u64 srcTick = Memory::ReadUnchecked_U64(tickLocalPtr);
// TODO : Let the user select his timezone / daylight saving instead of taking system param ?
@@ -606,7 +606,7 @@ static int sceRtcGetDosTime(u32 datePtr, u32 dosTime) {
}
static int sceRtcSetWin32FileTime(u32 datePtr, u64 win32Time) {
if (!Memory::IsValidAddress(datePtr))
if (!Memory::IsValidRange(datePtr, sizeof(ScePspDateTime)))
{
ERROR_LOG_REPORT(Log::sceRtc, "sceRtcSetWin32FileTime(%08x, %lld): invalid address", datePtr, win32Time);
return -1;
@@ -621,7 +621,7 @@ static int sceRtcSetWin32FileTime(u32 datePtr, u64 win32Time) {
}
static int sceRtcGetWin32FileTime(u32 datePtr, u32 win32TimePtr) {
if (!Memory::IsValidAddress(datePtr)) {
if (!Memory::IsValidRange(datePtr, sizeof(ScePspDateTime))) {
ERROR_LOG_REPORT(Log::sceRtc, "sceRtcGetWin32FileTime(%08x, %08x): invalid address", datePtr, win32TimePtr);
return -1;
}
@@ -917,7 +917,7 @@ static bool rtcParseRFC2822(const char *s, RtcParseResult &r) {
static int sceRtcParseDateTime(u32 destTickPtr, u32 dateStringPtr)
{
if (!Memory::IsValid4AlignedRange(destTickPtr, 8) || !Memory::IsValidAddress(dateStringPtr))
if (!Memory::IsValid4AlignedRange(destTickPtr, 8) || !Memory::IsValidNullTerminatedString(dateStringPtr))
return hleLogError(Log::sceRtc, -1, "bad address");
const char *s = Memory::GetCharPointer(dateStringPtr);
@@ -1052,7 +1052,7 @@ static int __RtcFormatRFC3339(u32 outPtr, u32 srcTickPtr, int tz) {
static int sceRtcFormatRFC2822(u32 outPtr, u32 srcTickPtr, int tz)
{
if (!Memory::IsValidAddress(outPtr) || !Memory::IsValid4AlignedRange(srcTickPtr, 8))
if (!Memory::IsValidRange(outPtr, 32) || !Memory::IsValid4AlignedRange(srcTickPtr, 8))
{
// TODO: Not well tested.
ERROR_LOG(Log::sceRtc, "sceRtcFormatRFC2822(%08x, %08x, %d): invalid address", outPtr, srcTickPtr, tz);
@@ -1065,7 +1065,7 @@ static int sceRtcFormatRFC2822(u32 outPtr, u32 srcTickPtr, int tz)
static int sceRtcFormatRFC2822LocalTime(u32 outPtr, u32 srcTickPtr)
{
if (!Memory::IsValidAddress(outPtr) || !Memory::IsValid4AlignedRange(srcTickPtr, 8))
if (!Memory::IsValidRange(outPtr, 32) || !Memory::IsValid4AlignedRange(srcTickPtr, 8))
{
// TODO: Not well tested.
ERROR_LOG(Log::sceRtc, "sceRtcFormatRFC2822LocalTime(%08x, %08x): invalid address", outPtr, srcTickPtr);
@@ -1089,7 +1089,7 @@ static int sceRtcFormatRFC2822LocalTime(u32 outPtr, u32 srcTickPtr)
static int sceRtcFormatRFC3339(u32 outPtr, u32 srcTickPtr, int tz)
{
if (!Memory::IsValidAddress(outPtr) || !Memory::IsValid4AlignedRange(srcTickPtr, 8))
if (!Memory::IsValidRange(outPtr, 32) || !Memory::IsValid4AlignedRange(srcTickPtr, 8))
{
// TODO: Not well tested.
ERROR_LOG(Log::sceRtc, "sceRtcFormatRFC3339(%08x, %08x, %d): invalid address", outPtr, srcTickPtr, tz);
@@ -1102,7 +1102,7 @@ static int sceRtcFormatRFC3339(u32 outPtr, u32 srcTickPtr, int tz)
static int sceRtcFormatRFC3339LocalTime(u32 outPtr, u32 srcTickPtr)
{
if (!Memory::IsValidAddress(outPtr) || !Memory::IsValid4AlignedRange(srcTickPtr, 8))
if (!Memory::IsValidRange(outPtr, 32) || !Memory::IsValid4AlignedRange(srcTickPtr, 8))
{
// TODO: Not well tested.
ERROR_LOG(Log::sceRtc, "sceRtcFormatRFC3339LocalTime(%08x, %08x): invalid address", outPtr, srcTickPtr);
+16 -6
View File
@@ -26,6 +26,16 @@
#include "Core/HLE/sceSfmt19937.h"
// The whole state struct is in game memory.
static bool ValidSfmt(u32 sfmt) {
return Memory::IsValidRange(sfmt, sizeof(sfmt_t));
}
// In 64 bits, since count * size wraps (and the end can straddle a gap between regions).
static bool ValidArray(u32 addr, int count, int elemSize) {
return count > 0 && (u64)count * elemSize <= 0xFFFFFFFF && Memory::IsValidRange(addr, (u32)((u64)count * elemSize));
}
// 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);
@@ -33,7 +43,7 @@ static bool ValidSfmtIndex(u32 sfmt) {
}
static int sceSfmt19937InitGenRand(u32 sfmt, u32 seed) {
if (!Memory::IsValidAddress(sfmt)) {
if (!ValidSfmt(sfmt)) {
ERROR_LOG(Log::HLE, "sceSfmt19937InitGenRand(sfmt=%08x, seed=%08x) - bad address(es)", sfmt, seed);
return -1;
}
@@ -46,7 +56,7 @@ static int sceSfmt19937InitGenRand(u32 sfmt, u32 seed) {
}
static int sceSfmt19937InitByArray(u32 sfmt, u32 seeds, int seedslen) {
if (!Memory::IsValidAddress(sfmt) || !Memory::IsValidAddress(seeds) || !Memory::IsValidAddress(seeds + 4 * (seedslen - 1))) {
if (!ValidSfmt(sfmt) || !ValidArray(seeds, seedslen, 4)) {
ERROR_LOG(Log::HLE, "sceSfmt19937InitByArray(sfmt=%08x, seeds=%08x, seedslen=%08x) - bad address(es)", sfmt, seeds, seedslen);
return -1;
}
@@ -60,7 +70,7 @@ static int sceSfmt19937InitByArray(u32 sfmt, u32 seeds, int seedslen) {
}
static u32 sceSfmt19937GenRand32(u32 sfmt) {
if (!Memory::IsValidAddress(sfmt) || !ValidSfmtIndex(sfmt)) {
if (!ValidSfmt(sfmt) || !ValidSfmtIndex(sfmt)) {
ERROR_LOG(Log::HLE, "sceSfmt19937GenRand32(sfmt=%08x) - bad address(es)", sfmt);
return -1;
}
@@ -73,7 +83,7 @@ static u32 sceSfmt19937GenRand32(u32 sfmt) {
}
static u64 sceSfmt19937GenRand64(u32 sfmt) {
if (!Memory::IsValidAddress(sfmt) || !ValidSfmtIndex(sfmt)) {
if (!ValidSfmt(sfmt) || !ValidSfmtIndex(sfmt)) {
ERROR_LOG(Log::HLE, "sceSfmt19937GenRand64(sfmt=%08x) - bad address(es)", sfmt);
return -1;
}
@@ -87,7 +97,7 @@ static u64 sceSfmt19937GenRand64(u32 sfmt) {
static int sceSfmt19937FillArray32(u32 sfmt, u32 array, int arraylen) {
// 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))) {
if (arraylen < SFMT_N32 || (arraylen % 4) != 0 || !ValidSfmt(sfmt) || !ValidArray(array, arraylen, 4)) {
ERROR_LOG(Log::HLE, "sceSfmt19937FillArray32(sfmt=%08x, ar=%08x, arlen=%08x) - bad address(es)", sfmt, array, arraylen);
return -1;
}
@@ -102,7 +112,7 @@ static int sceSfmt19937FillArray32(u32 sfmt, u32 array, int arraylen) {
static int sceSfmt19937FillArray64(u32 sfmt, u32 array, int arraylen) {
// 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))) {
if (arraylen < SFMT_N64 || (arraylen % 2) != 0 || !ValidSfmt(sfmt) || !ValidArray(array, arraylen, 8)) {
ERROR_LOG(Log::HLE, "sceSfmt19937FillArray64(sfmt=%08x, ar=%08x, arlen=%08x) - bad address(es)", sfmt, array, arraylen);
return -1;
}
+1 -1
View File
@@ -22,7 +22,7 @@
#include "Core/HLE/sceSha256.h"
static int sceSha256Digest(u32 data, int dataLen, u32 digestPtr) {
if (!Memory::IsValidAddress(data) || !Memory::IsValidAddress(digestPtr) || !Memory::IsValidAddress(data + dataLen)) {
if (dataLen < 0 || !Memory::IsValidAddress(data) || !Memory::IsValidRange(data, (u32)dataLen) || !Memory::IsValidRange(digestPtr, 32)) {
ERROR_LOG(Log::HLE, "sceSha256Digest(data=%08x, len=%d, digest=%08x) - bad address(es)", data, dataLen, digestPtr);
return -1;
}
+4 -2
View File
@@ -1517,8 +1517,10 @@ static bool IsValidDisplayRange(u32 addr, int stride, GEBufferFormat format) {
if (stride <= 0) {
return false;
}
const u32 bpp = BufferFormatBytesPerPixel(format);
return Memory::IsValidRange(addr, ((272 - 1) * (u32)stride + 480) * bpp);
// In 64 bits: a huge stride (a multiple of 64 is all sceDisplaySetFramebuf asks for) wrapped
// this to a small size that passed.
const u64 bytes = ((272 - 1) * (u64)stride + 480) * BufferFormatBytesPerPixel(format);
return bytes <= 0xFFFFFFFF && Memory::IsValidRange(addr, (u32)bytes);
}
Draw::Texture *FramebufferManagerCommon::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height) {
+22
View File
@@ -778,7 +778,13 @@ void DumpExecute::MarkDrawn(u32 prim) {
return;
}
// Real frames draw to a few targets with a few distinct areas, but a crafted dump can make every
// prim new, and each one is checked against all the others. Past these, coarsen instead.
const size_t MAX_TARGETS = 1024;
const size_t MAX_RECTS = 64;
auto add = [&](u32 base, u32 strideBytes, u32 bytesPerPixel, const DrawnRect &rect) {
if (drawnTargets_.size() >= MAX_TARGETS && drawnTargets_.find(base) == drawnTargets_.end())
return;
DrawnTarget &t = drawnTargets_[base];
t.strideBytes = strideBytes;
t.bpp = bytesPerPixel;
@@ -786,6 +792,16 @@ void DumpExecute::MarkDrawn(u32 prim) {
if (r.Contains(rect.x1, rect.y1) && r.Contains(rect.x2, rect.y2))
return;
}
if (t.rects.size() >= MAX_RECTS) {
// Merge everything into one bounding box. That can only skip more in CopyAroundDrawn.
DrawnRect u = rect;
for (const DrawnRect &r : t.rects) {
u = DrawnRect{ std::min(u.x1, r.x1), std::min(u.y1, r.y1), std::max(u.x2, r.x2), std::max(u.y2, r.y2) };
}
t.rects.clear();
t.rects.push_back(u);
return;
}
t.rects.push_back(rect);
};
add(start, (fbWidth_ & 0x07FC) * bpp, bpp, DrawnRect{ x1, y1, x2, y2 });
@@ -1116,6 +1132,12 @@ void DumpExecute::Display(u32 ptr, u32 sz, bool allowFlip) {
haveDisplay_ = disp->topaddr.ptr != 0;
if (!haveDisplay_)
return;
// The dump is a file, so check what sceDisplaySetFramebuf would have.
if ((disp->topaddr.ptr & 0xF) != 0 || disp->linesize <= 0 || (disp->linesize & 0x3F) != 0 || disp->linesize > 4096 || disp->pixelFormat < 0 || disp->pixelFormat > GE_FORMAT_8888) {
ERROR_LOG(Log::G3D, "Bad display in dump: %08x, stride %d, format %d", disp->topaddr.ptr, disp->linesize, disp->pixelFormat);
haveDisplay_ = false;
return;
}
__DisplaySetFramebuf(disp->topaddr.ptr, disp->linesize, disp->pixelFormat, 1);
if (allowFlip) {
__DisplaySetFramebuf(disp->topaddr.ptr, disp->linesize, disp->pixelFormat, 0);
+2 -1
View File
@@ -624,7 +624,8 @@ u32 GPUCommon::EnqueueList(u32 listpc, u32 stall, int subIntrBase, PSPPointer<Ps
dl.bboxResult = false;
dl.stackAddr = stackAddr;
if (args.IsValid() && args->context.IsValid())
// The whole 512-word context is saved and restored there, not just what PSPPointer checks.
if (args.IsValid() && args->context.IsValid() && Memory::IsValidRange(args->context.ptr, 512 * 4))
dl.context = args->context;
else
dl.context = 0;
+6 -1
View File
@@ -647,7 +647,8 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(const DisplayLayoutConfig &config,
outputFlags |= OutputFlags::PILLARBOX;
}
}
} else if (srcwidth == 0 || srcheight == 0 || displayStride_ < 0 || !Memory::IsValidRange(displayFramebuf_, (displayStride_ == 0 ? srcwidth : displayStride_) * srcheight * (displayFormat_ == GE_FORMAT_8888 ? 4 : 2))) {
} else if (srcwidth == 0 || srcheight == 0 || displayStride_ > 4096 || (u64)(displayStride_ == 0 ? srcwidth : displayStride_) * srcheight * (displayFormat_ == GE_FORMAT_8888 ? 4 : 2) > 0xFFFFFFFF ||
!Memory::IsValidRange(displayFramebuf_, (displayStride_ == 0 ? srcwidth : displayStride_) * srcheight * (displayFormat_ == GE_FORMAT_8888 ? 4 : 2))) {
// Nothing, or not all of it in memory: the PSP takes strides that run past the end of VRAM, or
// negative ones (rows going up.)
hasImage = false;
@@ -1595,6 +1596,10 @@ bool SoftGPU::GetCurrentFramebuffer(GPUDebugBuffer &buffer, GPUDebugFramebufferT
size.y = 272;
stride = displayStride_;
fmt = displayFormat_;
// All 272 rows are copied, at the game's stride.
const u64 bytes = ((u64)(size.y - 1) * stride + size.x) * (fmt == GE_FORMAT_8888 ? 4 : 2);
if (bytes > 0xFFFFFFFF || !Memory::IsValidRange(displayFramebuf_, (u32)bytes))
return false;
src = Memory::GetPointerOrException(displayFramebuf_);
}
+6
View File
@@ -57,3 +57,9 @@ small examples to copy from). A module is a `const HLEFunction <name>[]` table o
(only `android/jni/Android.mk` and `libretro/Makefile.common` genuinely can't be build-tested here - see
[Building and testing](building.md) for what verification is possible for those.)
## Modules to leave out of reviews
`sceNet_lib` (`Core/HLE/sceNet_lib.cpp`) is an experimental implementation of some of the firmware's
`pspnet.prx` exports, added by a contributor. Games can't call it, so leave it out of HLE reviews and
hardening passes.