From 0a778a13ffb954ea2b908c2302bbc901331dc0cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 31 Oct 2026 11:26:03 +0100 Subject: [PATCH] HLE: Check more guest buffers by their full length, and an empty insert Calls that checked one byte of a game's buffer and then wrote or read the whole thing, or trusted a negative length: - sceNetEtherNtostr/Strton, the apctl BSS descriptor calls, GetState and sceNetGetDropRate. - sceNpAuthGetTicket wrote its whole ticket whatever the length; it's now built locally and written once the buffer is known to hold it. sceNpAuthGetTicketParam checked its limits after copying, and walked ticket params past the ticket's end. - sceNp2's communication ID and passphrase, a negative maxServerIds, and an unchecked unknown1Ptr. - sceParseUri's work area and scheme string, sceParseHttp's negative header length and value address, and ReadVariableSizedStruct's size. - sceKernelCreateLwMutex initialized the work area without checking it at all, and the other LwMutex calls checked one byte of it. - sceNetAdhocctlGetScanInfo cleared the length the game gave, of a buffer checked by one byte (or any length, negative ones included). Also: a game mode join inserted at begin() + 1 into a possibly empty list of players. Co-Authored-By: Claude Opus 5.5 --- Core/HLE/HLEUtil.h | 7 ++-- Core/HLE/proAdhoc.cpp | 3 +- Core/HLE/sceKernelMutex.cpp | 18 ++++++---- Core/HLE/sceNet.cpp | 38 ++++++++++++++------- Core/HLE/sceNetAdhoc.cpp | 13 ++++--- Core/HLE/sceNp.cpp | 67 ++++++++++++++++++++++++++----------- Core/HLE/sceNp2.cpp | 10 +++--- Core/HLE/sceParseHttp.cpp | 9 ++--- Core/HLE/sceParseUri.cpp | 29 ++++++++++------ 9 files changed, 127 insertions(+), 67 deletions(-) diff --git a/Core/HLE/HLEUtil.h b/Core/HLE/HLEUtil.h index 1a590d2498..9f67b965ea 100644 --- a/Core/HLE/HLEUtil.h +++ b/Core/HLE/HLEUtil.h @@ -4,14 +4,17 @@ // Used in various places in the PSP OS. template bool ReadVariableSizedStruct(u32 addr, T *out) { + if (!Memory::IsValid4AlignedAddress(addr)) { + return false; + } int size = Memory::ReadUnchecked_U32(addr); - if (!Memory::IsValid4AlignedRange(addr, size)) { + if (size < 0 || !Memory::IsValid4AlignedRange(addr, size)) { return false; } memset(out, 0, sizeof(*out)); // Only copy the right size to support different struct versions. // Let's add a debug assert in case we have a struct that is too small for the data. _dbg_assert_(sizeof(*out) >= size); - Memory::Memcpy(out, addr, std::min(size, (int)sizeof(*out))); + Memory::MemcpyUnchecked(out, addr, std::min(size, (int)sizeof(*out))); return true; } diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index 20da36cf72..6a91d3f4fb 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -1762,7 +1762,8 @@ void friendFinderPoll() { gameModeMacs.push_back(*(SceNetEtherAddr *)adhocctl_event.connect.mac); } else { - it = gameModeMacs.begin() + 1; + // After the host, who's first - if the list has one yet. + it = gameModeMacs.empty() ? gameModeMacs.end() : gameModeMacs.begin() + 1; gameModeMacs.insert(it, *(SceNetEtherAddr *)adhocctl_event.connect.mac); } diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 9aca3fb130..3a1c882bc5 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -609,6 +609,11 @@ int sceKernelCreateLwMutex(MIPSState *mips, u32 workareaPtr, const char *name, u return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ILLEGAL_COUNT); } + auto workarea = PSPPointer::Create(workareaPtr); + if (!workarea.IsValid()) { + return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ILLEGAL_ADDR, "bad workarea"); + } + LwMutex *mutex = new LwMutex(); SceUID id = kernelObjects.Create(mutex); mutex->nm.size = sizeof(mutex->nm); @@ -618,7 +623,6 @@ int sceKernelCreateLwMutex(MIPSState *mips, u32 workareaPtr, const char *name, u mutex->nm.uid = id; mutex->nm.workarea = workareaPtr; mutex->nm.initialCount = initialCount; - auto workarea = PSPPointer::Create(workareaPtr); workarea->init(); workarea->lockLevel = initialCount; if (initialCount == 0) @@ -664,7 +668,7 @@ bool __KernelUnlockLwMutexForThread(LwMutex *mutex, T workarea, SceUID threadID, int sceKernelDeleteLwMutex(u32 workareaPtr) { DEBUG_LOG(Log::sceKernel, "sceKernelDeleteLwMutex(%08x)", workareaPtr); - if (!workareaPtr || !Memory::IsValidAddress(workareaPtr)) + if (!workareaPtr || !Memory::IsValidRange(workareaPtr, sizeof(NativeLwMutexWorkarea))) return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ILLEGAL_ADDR); auto workarea = PSPPointer::Create(workareaPtr); @@ -798,7 +802,7 @@ void __KernelLwMutexEndCallback(SceUID threadID, SceUID prevCallbackId) } int sceKernelTryLockLwMutex(u32 workareaPtr, int count) { - if (!Memory::IsValidAddress(workareaPtr)) { + if (!Memory::IsValidRange(workareaPtr, sizeof(NativeLwMutexWorkarea))) { return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ACCESS_ERROR, "Bad workarea pointer for LwMutex"); } @@ -816,7 +820,7 @@ int sceKernelTryLockLwMutex(u32 workareaPtr, int count) { } int sceKernelTryLockLwMutex_600(u32 workareaPtr, int count) { - if (!Memory::IsValidAddress(workareaPtr)) { + if (!Memory::IsValidRange(workareaPtr, sizeof(NativeLwMutexWorkarea))) { return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ACCESS_ERROR, "Bad workarea pointer for LwMutex"); } @@ -833,7 +837,7 @@ int sceKernelTryLockLwMutex_600(u32 workareaPtr, int count) { } int sceKernelLockLwMutex(u32 workareaPtr, int count, u32 timeoutPtr) { - if (!Memory::IsValidAddress(workareaPtr)) { + if (!Memory::IsValidRange(workareaPtr, sizeof(NativeLwMutexWorkarea))) { return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ACCESS_ERROR, "Bad workarea pointer for LwMutex"); } @@ -865,7 +869,7 @@ int sceKernelLockLwMutex(u32 workareaPtr, int count, u32 timeoutPtr) { } int sceKernelLockLwMutexCB(u32 workareaPtr, int count, u32 timeoutPtr) { - if (!Memory::IsValidAddress(workareaPtr)) { + if (!Memory::IsValidRange(workareaPtr, sizeof(NativeLwMutexWorkarea))) { return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ACCESS_ERROR, "Bad workarea pointer for LwMutex"); } @@ -897,7 +901,7 @@ int sceKernelLockLwMutexCB(u32 workareaPtr, int count, u32 timeoutPtr) { } int sceKernelUnlockLwMutex(u32 workareaPtr, int count) { - if (!Memory::IsValidAddress(workareaPtr)) { + if (!Memory::IsValidRange(workareaPtr, sizeof(NativeLwMutexWorkarea))) { return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ACCESS_ERROR, "Bad workarea pointer for LwMutex"); } diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index da46d943f5..193fd934c6 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -1145,12 +1145,13 @@ static u32 sceWlanGetSwitchState() { static void sceNetEtherNtostr(MIPSState *mips, u32 macPtr, u32 bufferPtr) { DEBUG_LOG(Log::sceNet, "sceNetEtherNtostr(%08x, %08x) at %08x", macPtr, bufferPtr, mips->pc); - if (Memory::IsValidAddress(bufferPtr) && Memory::IsValidAddress(macPtr)) { + // "xx:xx:xx:xx:xx:xx" and a null. + if (Memory::IsValidRange(bufferPtr, 18) && Memory::IsValidRange(macPtr, ETHER_ADDR_LEN)) { char *buffer = (char *)Memory::GetPointerWriteUnchecked(bufferPtr); const u8 *mac = Memory::GetPointerUnchecked(macPtr); // MAC address is always 6 bytes / 48 bits. - sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x", + snprintf(buffer, 18, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); VERBOSE_LOG(Log::sceNet, "sceNetEtherNtostr - [%s]", buffer); @@ -1173,9 +1174,9 @@ static int hex_to_digit(int c) { static void sceNetEtherStrton(u32 bufferPtr, u32 macPtr) { DEBUG_LOG(Log::sceNet, "sceNetEtherStrton(%08x, %08x)", bufferPtr, macPtr); - if (Memory::IsValidAddress(bufferPtr) && Memory::IsValidAddress(macPtr)) { + if (Memory::IsValidNullTerminatedString(bufferPtr) && Memory::IsValidRange(macPtr, ETHER_ADDR_LEN)) { const char *buffer = (const char *)Memory::GetPointerUnchecked(bufferPtr); - u8 *mac = Memory::GetPointerWriteOrException(macPtr); + u8 *mac = Memory::GetPointerWriteUnchecked(macPtr); // MAC address is always 6 pairs of hex digits. // TODO: Funny stuff happens if it's too short. @@ -1201,7 +1202,7 @@ static void sceNetEtherStrton(u32 bufferPtr, u32 macPtr) { } } - VERBOSE_LOG(Log::sceNet, "sceNetEtherStrton - [%s]", mac2str((SceNetEtherAddr*)Memory::GetPointerOrException(macPtr)).c_str()); + VERBOSE_LOG(Log::sceNet, "sceNetEtherStrton - [%s]", mac2str((const SceNetEtherAddr *)Memory::GetPointerUnchecked(macPtr)).c_str()); // Seems to maybe kinda return the last value. Probably returns void. //return value; } @@ -1299,7 +1300,7 @@ static int sceNetApctlInit(MIPSState *mips, int stackSize, int initPriority) { userMemory.Free(apctlProdCodeAddr); } apctlProdCodeAddr = userMemory.Alloc(structsz, false, "ApctlAdhocId"); - SceNetAdhocctlAdhocId* prodCode = (SceNetAdhocctlAdhocId*)Memory::GetCharPointer(apctlProdCodeAddr); + SceNetAdhocctlAdhocId* prodCode = Memory::IsValidRange(apctlProdCodeAddr, structsz) ? (SceNetAdhocctlAdhocId *)Memory::GetPointerWriteUnchecked(apctlProdCodeAddr) : nullptr; if (prodCode) { memset(prodCode, 0, structsz); // TODO: Use a 9-characters product id instead of disc id (ie. not null-terminated(VT_UTF8_SPE) and shouldn't be variable size?) @@ -1611,9 +1612,9 @@ static int sceNetApctlGetState(u32 pStateAddr) { } // Valid Arguments - if (Memory::IsValidAddress(pStateAddr)) { + if (Memory::IsValid4AlignedAddress(pStateAddr)) { // Return Thread Status - Memory::WriteOrException_U32(NetApctl_GetState(), pStateAddr); + Memory::WriteUnchecked_U32(NetApctl_GetState(), pStateAddr); // Return Success return hleLogDebug(Log::sceNet, 0); } @@ -1652,7 +1653,10 @@ int NetApctl_GetBSSDescIDListUser(u32 sizeAddr, u32 bufAddr) { // Return size required Memory::WriteUnchecked_U32(entries * userInfoSize, sizeAddr); - if (bufAddr != 0 && Memory::IsValidAddress(sizeAddr)) { + if (bufAddr != 0) { + // Only write entries that fit in valid memory. + if (size > 0) + size = (int)Memory::ClampValidSizeAt(bufAddr, size); int offset = 0; for (int i = 0; i < entries; i++) { // Check if enough space available to write the next structure @@ -1683,7 +1687,15 @@ static int sceNetApctlGetBSSDescIDListUser(u32 sizeAddr, u32 bufAddr) { } int NetApctl_GetBSSDescEntryUser(int entryId, int infoId, u32 resultAddr) { - if (!Memory::IsValidAddress(resultAddr)) + u32 resultSize = 4; + switch (infoId) { + case PSP_NET_APCTL_DESC_IBSS: resultSize = ETHER_ADDR_LEN; break; + case PSP_NET_APCTL_DESC_SSID_NAME: resultSize = APCTL_SSID_MAXLEN; break; + case PSP_NET_APCTL_DESC_CHANNEL: + case PSP_NET_APCTL_DESC_SIGNAL_STRENGTH: resultSize = 1; break; + default: break; + } + if (!Memory::IsValidRange(resultAddr, resultSize)) return hleLogError(Log::sceNet, -1, "apctl invalid arg"); // 0x8002013A or ERROR_NET_WLAN_INVALID_ARG ? // Generate an SSID name @@ -1853,8 +1865,10 @@ static int sceNetUpnpGetNatInfo() { } static int sceNetGetDropRate(u32 dropRateAddr, u32 dropDurationAddr) { - Memory::WriteOrException_U32(netDropRate, dropRateAddr); - Memory::WriteOrException_U32(netDropDuration, dropDurationAddr); + if (Memory::IsValid4AlignedAddress(dropRateAddr)) + Memory::WriteUnchecked_U32(netDropRate, dropRateAddr); + if (Memory::IsValid4AlignedAddress(dropDurationAddr)) + Memory::WriteUnchecked_U32(netDropDuration, dropDurationAddr); return hleLogInfo(Log::sceNet, 0); } diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 9fd16d1292..0b246039e1 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -2744,15 +2744,15 @@ int sceNetAdhocctlScan(MIPSState *mips) { int sceNetAdhocctlGetScanInfo(MIPSState *mips, u32 sizeAddr, u32 bufAddr) { s32_le *buflen = NULL; - if (Memory::IsValidAddress(sizeAddr)) { - buflen = (s32_le *)Memory::GetPointerOrException(sizeAddr); + if (Memory::IsValid4AlignedAddress(sizeAddr)) { + buflen = (s32_le *)Memory::GetPointerWriteUnchecked(sizeAddr); } SceNetAdhocctlScanInfoEmu *buf = NULL; if (Memory::IsValidAddress(bufAddr)) { buf = (SceNetAdhocctlScanInfoEmu *)Memory::GetPointerOrException(bufAddr); } - INFO_LOG(Log::sceNet, "sceNetAdhocctlGetScanInfo([%08x]=%i, %08x) at %08x", sizeAddr, Memory::ReadUnchecked_U32(sizeAddr), bufAddr, mips->pc); + INFO_LOG(Log::sceNet, "sceNetAdhocctlGetScanInfo([%08x]=%i, %08x) at %08x", sizeAddr, buflen ? (int)*buflen : 0, bufAddr, mips->pc); if (!g_Config.bEnableWlan) { return hleLogWarning(Log::sceNet, 0, "WLAN off"); } @@ -2783,14 +2783,17 @@ int sceNetAdhocctlGetScanInfo(MIPSState *mips, u32 sizeAddr, u32 bufAddr) { } // Normal Information Mode else { + // Only touch the part of the buffer that's valid memory. + const u32 validLen = Memory::ClampValidSizeAt(bufAddr, std::max((s32)*buflen, 0)); + // Clear Memory - memset(buf, 0, *buflen); + memset(buf, 0, validLen); // Network Discovery Counter int discovered = 0; // Count requested Networks - int requestcount = *buflen / sizeof(SceNetAdhocctlScanInfoEmu); + int requestcount = validLen / sizeof(SceNetAdhocctlScanInfoEmu); // Minimum Argument Requirements if (requestcount > 0) { diff --git a/Core/HLE/sceNp.cpp b/Core/HLE/sceNp.cpp index 822ca675fc..4474118a97 100644 --- a/Core/HLE/sceNp.cpp +++ b/Core/HLE/sceNp.cpp @@ -168,24 +168,24 @@ static int sceNpTerm() static int sceNpGetContentRatingFlag(u32 parentalControlAddr, u32 userAgeAddr) { - if (!Memory::IsValidAddress(parentalControlAddr) || !Memory::IsValidAddress(userAgeAddr)) + if (!Memory::IsValid4AlignedAddress(parentalControlAddr) || !Memory::IsValid4AlignedAddress(userAgeAddr)) return hleLogError(Log::sceNet, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg"); INFO_LOG(Log::sceNet, "%s - Parental Control: %d", __FUNCTION__, npParentalControl); INFO_LOG(Log::sceNet, "%s - User Age: %d", __FUNCTION__, npUserAge); - Memory::WriteOrException_U32(npParentalControl, parentalControlAddr); - Memory::WriteOrException_U32(npUserAge, userAgeAddr); + Memory::WriteUnchecked_U32(npParentalControl, parentalControlAddr); + Memory::WriteUnchecked_U32(npUserAge, userAgeAddr); return hleLogWarning(Log::sceNet, 0, "UNTESTED"); } static int sceNpGetChatRestrictionFlag(u32 flagAddr) { - if (!Memory::IsValidAddress(flagAddr)) + if (!Memory::IsValid4AlignedAddress(flagAddr)) return hleLogError(Log::sceNet, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg"); - Memory::WriteOrException_U32(npChatRestriction, flagAddr); + Memory::WriteUnchecked_U32(npChatRestriction, flagAddr); return hleLogWarning(Log::sceNet, 0, "Chat restriction: %d", npChatRestriction); } @@ -276,7 +276,7 @@ static int sceNpGetUserProfile(u32 profilePtr) WARN_LOG(Log::sceNet, "UNTESTED %s(%08x)", __FUNCTION__, profilePtr); auto profile = PSPPointer::Create(profilePtr); - if (!Memory::IsValidAddress(profilePtr)) + if (!profile.IsValid()) return hleLogError(Log::sceNet, SCE_NP_ERROR_INVALID_ARGUMENT, "invalid arg"); profile.FillWithZero(); @@ -361,13 +361,17 @@ int sceNpAuthCreateStartRequest(u32 paramAddr) { return hleLogError(Log::sceNet, SCE_NP_AUTH_ERROR_INVALID_ARGUMENT, "invalid arg"); } - npServiceId = Memory::GetCharPointer(params.serviceIdAddr); + // Missing fields in a short struct are zero. + auto guestString = [](u32 addr) -> const char * { + return Memory::IsValidNullTerminatedString(addr) ? Memory::GetCharPointerUnchecked(addr) : ""; + }; + npServiceId = guestString(params.serviceIdAddr); INFO_LOG(Log::sceNet, "%s - Max Version: %u.%u", __FUNCTION__, params.version.major, params.version.minor); - INFO_LOG(Log::sceNet, "%s - Service ID: %s", __FUNCTION__, Memory::GetCharPointer(params.serviceIdAddr)); - INFO_LOG(Log::sceNet, "%s - Entitlement ID: %s", __FUNCTION__, Memory::GetCharPointer(params.entitlementIdAddr)); + INFO_LOG(Log::sceNet, "%s - Service ID: %s", __FUNCTION__, npServiceId.c_str()); + INFO_LOG(Log::sceNet, "%s - Entitlement ID: %s", __FUNCTION__, guestString(params.entitlementIdAddr)); INFO_LOG(Log::sceNet, "%s - Consumed Count: %d", __FUNCTION__, params.consumedCount); - INFO_LOG(Log::sceNet, "%s - Cookie (size = %d): %s", __FUNCTION__, params.cookieSize, Memory::GetCharPointer(params.cookieAddr)); + INFO_LOG(Log::sceNet, "%s - Cookie (size = %d): %s", __FUNCTION__, params.cookieSize, guestString(params.cookieAddr)); u32 retval = 0; if (params.size >= 32 && params.ticketCbAddr != 0) { @@ -428,12 +432,13 @@ int sceNpAuthGetTicket(u32 requestId, u32 bufferAddr, u32 length) { } int result = length; - Memory::Memset(bufferAddr, 0, length, "NpAuthGetTicket"); + // Built here, then copied once we know it fits. + u8 staging[512]{}; SceNpTicket ticket = {}; // Dummy Login ticket returned as Login response. Dummy ticket contents were taken from https://www.psdevwiki.com/ps3/X-I-5-Ticket ticket.header.version = TICKET_VER_2_1; ticket.header.size = 0xF0; // size excluding the header - u8* buf = Memory::GetPointerWriteOrException(bufferAddr + sizeof(ticket)); + u8 *buf = staging + sizeof(ticket); int ofs = 0; ofs += writeTicketParam(buf, PARAM_TYPE_STRING_ASCII, "\x4c\x47\x56\x3b\x81\x39\x4a\x22\xd8\x6b\xc1\x57\x71\x6e\xfd\xb8\xab\x63\xcc\x51", 20); // 20 random letters, token key or SceNpSignature? ofs += writeTicketU32Param(buf + ofs, PARAM_TYPE_INT, 0x0100); // a flags? @@ -446,7 +451,9 @@ int sceNpAuthGetTicket(u32 requestId, u32 bufferAddr, u32 length) { ofs += writeTicketStringParam(buf + ofs, PARAM_TYPE_STRING, npOnlineId.c_str(), 32); // username (pre-cut to 16 chars) ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, npCountryCode, 4); // SceNpCountryCode ? ie. "fr" + 00 02 ofs += writeTicketStringParam(buf + ofs, PARAM_TYPE_STRING, npRegionCode, 4); // 2-char code? related to country/lang code? ie. "c9" + 00 00 - ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, npServiceId.c_str(), 24); + std::string serviceId = npServiceId; + serviceId.resize(24, '\0'); + ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, serviceId.c_str(), 24); int status = 0; if (npParentalControl == PARENTAL_CONTROL_ENABLED) { status |= STATUS_ACCOUNT_PARENTAL_CONTROL_ENABLED; @@ -457,14 +464,22 @@ int sceNpAuthGetTicket(u32 requestId, u32 bufferAddr, u32 length) { ofs += writeTicketParam(buf + ofs, PARAM_TYPE_NULL); ticket.section.type = SECTION_TYPE_BODY; ticket.section.size = ofs; - Memory::Memcpy(bufferAddr, &ticket, sizeof(SceNpTicket), "NpAuthGetTicket"); + memcpy(staging, &ticket, sizeof(SceNpTicket)); SceNpTicketSection footer = { SECTION_TYPE_FOOTER, 32 }; // footer section? ie. 32-bytes on version 2.1 containing 4-chars ASCII + 20-chars ASCII - Memory::Memcpy(bufferAddr + sizeof(ticket) + ofs, &footer, sizeof(SceNpTicketSection), "NpAuthGetTicket"); + memcpy(buf + ofs, &footer, sizeof(SceNpTicketSection)); ofs += sizeof(footer); ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, "\x34\xcd\x3c\xa9", 4); ofs += writeTicketParam(buf + ofs, PARAM_TYPE_STRING_ASCII, "\x3a\x4b\x42\x66\x92\xda\x6b\x7c\xb7\x4c\xe8\xd9\x4f\x2b\x77\x15\x91\xb8\xa4\xa9", 20); // 20 random letters, token key or SceNpSignature? - // includes Language list? - Memory::Memset(bufferAddr + sizeof(ticket) + ofs, 0, 36); + // includes Language list? (36 zero bytes, staging is zeroed) + const u32 total = (u32)sizeof(ticket) + ofs + 36; + _dbg_assert_(total <= sizeof(staging)); + + // The whole ticket gets written, even if length is shorter. + if (!Memory::IsValidRange(bufferAddr, std::max(length, total))) { + return hleLogError(Log::sceNet, SCE_NP_AUTH_ERROR_INVALID_ARGUMENT, "invalid buffer"); + } + Memory::Memset(bufferAddr, 0, length, "NpAuthGetTicket"); + Memory::Memcpy(bufferAddr, staging, total, "NpAuthGetTicket"); result = ticket.header.size + sizeof(ticket.header); // dummy ticket is 248 bytes @@ -512,20 +527,32 @@ int sceNpAuthDestroyRequest(int requestId) int sceNpAuthGetTicketParam(u32 ticketBufPtr, int ticketLen, int paramNum, u32 bufferPtr) { const u32 PARAM_BUFFER_MAX_SIZE = 256; + if (!Memory::IsValidRange(bufferPtr, PARAM_BUFFER_MAX_SIZE)) { + return hleLogError(Log::sceNet, SCE_NP_MANAGER_ERROR_INVALID_ARGUMENT, "invalid buffer"); + } Memory::Memset(bufferPtr, 0, PARAM_BUFFER_MAX_SIZE); // JPCSP: This clear is always done, even when an error is returned if (paramNum < 0 || paramNum >= NUMBER_PARAMETERS) { return hleLogError(Log::sceNet, SCE_NP_MANAGER_ERROR_INVALID_ARGUMENT); } + if (ticketLen < (int)sizeof(SceNpTicket) || !Memory::IsValidRange(ticketBufPtr, ticketLen)) { + return hleLogError(Log::sceNet, SCE_NP_MANAGER_ERROR_INVALID_ARGUMENT, "invalid ticket"); + } - SceNpTicket* ticket = (SceNpTicket*)Memory::GetPointerOrException(ticketBufPtr); + const SceNpTicket *ticket = (const SceNpTicket *)Memory::GetPointerUnchecked(ticketBufPtr); + const u32 ticketEnd = ticketBufPtr + ticketLen; u32 inbuf = ticketBufPtr; inbuf += sizeof(ticket->header); inbuf += ticket->section.size + sizeof(ticket->section); u32 outbuf = bufferPtr; for (int i = 0; i < paramNum; i++) { - SceNpTicketParamData* ticketParam = (SceNpTicketParamData*)Memory::GetPointerOrException(inbuf); + // Only copy params that are inside the ticket and fit in the output. + if (inbuf < ticketBufPtr || inbuf + sizeof(SceNpTicketParamData) > ticketEnd) + break; + const SceNpTicketParamData *ticketParam = (const SceNpTicketParamData *)Memory::GetPointerUnchecked(inbuf); u32 sz = (u32)sizeof(SceNpTicketParamData) + ticketParam->length; - Memory::Memcpy(outbuf, inbuf, sz); + if (sz > ticketEnd - inbuf || sz > PARAM_BUFFER_MAX_SIZE - (outbuf - bufferPtr)) + break; + Memory::MemcpyUnchecked(outbuf, inbuf, sz); DEBUG_LOG(Log::sceNet, "%s - Param #%d: Type = %04x, Length = %u", __FUNCTION__, i, static_cast(ticketParam->type), static_cast(ticketParam->length)); outbuf += sz; inbuf += sz; diff --git a/Core/HLE/sceNp2.cpp b/Core/HLE/sceNp2.cpp index b597440207..7670d61390 100644 --- a/Core/HLE/sceNp2.cpp +++ b/Core/HLE/sceNp2.cpp @@ -115,14 +115,14 @@ static int sceNpMatching2CreateContext(u32 communicationIdPtr, u32 passPhrasePtr if (!npMatching2Inited) return hleLogError(Log::sceNet, SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED); - if (!Memory::IsValidAddress(communicationIdPtr) || !Memory::IsValidAddress(passPhrasePtr) || !Memory::IsValidRange(ctxIdPtr, 2)) + if (!Memory::IsValidRange(communicationIdPtr, sizeof(SceNpCommunicationId)) || !Memory::IsValidRange(passPhrasePtr, sizeof(SceNpCommunicationPassphrase)) || !Memory::IsValidRange(ctxIdPtr, 2)) return hleLogError(Log::sceNet, SCE_NP_MATCHING2_ERROR_CONTEXT_MAX); // FIXME: It seems Context are mapped to TitleID? may return 0x80550C05 or 0x80550C06 when finding an existing context - SceNpCommunicationId* titleid = (SceNpCommunicationId*)Memory::GetCharPointer(communicationIdPtr); + const SceNpCommunicationId *titleid = (const SceNpCommunicationId *)Memory::GetPointerUnchecked(communicationIdPtr); memcpy(npTitleId.data, titleid->data, sizeof(npTitleId)); - SceNpCommunicationPassphrase* passph = (SceNpCommunicationPassphrase*)Memory::GetCharPointer(passPhrasePtr); + const SceNpCommunicationPassphrase *passph = (const SceNpCommunicationPassphrase *)Memory::GetPointerUnchecked(passPhrasePtr); SceNpId npid{}; int retval = NpGetNpId(&npid); @@ -372,7 +372,7 @@ static int sceNpMatching2GetServerIdListLocal(MIPSState *mips, int ctxId, u32 se if (!npMatching2Inited) return hleLogError(Log::sceNet, SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED); - if (!Memory::IsValidRange(serverIdsPtr, maxServerIds * sizeof(u16))) + if (maxServerIds < 0 || !Memory::IsValidRange(serverIdsPtr, maxServerIds * sizeof(u16))) return hleLogError(Log::sceNet, SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT); // Returning dummy ids, 16-bit variables according to JPCSP @@ -389,7 +389,7 @@ static int sceNpMatching2GetServerInfo(int ctxId, u32 serverIdPtr, u32 unknown1P if (!npMatching2Inited) return hleLogError(Log::sceNet, SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED); - if (!Memory::IsValidRange(serverIdPtr, 2) || !Memory::IsValid4AlignedRange(unknown2Ptr, 8)) + if (!Memory::IsValidRange(serverIdPtr, 2) || !Memory::IsValid4AlignedRange(unknown1Ptr, 8) || !Memory::IsValid4AlignedRange(unknown2Ptr, 8)) return hleLogError(Log::sceNet, SCE_NP_MATCHING2_ERROR_CONTEXT_MAX); // Should be SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT ? // Server ID is a 16-bit variable according to JPCSP diff --git a/Core/HLE/sceParseHttp.cpp b/Core/HLE/sceParseHttp.cpp index 4bc7bd1cfd..98acb6488c 100644 --- a/Core/HLE/sceParseHttp.cpp +++ b/Core/HLE/sceParseHttp.cpp @@ -40,10 +40,11 @@ std::string getHeaderString(std::istringstream &headers) { // FIXME: Is it allowed for fieldName to be null/0 or empty string ? JPCSP seems to ignore it static int sceParseHttpResponseHeader(u32 headerAddr, int headerLength, const char *fieldName, u32 valueAddr, u32 valueLengthAddr) { - if (!Memory::IsValidRange(headerAddr, headerLength)) + if (headerLength < 0 || !Memory::IsValidRange(headerAddr, headerLength)) return hleLogError(Log::sceNet, -1, "invalid arg"); - if (!Memory::IsValidRange(valueLengthAddr, 4)) + // The value's address gets written to valueAddr. + if (!Memory::IsValidRange(valueLengthAddr, 4) || !Memory::IsValidRange(valueAddr, 4)) return hleLogError(Log::sceNet, -1, "invalid arg"); // FIXME: Not sure whether valueAddr can be null or not (in case the game just need to get the size to allocate the value buffer first) @@ -53,7 +54,7 @@ static int sceParseHttpResponseHeader(u32 headerAddr, int headerLength, const ch std::string field = fieldName ? fieldName : ""; field = StripSpaces(field); - std::string headers(Memory::GetCharPointer(headerAddr), headerLength); + std::string headers = headerLength > 0 ? std::string(Memory::GetCharPointerUnchecked(headerAddr), headerLength) : std::string(); std::istringstream hdrs(headers); u32 addr = 0; u32 len = 0; @@ -108,7 +109,7 @@ static int sceParseHttpStatusLine(MIPSState *mips, u32 headerAddr, u32 headerLen if (!Memory::IsValidRange(headerAddr, headerLength)) return hleLogError(Log::sceNet, -1, "invalid arg"); - std::string headers(Memory::GetCharPointer(headerAddr), headerLength); + std::string headers = headerLength > 0 ? std::string(Memory::GetCharPointerUnchecked(headerAddr), headerLength) : std::string(); // Get first line from header, line ending can be '\n' or '\r' std::istringstream hdr(headers); std::string line; diff --git a/Core/HLE/sceParseUri.cpp b/Core/HLE/sceParseUri.cpp index 3e73291c34..3956f57978 100644 --- a/Core/HLE/sceParseUri.cpp +++ b/Core/HLE/sceParseUri.cpp @@ -60,6 +60,11 @@ static int sceUriParse(MIPSState *mips, u32 parsedUriAreaAddr, const char* url, return hleLogDebug(Log::sceNet, 0, "workAreaSize: %d, %d", sz, workAreaSize); } + // workAreaAddString writes into it unchecked. + if (workAreaSize < 0 || !Memory::IsValidRange(workAreaAddr, workAreaSize)) { + return hleLogError(Log::sceNet, -1, "invalid work area"); + } + auto parsedUri = PSPPointer::Create(parsedUriAreaAddr); // Parse the URL into URI components @@ -172,8 +177,7 @@ static int sceUriBuild(MIPSState *mips, u32 workAreaAddr, u32 workAreaSizeAddr, auto getUriComponent = [](u32 addr, int flags, int flagMask) -> std::string { if (addr == 0 || (flags & flagMask) == 0) return ""; - const char* str = Memory::GetCharPointer(addr); - return str ? std::string(str) : ""; + return Memory::IsValidNullTerminatedString(addr) ? std::string(Memory::GetCharPointerUnchecked(addr)) : ""; }; std::string scheme = getUriComponent(parsedUri->schemeAddr, flags, 0x1); @@ -214,15 +218,12 @@ static int sceUriBuild(MIPSState *mips, u32 workAreaAddr, u32 workAreaSizeAddr, if (port > 0) { int defaultPort = -1; - if (parsedUri->schemeAddr != 0) { - const char* protocol = Memory::GetCharPointer(parsedUri->schemeAddr); - if (protocol) { - std::string protocolStr(protocol); - if (protocolStr == "http") defaultPort = 80; - //else if (protocolStr == "https") defaultPort = 443; - else if (protocolStr == "ftp") defaultPort = 21; - else if (protocolStr == "ssh") defaultPort = 22; - } + if (Memory::IsValidNullTerminatedString(parsedUri->schemeAddr)) { + std::string protocolStr(Memory::GetCharPointerUnchecked(parsedUri->schemeAddr)); + if (protocolStr == "http") defaultPort = 80; + //else if (protocolStr == "https") defaultPort = 443; + else if (protocolStr == "ftp") defaultPort = 21; + else if (protocolStr == "ssh") defaultPort = 22; } if (port != defaultPort) { uri += ":" + std::to_string(port); @@ -255,6 +256,9 @@ static int sceUriBuild(MIPSState *mips, u32 workAreaAddr, u32 workAreaSizeAddr, if (requiredSize > workAreaSize) { return hleLogError(Log::sceNet, -1, "work area too small: need %d, have %d", requiredSize, workAreaSize); } + if (!Memory::IsValidRange(workAreaAddr, requiredSize)) { + return hleLogError(Log::sceNet, -1, "invalid work area"); + } Memory::MemcpyUnchecked(workAreaAddr, uri.c_str(), requiredSize); @@ -314,6 +318,9 @@ static int sceUriEscape(MIPSState *mips, u32 escapedAddr, u32 escapedLengthAddr, if (requiredSize > escapedBufferLength) { return hleLogError(Log::sceNet, -1, "buffer too small: need %d, have %d", requiredSize, escapedBufferLength); } + if (!Memory::IsValidRange(escapedAddr, requiredSize)) { + return hleLogError(Log::sceNet, -1, "invalid buffer"); + } std::string escaped = "";