mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-02 12:32:03 +01:00
sceNetAdhoc: Check the game's ad hoc buffers by their full length
Calls that checked one byte of a buffer, then used the game's length: - GetPeerList and GetAddrByName cleared that many bytes; GetPdpStat and GetPtpStat filled that many entries. Clamped to valid memory. - PollSocket read and wrote count entries. - GetNameByAddr, GetPeerInfo, Join, GetGameModeInfo and UpdateReplica wrote or read whole structs. - PtpSend and PtpRecv used the whole data length. - PdpSend and PdpRecv took host pointers from generic wrappers. Their syscalls now check the game's buffers and pass NULL for bad ones, which the calls already reject, since game mode calls the functions with host buffers. - Matching SelectTarget, CancelTargetWithOpt and SendData copied the whole opt or data length. GetMembers cleared a NULL buffer when given a bad one. Also: the matching thread list is savestated without the contexts, so a context's index may be past its end after a load. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c0e750726f
commit
74178a8106
3 files changed
+59
-37
No files matched your search
@@ -921,19 +921,6 @@ template<auto func> void WrapI_ICI(MIPSState *mips) {
|
||||
RETURN(retval);
|
||||
}
|
||||
|
||||
template<auto func> void WrapI_IVVVVUI(MIPSState *mips) {
|
||||
// Any of the pointers may be NULL (sceNetAdhocPdpRecv takes NULL for the sender), so don't fault on it.
|
||||
auto ptr = [](u32 addr) -> void * { return addr ? Memory::GetPointerWriteOrException(addr) : nullptr; };
|
||||
u32 retval = HLECall<int(int, void *, void *, void *, void *, u32, int), func>(mips, PARAM(0), ptr(PARAM(1)), ptr(PARAM(2)), ptr(PARAM(3)), ptr(PARAM(4)), PARAM(5), PARAM(6) );
|
||||
RETURN(retval);
|
||||
}
|
||||
|
||||
template<auto func> void WrapI_ICUVIII(MIPSState *mips) {
|
||||
// The buffer may be NULL (sceNetAdhocPdpSend with a length of 0 is fine with that), so don't fault on it.
|
||||
u32 retval = HLECall<int(int, const char *, u32, void *, int, int, int), func>(mips, PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2), PARAM(3) ? Memory::GetPointerWriteOrException(PARAM(3)) : nullptr, PARAM(4), PARAM(5), PARAM(6));
|
||||
RETURN(retval);
|
||||
}
|
||||
|
||||
template<auto func> void WrapI_VUI(MIPSState *mips) {
|
||||
u32 retval = HLECall<int(void *, u32, int), func>(mips, Memory::GetPointerWriteOrException(PARAM(0)), PARAM(1), PARAM(2));
|
||||
RETURN(retval);
|
||||
|
||||
+41
-16
@@ -2551,7 +2551,8 @@ int sceNetAdhocPollSocket(MIPSState *mips, u32 socketStructAddr, int count, int
|
||||
if (netAdhocInited)
|
||||
{
|
||||
SceNetAdhocPollSd * sds = NULL;
|
||||
if (Memory::IsValidAddress(socketStructAddr)) sds = (SceNetAdhocPollSd *)Memory::GetPointerOrException(socketStructAddr);
|
||||
const u64 sdsSize = (u64)std::max(count, 0) * sizeof(SceNetAdhocPollSd);
|
||||
if (sdsSize <= 0x10000000 && Memory::IsValidRange(socketStructAddr, (u32)sdsSize)) sds = (SceNetAdhocPollSd *)Memory::GetPointerOrException(socketStructAddr);
|
||||
|
||||
// Valid Arguments
|
||||
if (sds != NULL && count > 0)
|
||||
@@ -3062,7 +3063,7 @@ static int sceNetAdhocctlGetNameByAddr(MIPSState *mips, const char *mac, u32 nam
|
||||
if (netAdhocctlInited)
|
||||
{
|
||||
// Valid Arguments
|
||||
if (mac != NULL && Memory::IsValidAddress(nameAddr))
|
||||
if (mac != NULL && Memory::IsValidRange(nameAddr, sizeof(SceNetAdhocctlNickname)))
|
||||
{
|
||||
SceNetAdhocctlNickname * nickname = (SceNetAdhocctlNickname *)Memory::GetPointerOrException(nameAddr);
|
||||
// Get Local MAC Address
|
||||
@@ -3124,7 +3125,7 @@ int sceNetAdhocctlGetPeerInfo(MIPSState *mips, const char *mac, int size, u32 pe
|
||||
|
||||
SceNetEtherAddr * maddr = (SceNetEtherAddr *)mac;
|
||||
SceNetAdhocctlPeerInfoEmu * buf = NULL;
|
||||
if (Memory::IsValidAddress(peerInfoAddr)) {
|
||||
if (Memory::IsValidRange(peerInfoAddr, sizeof(SceNetAdhocctlPeerInfoEmu))) {
|
||||
buf = (SceNetAdhocctlPeerInfoEmu *)Memory::GetPointerOrException(peerInfoAddr);
|
||||
}
|
||||
// Library initialized
|
||||
@@ -3283,7 +3284,7 @@ int sceNetAdhocctlJoin(MIPSState *mips, u32 scanInfoAddr) {
|
||||
if (netAdhocctlInited)
|
||||
{
|
||||
// Valid Argument
|
||||
if (Memory::IsValidAddress(scanInfoAddr))
|
||||
if (Memory::IsValidRange(scanInfoAddr, sizeof(SceNetAdhocctlScanInfoEmu)))
|
||||
{
|
||||
SceNetAdhocctlScanInfoEmu* sinfo = (SceNetAdhocctlScanInfoEmu*)Memory::GetPointerOrException(scanInfoAddr);
|
||||
char grpName[ADHOCCTL_GROUPNAME_LEN + 1] = { 0 };
|
||||
@@ -3524,7 +3525,7 @@ static int sceNetAdhocGetPdpStat(MIPSState *mips, u32 structSize, u32 structAddr
|
||||
else if (buflen != NULL && buf != NULL)
|
||||
{
|
||||
// Figure out how many Sockets we will return
|
||||
int count = *buflen / sizeof(SceNetAdhocPdpStat);
|
||||
int count = Memory::ClampValidSizeAt(structAddr, std::max((s32)*buflen, 0)) / sizeof(SceNetAdhocPdpStat);
|
||||
if (count > socketcount) count = socketcount;
|
||||
|
||||
// Copy Counter
|
||||
@@ -3611,7 +3612,7 @@ static int sceNetAdhocGetPtpStat(MIPSState *mips, u32 structSize, u32 structAddr
|
||||
// Status Returner Mode
|
||||
else if (buflen != NULL && buf != NULL) {
|
||||
// Figure out how many Sockets we will return
|
||||
int count = *buflen / sizeof(SceNetAdhocPtpStat);
|
||||
int count = Memory::ClampValidSizeAt(structAddr, std::max((s32)*buflen, 0)) / sizeof(SceNetAdhocPtpStat);
|
||||
if (count > socketcount) count = socketcount;
|
||||
|
||||
// Copy Counter
|
||||
@@ -4318,7 +4319,7 @@ static int sceNetAdhocPtpSend(MIPSState *mips, int id, u32 dataAddr, u32 dataSiz
|
||||
if (*len == 0) {
|
||||
return hleLogDebug(Log::sceNet, 0, "nothing to send");
|
||||
}
|
||||
if (*len < 0 || data == NULL) {
|
||||
if (*len < 0 || data == NULL || !Memory::IsValidRange(dataAddr, *len)) {
|
||||
return hleLogError(Log::sceNet, SCE_NET_ADHOC_ERROR_INVALID_DATALEN, "invalid data length");
|
||||
}
|
||||
// Valid Arguments
|
||||
@@ -4374,7 +4375,7 @@ static int sceNetAdhocPtpRecv(MIPSState *mips, int id, u32 dataAddr, u32 dataSiz
|
||||
// Library is initialized
|
||||
if (netAdhocInited) {
|
||||
// Valid Arguments
|
||||
if (buf != NULL && len != NULL && *len > 0) {
|
||||
if (buf != NULL && len != NULL && *len > 0 && Memory::IsValidRange(dataAddr, *len)) {
|
||||
// Valid Socket
|
||||
if (id > 0 && id <= MAX_SOCKET && adhocSockets[id - 1] != NULL) {
|
||||
// Cast Socket
|
||||
@@ -4678,7 +4679,7 @@ static int sceNetAdhocGameModeUpdateReplica(MIPSState *mips, int id, u32 infoAdd
|
||||
|
||||
// Bomberman Panic Bomber is using 0/null on infoAddr, so i guess it's optional.
|
||||
GameModeUpdateInfo* gmuinfo = NULL;
|
||||
if (Memory::IsValidAddress(infoAddr)) {
|
||||
if (Memory::IsValidRange(infoAddr, sizeof(GameModeUpdateInfo))) {
|
||||
gmuinfo = (GameModeUpdateInfo*)Memory::GetPointerOrException(infoAddr);
|
||||
}
|
||||
|
||||
@@ -4836,13 +4837,31 @@ void __NetTriggerCallbacks()
|
||||
hleNoLogVoid();
|
||||
}
|
||||
|
||||
// The game's buffer, if all of it is valid memory, else NULL, which the calls treat as missing.
|
||||
static void *GuestBuffer(u32 addr, int size) {
|
||||
return Memory::IsValidRange(addr, std::max(size, 1)) ? Memory::GetPointerWriteUnchecked(addr) : nullptr;
|
||||
}
|
||||
|
||||
// The PDP syscalls check the game's buffers here, since game mode calls the functions with host ones.
|
||||
static int sceNetAdhocPdpSendSyscall(MIPSState *mips, int id, u32 macAddr, u32 port, u32 dataAddr, u32 len, u32 timeout, u32 flag) {
|
||||
void *data = dataAddr ? GuestBuffer(dataAddr, (int)len) : nullptr;
|
||||
return sceNetAdhocPdpSend(mips, id, (const char *)GuestBuffer(macAddr, ETHER_ADDR_LEN), port, data, (int)len, (int)timeout, (int)flag);
|
||||
}
|
||||
|
||||
static int sceNetAdhocPdpRecvSyscall(MIPSState *mips, int id, u32 macAddr, u32 portAddr, u32 bufAddr, u32 lenAddr, u32 timeout, u32 flag) {
|
||||
void *len = GuestBuffer(lenAddr, 4);
|
||||
void *buf = GuestBuffer(bufAddr, len ? *(s32_le *)len : 0);
|
||||
// NULL for the sender's MAC and port is fine.
|
||||
return sceNetAdhocPdpRecv(mips, id, macAddr ? GuestBuffer(macAddr, ETHER_ADDR_LEN) : nullptr, portAddr ? GuestBuffer(portAddr, 2) : nullptr, buf, len, timeout, (int)flag);
|
||||
}
|
||||
|
||||
const HLEFunction sceNetAdhoc[] = {
|
||||
{0XE1D621D7, &WrapU_V<sceNetAdhocInit>, "sceNetAdhocInit", 'x', "" },
|
||||
{0XA62C6F57, &WrapI_V<sceNetAdhocTerm>, "sceNetAdhocTerm", 'i', "" },
|
||||
{0X0AD043ED, &WrapI_C<sceNetAdhocctlConnect>, "sceNetAdhocctlConnect", 'i', "s" },
|
||||
{0X6F92741B, &WrapI_CIIU<sceNetAdhocPdpCreate>, "sceNetAdhocPdpCreate", 'i', "siix" },
|
||||
{0XABED3790, &WrapI_ICUVIII<sceNetAdhocPdpSend>, "sceNetAdhocPdpSend", 'i', "isxpiii" },
|
||||
{0XDFE53E03, &WrapI_IVVVVUI<sceNetAdhocPdpRecv>, "sceNetAdhocPdpRecv", 'i', "ippppxi" },
|
||||
{0XABED3790, &WrapI_IUUUUUU<sceNetAdhocPdpSendSyscall>, "sceNetAdhocPdpSend", 'i', "ixxxiii" },
|
||||
{0XDFE53E03, &WrapI_IUUUUUU<sceNetAdhocPdpRecvSyscall>, "sceNetAdhocPdpRecv", 'i', "ixxxxxi" },
|
||||
{0X7F27BB5E, &WrapI_II<sceNetAdhocPdpDelete>, "sceNetAdhocPdpDelete", 'i', "ii" },
|
||||
{0XC7C1FC57, &WrapI_UU<sceNetAdhocGetPdpStat>, "sceNetAdhocGetPdpStat", 'i', "xx" },
|
||||
{0X157E6225, &WrapI_II<sceNetAdhocPtpClose>, "sceNetAdhocPtpClose", 'i', "ii" },
|
||||
@@ -4897,7 +4916,7 @@ static int sceNetAdhocctlGetGameModeInfo(u32 infoAddr) {
|
||||
if (!netAdhocctlInited)
|
||||
return hleLogError(Log::sceNet, SCE_NET_ADHOCCTL_ERROR_NOT_INITIALIZED, "not initialized");
|
||||
|
||||
if (!Memory::IsValidAddress(infoAddr))
|
||||
if (!Memory::IsValidRange(infoAddr, sizeof(SceNetAdhocctlGameModeInfo)))
|
||||
return hleLogError(Log::sceNet, SCE_NET_ADHOCCTL_ERROR_INVALID_ARG, "invalid arg");
|
||||
|
||||
// pspautotests net/adhoc/gamemode
|
||||
@@ -4961,13 +4980,16 @@ static int sceNetAdhocctlGetPeerList(MIPSState *mips, u32 sizeAddr, u32 bufAddr)
|
||||
// Discovery Counter
|
||||
int discovered = 0;
|
||||
|
||||
// Only the part of the buffer that's valid memory.
|
||||
const u32 validLen = Memory::ClampValidSizeAt(bufAddr, std::max((s32)*buflen, 0));
|
||||
|
||||
// Calculate Request Count
|
||||
int requestcount = *buflen / sizeof(SceNetAdhocctlPeerInfoEmu);
|
||||
int requestcount = validLen / sizeof(SceNetAdhocctlPeerInfoEmu);
|
||||
|
||||
// FIXME: When bufAddr is not null but buffer size is smaller than activePeers * sizeof(SceNetAdhocctlPeerInfoEmu), simply return buffer size = 0 without filling the buffer?
|
||||
|
||||
// Clear Memory
|
||||
memset(buf, 0, *buflen);
|
||||
memset(buf, 0, validLen);
|
||||
|
||||
// Minimum Arguments
|
||||
if (requestcount > 0) {
|
||||
@@ -5056,14 +5078,17 @@ static int sceNetAdhocctlGetAddrByName(MIPSState *mips, const char *nickName, u3
|
||||
// Normal Information Mode
|
||||
else
|
||||
{
|
||||
// Only 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);
|
||||
|
||||
// Discovered Player Count
|
||||
int discovered = 0;
|
||||
|
||||
// Calculate Requested Elements
|
||||
int requestcount = *buflen / sizeof(SceNetAdhocctlPeerInfoEmu);
|
||||
int requestcount = validLen / sizeof(SceNetAdhocctlPeerInfoEmu);
|
||||
|
||||
// Minimum Space available
|
||||
if (requestcount > 0)
|
||||
|
||||
@@ -1382,11 +1382,14 @@ int NetAdhocMatching_Stop(int matchingId) {
|
||||
|
||||
// Stop fake PSP Thread.
|
||||
// kernelObjects may already been cleared early during a Shutdown, thus trying to access it may generates Warning/Error in the log
|
||||
if (matchingThreads[item->matching_thid] > 0 && strcmp(__KernelGetThreadName(matchingThreads[item->matching_thid]), "ERROR") != 0) {
|
||||
__KernelStopThread(matchingThreads[item->matching_thid], SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocMatching stopped");
|
||||
__KernelDeleteThread(matchingThreads[item->matching_thid], SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocMatching deleted");
|
||||
// A savestate load replaces the thread list but not the contexts, so the index may be past its end.
|
||||
if (item->matching_thid >= 0 && item->matching_thid < (int)matchingThreads.size()) {
|
||||
if (matchingThreads[item->matching_thid] > 0 && strcmp(__KernelGetThreadName(matchingThreads[item->matching_thid]), "ERROR") != 0) {
|
||||
__KernelStopThread(matchingThreads[item->matching_thid], SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocMatching stopped");
|
||||
__KernelDeleteThread(matchingThreads[item->matching_thid], SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocMatching deleted");
|
||||
}
|
||||
matchingThreads[item->matching_thid] = 0;
|
||||
}
|
||||
matchingThreads[item->matching_thid] = 0;
|
||||
|
||||
// Make sure nobody locking/using the socket
|
||||
item->socketlock->lock();
|
||||
@@ -1721,6 +1724,9 @@ int NetAdhocMatching_Start(int matchingId, int evthPri, int evthPartitionId, int
|
||||
// Created and started without rescheduling, like adhocctl's thread: starting it through
|
||||
// sceKernelStartThread switched threads inside this call, and the game got the thread's ID back
|
||||
// instead of 0 (pspautotests net/adhoc/matching).
|
||||
if (item->matching_thid >= (int)matchingThreads.size()) {
|
||||
matchingThreads.resize(item->matching_thid + 1);
|
||||
}
|
||||
matchingThreads[item->matching_thid] = __KernelCreateThread(thrname.c_str(), __KernelGetCurThreadModuleId(), matchingThreadHackAddr, evthPri, evthStack, PSP_THREAD_ATTR_USER, 0, true);
|
||||
//item->matchingThread = new HLEHelperThread(thrname.c_str(), "sceNetAdhocMatching", "__NetMatchingCallbacks", inthPri, inthStack);
|
||||
if (matchingThreads[item->matching_thid] > 0) {
|
||||
@@ -1823,7 +1829,7 @@ static int sceNetAdhocMatchingSelectTarget(MIPSState *mips, int matchingId, cons
|
||||
}
|
||||
|
||||
void * opt = NULL;
|
||||
if (Memory::IsValidAddress(optDataPtr)) opt = Memory::GetPointerWriteUnchecked(optDataPtr);
|
||||
if (Memory::IsValidRange(optDataPtr, std::max(optLen, 1))) opt = Memory::GetPointerWriteUnchecked(optDataPtr);
|
||||
// Host Mode
|
||||
if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT) {
|
||||
// Already Connected. Right after accepting, it's still in progress (pspautotests net/adhoc/pair/matching).
|
||||
@@ -1921,7 +1927,7 @@ int NetAdhocMatching_CancelTargetWithOpt(int matchingId, const char* macAddress,
|
||||
|
||||
SceNetEtherAddr* target = (SceNetEtherAddr*)macAddress;
|
||||
void* opt = NULL;
|
||||
if (Memory::IsValidAddress(optDataPtr)) opt = Memory::GetPointerWriteUnchecked(optDataPtr);
|
||||
if (Memory::IsValidRange(optDataPtr, std::max(optLen, 1))) opt = Memory::GetPointerWriteUnchecked(optDataPtr);
|
||||
|
||||
if (target == NULL || ((optLen != 0) && (optLen <= 0 || opt == NULL))) {
|
||||
// Invalid Arguments
|
||||
@@ -2183,8 +2189,12 @@ static int sceNetAdhocMatchingGetMembers(int matchingId, u32 sizeAddr, u32 buf)
|
||||
// Fix Oversize Request
|
||||
if ((*buflen) > available) *buflen = available;
|
||||
|
||||
// Only the part of the buffer that's valid memory (none if buf2 is NULL).
|
||||
*buflen = Memory::ClampValidSizeAt(buf, *buflen);
|
||||
|
||||
// Clear Memory
|
||||
memset(buf2, 0, *buflen);
|
||||
if (buf2)
|
||||
memset(buf2, 0, *buflen);
|
||||
|
||||
// Calculate Requested Peer Count
|
||||
int requestedpeers = (*buflen) / sizeof(SceNetAdhocMatchingMemberInfoEmu);
|
||||
@@ -2354,7 +2364,7 @@ int sceNetAdhocMatchingSendData(MIPSState *mips, int matchingId, const char *mac
|
||||
}
|
||||
|
||||
void* data = NULL;
|
||||
if (Memory::IsValidAddress(dataAddr)) data = Memory::GetPointerWriteUnchecked(dataAddr);
|
||||
if (Memory::IsValidRange(dataAddr, dataLen)) data = Memory::GetPointerWriteUnchecked(dataAddr);
|
||||
|
||||
// Lock the peer
|
||||
std::lock_guard<std::recursive_mutex> peer_guard(peerlock);
|
||||
|
||||
Reference in new issue
Block a user