From 13cf93c75fa24f288bcdbc5296fb01663525110b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 8 Nov 2026 13:54:40 +0100 Subject: [PATCH] sceNetInet: Take receive buffers that run past the end of memory Recv, Recvfrom and Recvmsg wanted the game's whole buffer length to be valid memory (5610c9ff9b), checked again on every retry of a blocked call. That length is only how much the game can take, as with PdpRecv and PtpRecv, whose buffers a PSP takes past the end of memory (pspautotests net/adhoc/pdp and ptp). Now only the start has to be valid, and the host is given no more than fits. Sends still need all of the data. Co-Authored-By: Claude Opus 5.5 --- Core/HLE/sceNetInet.cpp | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp index be33a50846..c3a044637c 100644 --- a/Core/HLE/sceNetInet.cpp +++ b/Core/HLE/sceNetInet.cpp @@ -144,6 +144,21 @@ static bool InetValidRange(u32 ptr, u32 size, const char *func, SceUID threadID return false; } +// A receive buffer's length is only how much the game can take. Treated like PdpRecv and PtpRecv's, +// which may run past the end of memory on a PSP (pspautotests net/adhoc/pdp and ptp): only the start +// has to be valid, and InetRecvSize gives the host no more than fits. +static bool InetValidRecvBuffer(u32 ptr, u32 size, const char *func, SceUID threadID = __KernelGetCurThread()) { + if (size == 0 || Memory::IsValidAddress(ptr)) { + return true; + } + SetInetErrno(ERROR_INET_EFAULT, func, threadID); + return false; +} + +static u32 InetRecvSize(u32 ptr, u32 size) { + return Memory::ClampValidSizeAt(ptr, size); +} + bool netInetInited = false; // A blocking call waiting for its sockets. Host sockets are always non-blocking: rather than block @@ -832,7 +847,7 @@ static InetTry TryRecv(SceUID threadID, const u32 *args) { } int flgs = args[3] & ~PSP_NET_INET_MSG_DONTWAIT; // removing non-POSIX flag, which is an alternative way to use non-blocking mode flgs = convertMSGFlagsPSP2Host(flgs); - int retval = recv(inetSock->sock, (char*)Memory::GetPointerOrException(args[1]), args[2], flgs | MSG_NOSIGNAL); + int retval = recv(inetSock->sock, (char*)Memory::GetPointerOrException(args[1]), InetRecvSize(args[1], args[2]), flgs | MSG_NOSIGNAL); if (retval < 0) { const int hostErrno = socket_errno; if (IsWouldBlock(hostErrno)) { @@ -944,10 +959,11 @@ static bool InetWaitArgsValid(const InetWait &wait, SceUID threadID) { } return InetValidRange(args[0], args[1] * (u32)sizeof(SceNetInetPollfd), func, threadID); case InetWaitOp::Recv: + return InetValidRecvBuffer(args[1], args[2], func, threadID); case InetWaitOp::Send: return InetValidRange(args[1], args[2], func, threadID); case InetWaitOp::RecvFrom: - return InetValidRange(args[1], args[2], func, threadID) && optional(args[4], sizeof(SceNetInetSockaddr)) && optional(args[5], 4); + return InetValidRecvBuffer(args[1], args[2], func, threadID) && optional(args[4], sizeof(SceNetInetSockaddr)) && optional(args[5], 4); case InetWaitOp::SendTo: return InetValidRange(args[1], args[2], func, threadID) && optional(args[4], sizeof(SceNetInetSockaddr)); case InetWaitOp::Accept: @@ -1109,7 +1125,7 @@ static int sceNetInetRecv(int socket, u32 bufPtr, u32 bufLen, u32 flags) { return InetBadSocket(socket, __FUNCTION__); } - if (!InetValidRange(bufPtr, bufLen, __FUNCTION__)) { + if (!InetValidRecvBuffer(bufPtr, bufLen, __FUNCTION__)) { return hleLogError(Log::sceNet, -1, "bad buffer %08x, %d bytes", bufPtr, bufLen); } @@ -1840,7 +1856,7 @@ static InetTry TryRecvFrom(SceUID threadID, const u32 *args) { socklen_t hostLen = sizeof(saddr); int flgs = args[3] & ~PSP_NET_INET_MSG_DONTWAIT; // removing non-POSIX flag, which is an alternative way to use non-blocking mode flgs = convertMSGFlagsPSP2Host(flgs); - int retval = recvfrom(inetSock->sock, (char*)Memory::GetPointerOrException(args[1]), (int)args[2], flgs | MSG_NOSIGNAL, (struct sockaddr*)&saddr.addr, &hostLen); + int retval = recvfrom(inetSock->sock, (char*)Memory::GetPointerOrException(args[1]), (int)InetRecvSize(args[1], args[2]), flgs | MSG_NOSIGNAL, (struct sockaddr*)&saddr.addr, &hostLen); if (retval < 0) { const int hostErrno = socket_errno; if (IsWouldBlock(hostErrno)) { @@ -1869,7 +1885,7 @@ static int sceNetInetRecvfrom(int socket, u32 bufferPtr, int len, int flags, u32 return InetBadSocket(socket, __FUNCTION__); } - if (!InetValidRange(bufferPtr, (u32)len, __FUNCTION__)) { + if (!InetValidRecvBuffer(bufferPtr, (u32)len, __FUNCTION__)) { return hleLogError(Log::sceNet, -1, "bad buffer %08x, %d bytes", bufferPtr, len); } if ((fromPtr && !InetValidRange(fromPtr, sizeof(SceNetInetSockaddr), __FUNCTION__)) || (fromlenPtr && !InetValidRange(fromlenPtr, 4, __FUNCTION__))) { @@ -2001,7 +2017,7 @@ typedef iovec HostIovec; // Reads a game's msghdr, checking that everything the host will read or write through it is in game // memory, and builds the host's buffer list from msg_iov. Fails with errno set. -static bool ReadInetMsghdr(u32 msghdrPtr, InetMsghdr *msg, std::vector *iov, const char *func, SceUID threadID) { +static bool ReadInetMsghdr(u32 msghdrPtr, InetMsghdr *msg, std::vector *iov, bool receiving, const char *func, SceUID threadID) { if (!InetValidRange(msghdrPtr, sizeof(InetMsghdr), func, threadID)) { return false; } @@ -2029,16 +2045,18 @@ static bool ReadInetMsghdr(u32 msghdrPtr, InetMsghdr *msg, std::vector iov; - if (!ReadInetMsghdr(msghdrPtr, &msg, &iov, __FUNCTION__, __KernelGetCurThread())) { + if (!ReadInetMsghdr(msghdrPtr, &msg, &iov, false, __FUNCTION__, __KernelGetCurThread())) { return hleLogError(Log::sceNet, -1, "bad msghdr"); } if (msg.msg_control && msg.msg_controllen) { @@ -2148,7 +2166,7 @@ static InetTry TryRecvMsg(SceUID threadID, const u32 *args) { } InetMsghdr msg; std::vector iov; - if (!ReadInetMsghdr(args[1], &msg, &iov, "sceNetInetRecvmsg", threadID)) { + if (!ReadInetMsghdr(args[1], &msg, &iov, true, "sceNetInetRecvmsg", threadID)) { return { -1, false, 0 }; } const int hostFlags = convertMSGFlagsPSP2Host(args[2] & ~PSP_NET_INET_MSG_DONTWAIT) | MSG_NOSIGNAL;