Merge pull request #22804 from hrydgard/fix-net-logic

Net Claude code review: Fix use-after-pop, the resolver after loads, inet sockaddr/poll
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2026-10-27 09:15:05 +01:00
commit 83d73bb88e
7 files changed
+71 -41

No files matched your search

+2 -1
View File
@@ -812,7 +812,8 @@ void __NetApctlCallbacks()
// How AP works probably like this: Game use sceNetApctl function -> sceNetApctl let the hardware know and do their's thing and have a new State -> Let the game know the resulting State through Event on their handler
if (!apctlEvents.empty()) {
auto& args = apctlEvents.front();
// Copy it, the pop below destroys the element.
auto args = apctlEvents.front();
auto& oldState = args.data[0];
auto& newState = args.data[1];
auto& event = args.data[2];
+51 -31
View File
@@ -154,8 +154,23 @@ static u32_le sceNetInetInetAddr(const char *hostname) {
return hleLogDebug(Log::sceNet, retval);
}
// The PSP fills in a whole SceNetInetSockaddr (sa_len 16) and copies as much of it as namelen
// allows, setting namelen to the bytes copied (pspautotests net/inet/poll).
static void WriteSockaddrToPSP(const SockAddrIN4 &saddr, u32 namePtr, u32 namelenPtr) {
SceNetInetSockaddr name{};
name.sa_len = (u8)sizeof(name);
name.sa_family = saddr.addr.sa_family;
memcpy(name.sa_data, saddr.addr.sa_data, sizeof(name.sa_data));
const s32 namelen = (s32)Memory::ReadUnchecked_U32(namelenPtr);
const u32 copyLen = (u32)std::clamp(namelen, 0, (s32)sizeof(name));
if (Memory::IsValidRange(namePtr, copyLen)) {
Memory::Memcpy(namePtr, &name, copyLen, "InetSockaddr");
}
Memory::WriteUnchecked_U32(copyLen, namelenPtr);
}
static int sceNetInetGetpeername(int socket, u32 namePtr, u32 namelenPtr) {
if (!Memory::IsValidAddress(namePtr) || !Memory::IsValidAddress(namelenPtr)) {
if (!Memory::IsValidAddress(namePtr) || !Memory::IsValidRange(namelenPtr, 4)) {
UpdateErrnoFromHost(__KernelGetCurThread(), EFAULT, __FUNCTION__);
return hleLogError(Log::sceNet, -1, "invalid arg");
}
@@ -165,31 +180,21 @@ static int sceNetInetGetpeername(int socket, u32 namePtr, u32 namelenPtr) {
return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket);
}
SceNetInetSockaddr* name = (SceNetInetSockaddr*)Memory::GetPointerOrException(namePtr);
int* namelen = (int*)Memory::GetPointerOrException(namelenPtr);
SockAddrIN4 saddr{};
// TODO: Should've created convertSockaddrPSP2Host (and Host2PSP too) function as it's being used pretty often, thus fixing a bug on it will be tedious when scattered all over the places
saddr.addr.sa_family = name->sa_family;
int len = std::min(*namelen > 0 ? *namelen : 0, static_cast<int>(sizeof(saddr)));
name->sa_len = len;
memcpy(saddr.addr.sa_data, name->sa_data, sizeof(name->sa_data));
int retval = getpeername(inetSock->sock, (sockaddr*)&saddr, (socklen_t*)&len);
socklen_t len = sizeof(saddr);
int retval = getpeername(inetSock->sock, (sockaddr*)&saddr, &len);
DEBUG_LOG(Log::sceNet, "Getpeername: Family = %s, Address = %s, Port = %d", inetSocketDomain2str(saddr.addr.sa_family).c_str(), ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port));
*namelen = len;
if (retval < 0) {
UpdateErrnoFromHost(__KernelGetCurThread(), socket_errno, __FUNCTION__);
return hleLogError(Log::sceNet, retval);
}
// We shouldn't use the returned len here, because the returned len is the actual size needed, which can be larger than the inputted len
memcpy(name->sa_data, saddr.addr.sa_data, name->sa_len - (sizeof(name->sa_len) + sizeof(name->sa_family)));
name->sa_family = saddr.addr.sa_family;
WriteSockaddrToPSP(saddr, namePtr, namelenPtr);
return hleLogInfo(Log::sceNet, 0);
}
static int sceNetInetGetsockname(int socket, u32 namePtr, u32 namelenPtr) {
if (!Memory::IsValidAddress(namePtr) || !Memory::IsValidAddress(namelenPtr)) {
if (!Memory::IsValidAddress(namePtr) || !Memory::IsValidRange(namelenPtr, 4)) {
UpdateErrnoFromHost(__KernelGetCurThread(), EFAULT, __FUNCTION__);
return hleLogError(Log::sceNet, -1, "invalid arg");
}
@@ -199,25 +204,16 @@ static int sceNetInetGetsockname(int socket, u32 namePtr, u32 namelenPtr) {
return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket);
}
SceNetInetSockaddr* name = (SceNetInetSockaddr*)Memory::GetPointerOrException(namePtr);
int* namelen = (int*)Memory::GetPointerOrException(namelenPtr);
SockAddrIN4 saddr{};
saddr.addr.sa_family = name->sa_family;
int len = std::min(*namelen > 0 ? *namelen : 0, static_cast<int>(sizeof(saddr)));
name->sa_len = len;
memcpy(saddr.addr.sa_data, name->sa_data, sizeof(name->sa_data));
int retval = getsockname(inetSock->sock, (sockaddr*)&saddr, (socklen_t*)&len);
socklen_t len = sizeof(saddr);
int retval = getsockname(inetSock->sock, (sockaddr*)&saddr, &len);
DEBUG_LOG(Log::sceNet, "Getsockname: Family = %s, Address = %s, Port = %d", inetSocketDomain2str(saddr.addr.sa_family).c_str(), ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port));
*namelen = len;
if (retval < 0) {
UpdateErrnoFromHost(__KernelGetCurThread(), socket_errno, __FUNCTION__);
return hleLogError(Log::sceNet, retval);
}
// We shouldn't use the returned len here, because the returned len is the actual size needed, which can be larger than the inputted len
memcpy(name->sa_data, saddr.addr.sa_data, name->sa_len - (sizeof(name->sa_len) + sizeof(name->sa_family)));
name->sa_family = saddr.addr.sa_family;
WriteSockaddrToPSP(saddr, namePtr, namelenPtr);
return hleLogInfo(Log::sceNet, 0);
}
@@ -370,7 +366,13 @@ int sceNetInetPoll(MIPSState *mips, u32 fdsPtr, u32 nfds, int timeout) { // time
UpdateErrnoFromHost(__KernelGetCurThread(), EINVAL, __FUNCTION__);
return hleLogError(Log::sceNet, -1, "invalid socket id");
}
SOCKET hostSocket = g_socketManager.GetHostSocketFromInetSocket(fdarray[i].fd);
fdarray[i].revents = 0;
InetSocket *inetSock;
if (!g_socketManager.GetInetSocket(fdarray[i].fd, &inetSock)) {
// Not open, reported as POLLNVAL below.
continue;
}
SOCKET hostSocket = inetSock->sock;
if (hostSocket > maxHostFd) {
maxHostFd = hostSocket;
}
@@ -378,7 +380,6 @@ int sceNetInetPoll(MIPSState *mips, u32 fdsPtr, u32 nfds, int timeout) { // time
FD_SET(hostSocket, &readfds);
FD_SET(hostSocket, &writefds);
FD_SET(hostSocket, &exceptfds);
fdarray[i].revents = 0;
}
timeval tmout = { 5, 543210 }; // Workaround timeout value when timeout = NULL
@@ -395,7 +396,14 @@ int sceNetInetPoll(MIPSState *mips, u32 fdsPtr, u32 nfds, int timeout) { // time
retval = 0;
for (int i = 0; i < (s32)nfds; i++) {
SOCKET hostSocket = g_socketManager.GetHostSocketFromInetSocket(fdarray[i].fd);
InetSocket *inetSock;
if (!g_socketManager.GetInetSocket(fdarray[i].fd, &inetSock)) {
// As on hardware (pspautotests net/inet/poll).
fdarray[i].revents = INET_POLLNVAL;
retval++;
continue;
}
SOCKET hostSocket = inetSock->sock;
if ((fdarray[i].events & (INET_POLLRDNORM | INET_POLLIN)) && FD_ISSET(hostSocket, &readfds))
fdarray[i].revents |= (INET_POLLRDNORM | INET_POLLIN); //POLLIN_SET
if ((fdarray[i].events & (INET_POLLWRNORM | INET_POLLOUT)) && FD_ISSET(hostSocket, &writefds))
@@ -543,8 +551,15 @@ static int sceNetInetSetsockopt(MIPSState *mips, int socket, int level, int optn
tval.tv_sec = optval / 1000000; // seconds
tval.tv_usec = (optval % 1000000); // microseconds
retval = setsockopt(inetSock->sock, convertSockoptLevelPSP2Host(level), convertSockoptNamePSP2Host(optname, level), (char*)&tval, sizeof(tval));
} else {
} else if (optlen >= 0 && optlen <= (int)sizeof(optval)) {
retval = setsockopt(inetSock->sock, convertSockoptLevelPSP2Host(level), convertSockoptNamePSP2Host(optname, level), (char*)&optval, optlen);
} else {
// Bigger options are passed through from the game's memory.
if (!Memory::IsValidRange(optvalPtr, optlen)) {
UpdateErrnoFromHost(__KernelGetCurThread(), EFAULT, __FUNCTION__);
return hleLogError(Log::sceNet, -1, "invalid arg");
}
retval = setsockopt(inetSock->sock, convertSockoptLevelPSP2Host(level), convertSockoptNamePSP2Host(optname, level), (const char *)Memory::GetPointerUnchecked(optvalPtr), optlen);
}
if (retval < 0) {
UpdateErrnoFromHost(__KernelGetCurThread(), socket_errno, __FUNCTION__);
@@ -1055,6 +1070,8 @@ static int sceNetInetSendmsg(int socket, u32 msghdrPtr, int flags) {
if (pspMsghdr->msg_controllen >= sizeof(InetCmsghdr)) {
// TODO: Creates our own CMSG_* macros (32-bit version of it, similar to the one on PSP) to avoid alignment/size issue that can lead to memory corruption/out of bound issue.
for (WSACMSGHDR* cmsgptr = WSA_CMSG_FIRSTHDR(&hdr); cmsgptr != NULL; cmsgptr = WSA_CMSG_NXTHDR(&hdr, cmsgptr)) {
if (cmsgptr->cmsg_len < sizeof(WSACMSGHDR))
break;
cmsgptr->cmsg_type = convertCMsgTypePSP2Host(cmsgptr->cmsg_type, cmsgptr->cmsg_level);
cmsgptr->cmsg_level = convertSockoptLevelPSP2Host(cmsgptr->cmsg_level);
}
@@ -1065,6 +1082,9 @@ static int sceNetInetSendmsg(int socket, u32 msghdrPtr, int flags) {
// Note: Many existing implementations of CMSG_FIRSTHDR never look at msg_controllen and just return the value of cmsg_control.
if (pspMsghdr->msg_controllen >= sizeof(InetCmsghdr)) {
for (cmsghdr* cmsgptr = CMSG_FIRSTHDR(&hdr); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&hdr, cmsgptr)) {
// Some CMSG_NXTHDR implementations return the same header again for a zero length.
if (cmsgptr->cmsg_len < sizeof(cmsghdr))
break;
cmsgptr->cmsg_type = convertCMsgTypePSP2Host(cmsgptr->cmsg_type, cmsgptr->cmsg_level);
cmsgptr->cmsg_level = convertSockoptLevelPSP2Host(cmsgptr->cmsg_level);
}
+4 -2
View File
@@ -71,13 +71,15 @@ static int sceNetResolverInit() {
return hleLogInfo(Log::sceNet, 0);
}
// Also called on savestate load, so go back to the boot state, where it's running.
void __NetResolverShutdown() {
g_netResolvers.clear();
g_netResolverInitialized = false;
g_netResolverInitialized = true;
}
static int sceNetResolverTerm() {
__NetResolverShutdown();
g_netResolvers.clear();
g_netResolverInitialized = false;
return hleLogInfo(Log::sceNet, 0);
}
+2 -1
View File
@@ -125,7 +125,8 @@ bool NpAuthProcessEvents() {
return false;
}
auto& args = npAuthEvents.front();
// Copy it, the pop below destroys the element.
auto args = npAuthEvents.front();
auto& id = args.data[0];
auto& result = args.data[1];
auto& argAddr = args.data[2];
+10 -5
View File
@@ -54,7 +54,8 @@ bool NpMatching2ProcessEvents() {
return false;
}
auto& args = npMatching2Events.front();
// Copy it, the pop below destroys the element.
auto args = npMatching2Events.front();
auto& event = args.data[0];
auto& stat = args.data[1];
auto& serverIdPtr = args.data[2];
@@ -72,10 +73,14 @@ bool NpMatching2ProcessEvents() {
}
// Per npMatching2 function callback
u32* inStruct = (u32*)Memory::GetPointerOrException(inStructPtr);
if (Memory::IsValidAddress(inStruct[0])) {
DEBUG_LOG(Log::sceNet, "NpMatching2Callback [ServerID=%i][EventID=%04x][State=%04x][FuncAddr=%08x][ArgsPtr=%08x]", *(u32*)Memory::GetPointerOrException(serverIdPtr), event, stat, inStruct[0], inStruct[1]);
hleEnqueueCall(inStruct[0], 7, args.data);
// The opt param is optional.
if (Memory::IsValid4AlignedRange(inStructPtr, 8)) {
const u32 funcAddr = Memory::ReadUnchecked_U32(inStructPtr);
if (Memory::IsValidAddress(funcAddr)) {
const u32 serverId = Memory::IsValid4AlignedAddress(serverIdPtr) ? Memory::ReadUnchecked_U32(serverIdPtr) : 0;
DEBUG_LOG(Log::sceNet, "NpMatching2Callback [ServerID=%i][EventID=%04x][State=%04x][FuncAddr=%08x][ArgsPtr=%08x]", serverId, event, stat, funcAddr, Memory::ReadUnchecked_U32(inStructPtr + 4));
hleEnqueueCall(funcAddr, 7, args.data);
}
}
return true;
}
+1
View File
@@ -656,6 +656,7 @@ tests_next = [
"modules/unloadfill/unloadfill",
"net/http/http",
"net/inet/bind",
"net/inet/poll",
"net/primary/ether",
"sysmem/partition",
# These two mbx tests only appeared to work because they papered over bugs