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 <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-31 11:26:03 +01:00
1 parent c0e750726f
commit 0a778a13ff
9 files changed
+127 -67

No files matched your search

+5 -2
View File
@@ -4,14 +4,17 @@
// Used in various places in the PSP OS.
template<typename T> 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;
}
+2 -1
View File
@@ -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);
}
+11 -7
View File
@@ -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<NativeLwMutexWorkarea>::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<NativeLwMutexWorkarea>::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<NativeLwMutexWorkarea>::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");
}
+26 -12
View File
@@ -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);
}
+8 -5
View File
@@ -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) {
+47 -20
View File
@@ -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<SceNpUserInformation>::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<unsigned int>(ticketParam->type), static_cast<unsigned int>(ticketParam->length));
outbuf += sz;
inbuf += sz;
+5 -5
View File
@@ -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
+5 -4
View File
@@ -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;
+18 -11
View File
@@ -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<PSPParsedUri>::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 = "";