mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-13 09:39:33 +01:00
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 <noreply@anthropic.com>
This commit is contained in:
1 parent
60d531f9ef
commit
13cf93c75f
1 file changed
+29
-11
+29
-11
@@ -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<HostIovec> *iov, const char *func, SceUID threadID) {
|
||||
static bool ReadInetMsghdr(u32 msghdrPtr, InetMsghdr *msg, std::vector<HostIovec> *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<HostIovec
|
||||
if (!pspIov.iov_base) {
|
||||
continue;
|
||||
}
|
||||
if (!InetValidRange(pspIov.iov_base, pspIov.iov_len, func, threadID)) {
|
||||
const bool valid = receiving ? InetValidRecvBuffer(pspIov.iov_base, pspIov.iov_len, func, threadID) : InetValidRange(pspIov.iov_base, pspIov.iov_len, func, threadID);
|
||||
if (!valid) {
|
||||
return false;
|
||||
}
|
||||
char *base = (char *)Memory::GetPointerUnchecked(pspIov.iov_base);
|
||||
const u32 len = receiving ? InetRecvSize(pspIov.iov_base, pspIov.iov_len) : pspIov.iov_len;
|
||||
#if defined(_WIN32)
|
||||
(*iov)[i].buf = base;
|
||||
(*iov)[i].len = pspIov.iov_len;
|
||||
(*iov)[i].len = len;
|
||||
#else
|
||||
(*iov)[i].iov_base = base;
|
||||
(*iov)[i].iov_len = pspIov.iov_len;
|
||||
(*iov)[i].iov_len = len;
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
@@ -2055,7 +2073,7 @@ static int sceNetInetSendmsg(int socket, u32 msghdrPtr, int flags) {
|
||||
}
|
||||
InetMsghdr msg;
|
||||
std::vector<HostIovec> 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<HostIovec> 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;
|
||||
|
||||
Reference in new issue
Block a user