From 185bbd5b5d3fb55d0c9dbddeefc7f12306b90350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 6 Nov 2026 17:12:03 +0100 Subject: [PATCH] sceNetAdhocPtpRecv: Take a buffer that runs past the end of memory It's possible to try to recv more than fits in memory using sceNetAdhocPtpRecv, but it's ok as long as the received data fits, as shown by the new pspautotests net/adhoc/ptp. We refused it with INVALID_ARG on every call. Now the recv is limited to what fits in memory, and the game's length is put back if the call fails without waiting. Co-Authored-By: Claude Opus 5.5 --- Core/HLE/sceNetAdhoc.cpp | 21 ++++++++++++++++++--- pspautotests | 2 +- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 8c314a5f54..299408c467 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -4500,8 +4500,8 @@ static int sceNetAdhocPtpRecv(MIPSState *mips, int id, u32 dataAddr, u32 dataSiz int * len = (int *)Memory::GetPointerOrException(dataSizeAddr); // Library is initialized if (netAdhocInited) { - // Valid Arguments - if (buf != NULL && len != NULL && *len > 0 && Memory::IsValidRange(dataAddr, *len)) { + // Valid Arguments. The buffer may run past the end of memory as long as what arrives fits (pspautotests net/adhoc/ptp). + if (buf != NULL && len != NULL && *len > 0 && Memory::IsValidAddress(dataAddr)) { // Valid Socket if (id > 0 && id <= MAX_SOCKET && adhocSockets[id - 1] != NULL) { // Cast Socket @@ -4520,7 +4520,22 @@ static int sceNetAdhocPtpRecv(MIPSState *mips, int id, u32 dataAddr, u32 dataSiz return hleLogError(Log::sceNet, SCE_NET_ADHOC_ERROR_SOCKET_ALERTED, "socket alerted"); } - return Transport().PtpRecv(id, buf, len, timeout, flag); + // Receive no more than fits in memory. A blocking recv keeps the pointer, so the clamp goes into + // the game's length. A call that fails without waiting gets it back, as a PSP leaves it alone. + const int requested = *len; + const int valid = (int)Memory::ClampValidSizeAt(dataAddr, requested); + if (valid == requested) { + return Transport().PtpRecv(id, buf, len, timeout, flag); + } + *len = valid; + int result = Transport().PtpRecv(id, buf, len, timeout, flag); + const bool waiting = std::any_of(adhocSocketRequests.begin(), adhocSocketRequests.end(), [&](const auto &it) { + return it.second.length == (s32_le *)len; + }); + if (result != 0 && !waiting) { + *len = requested; + } + return result; } // A connection whose peer has gone stays DISCONNECTED (pspautotests net/adhoc/ptp). diff --git a/pspautotests b/pspautotests index 53f6be6eeb..33e5d3876c 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit 53f6be6eeba8f021993fdfebaf7e0ebdb2a59d66 +Subproject commit 33e5d3876c72fab5cfa98e48d2b81a856770d6e0