mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-12 00:59:41 +01:00
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:
1 parent
c0e750726f
commit
0a778a13ff
9 files changed
+127
-67
No files matched your search
+47
-20
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user