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 -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