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;