sceNetInet: The firmware's stack check, and GetPspError as it computes it

Every export of pspnet_inet.prx but the address conversions starts with
sceKernelCheckThreadStack(), and fails without doing anything if the
thread has less than 0x1000 bytes of stack free (0xFE0 for socket()):
the socket calls with -1, without setting errno, and Init, Term,
GetErrno, GetPspError and SocketAbort with 0x80410005. Each function's
limit plus its own stack frame comes to that. pspautotests
net/inet/errnothread shows it on a thread with a 4 KB stack.

GetPspError is 0x80010000 | errno, and 0 for no error, rather than a
table of guesses, so 0x80010001 on a thread with no errno record yet
(net/inet/errnothread).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-28 13:41:44 +01:00
1 parent 3f40ddc8db
commit fdbce5fbf2
5 files changed
+51 -94

No files matched your search

+48 -5
View File
@@ -94,6 +94,23 @@ static bool GetCallSocket(int socket, InetSocket **inetSock) {
return g_socketManager.GetInetSocket(socket, inetSock);
}
// Every call but the address conversions first checks that the thread has 0x1000 bytes of stack free
// (0xFE0 for socket()), and fails without doing anything if not: the socket calls with -1, without
// setting errno, and the others with 0x80410005 (pspautotests net/inet/errnothread).
static bool InetStackOk(u32 needed = 0x1000) {
const u32 stackStart = __KernelGetCurThreadStackStart();
return stackStart == 0 || currentMIPS->r[MIPS_REG_SP] - stackStart >= needed;
}
#define INET_CHECK_STACK_SOCKET(...) \
if (!InetStackOk(__VA_ARGS__)) { \
return hleLogError(Log::sceNet, -1, "not enough stack"); \
}
#define INET_CHECK_STACK() \
if (!InetStackOk()) { \
return hleLogError(Log::sceNet, ERROR_NET_NOT_ENOUGH_STACK, "not enough stack"); \
}
// Game memory that a host call will read or write directly. Fails with EFAULT rather than letting
// the host run past the end of it.
static bool InetValidRange(u32 ptr, u32 size, const char *func, SceUID threadID = __KernelGetCurThread()) {
@@ -205,6 +222,7 @@ void __NetInetShutdown() {
}
static int sceNetInetInit() {
INET_CHECK_STACK();
if (netInetInited)
return hleLogError(Log::sceNet, SCE_NET_INET_ERROR_ALREADY_INITIALIZED);
g_inetLastErrno.clear();
@@ -213,12 +231,14 @@ static int sceNetInetInit() {
}
static int sceNetInetTerm() {
INET_CHECK_STACK();
INFO_LOG(Log::sceNet, "sceNetInetTerm()");
__NetInetShutdown();
return hleLogDebug(Log::sceNet, 0);
}
static int sceNetInetGetErrno(MIPSState *mips) {
INET_CHECK_STACK();
const auto it = g_inetLastErrno.find(__KernelGetCurThread());
if (it == g_inetLastErrno.end()) {
return hleLogDebug(Log::sceNet, ERROR_NET_NO_SPACE, "no call on this thread yet");
@@ -233,11 +253,13 @@ static int sceNetInetGetErrno(MIPSState *mips) {
}
static int sceNetInetGetPspError(MIPSState *mips) {
int threadID = __KernelGetCurThread();
const int lastErrno = g_inetLastErrno[threadID];
uint32_t error = convertInetErrno2PSPError(lastErrno);
return hleLogInfo(Log::sceNet, error, "returning %s converted to %08x at %08x", convertInetErrno2str(lastErrno), error, mips->pc);
INET_CHECK_STACK();
// errno as an SCE error code, 0 staying 0. So a thread with no record yet gets 0x80010001
// (pspautotests net/inet/errnothread).
const auto it = g_inetLastErrno.find(__KernelGetCurThread());
const u32 lastErrno = it == g_inetLastErrno.end() ? (u32)ERROR_NET_NO_SPACE : (u32)it->second;
const u32 error = lastErrno == 0 ? 0 : (0x80010000 | (lastErrno & 0xFFFF));
return hleLogDebug(Log::sceNet, error, "errno %s at %08x", convertInetErrno2str(lastErrno), mips->pc);
}
// The address parsers work like 4.4BSD's, which the host's may not (pspautotests net/inet/addr).
@@ -427,6 +449,7 @@ static void WriteSockaddrToPSP(const SockAddrIN4 &saddr, u32 namePtr, u32 namele
}
static int sceNetInetGetpeername(int socket, u32 namePtr, u32 namelenPtr) {
INET_CHECK_STACK_SOCKET();
if (!Memory::IsValidAddress(namePtr) || !Memory::IsValidRange(namelenPtr, 4)) {
UpdateErrnoFromHost(__KernelGetCurThread(), EFAULT, __FUNCTION__);
return hleLogError(Log::sceNet, -1, "invalid arg");
@@ -454,6 +477,7 @@ static int sceNetInetGetpeername(int socket, u32 namePtr, u32 namelenPtr) {
}
static int sceNetInetGetsockname(int socket, u32 namePtr, u32 namelenPtr) {
INET_CHECK_STACK_SOCKET();
if (!Memory::IsValidAddress(namePtr) || !Memory::IsValidRange(namelenPtr, 4)) {
UpdateErrnoFromHost(__KernelGetCurThread(), EFAULT, __FUNCTION__);
return hleLogError(Log::sceNet, -1, "invalid arg");
@@ -899,6 +923,7 @@ static void __NetInetWaitPoll(u64 userdata, int cyclesLate) {
// FIXME: nfds is number of fd(s) as in posix poll, or was it maximum fd value as in posix select? Star Wars Battlefront Renegade seems to set the nfds to 64, while Coded Arms Contagion is using 256
int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr, u32 timeoutPtr) {
INET_CHECK_STACK_SOCKET();
InetThreadSeen();
for (u32 ptr : { readfdsPtr, writefdsPtr, exceptfdsPtr }) {
if (ptr && !InetValidRange(ptr, sizeof(SceNetInetFdSet), __FUNCTION__)) {
@@ -936,6 +961,7 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr
}
int sceNetInetPoll(MIPSState *mips, u32 fdsPtr, u32 nfds, int timeout) {
INET_CHECK_STACK_SOCKET();
DEBUG_LOG(Log::sceNet, "UNTESTED sceNetInetPoll(%08x, %d, %i) at %08x", fdsPtr, nfds, timeout, mips->pc);
InetThreadSeen();
if (nfds > PSP_NET_INET_FD_SETSIZE)
@@ -960,6 +986,7 @@ int sceNetInetPoll(MIPSState *mips, u32 fdsPtr, u32 nfds, int timeout) {
}
static int sceNetInetRecv(int socket, u32 bufPtr, u32 bufLen, u32 flags) {
INET_CHECK_STACK_SOCKET();
InetSocket *inetSock;
if (!GetCallSocket(socket, &inetSock)) {
return InetBadSocket(socket, __FUNCTION__);
@@ -985,6 +1012,7 @@ static int sceNetInetRecv(int socket, u32 bufPtr, u32 bufLen, u32 flags) {
}
static int sceNetInetSend(int socket, u32 bufPtr, u32 bufLen, u32 flags) {
INET_CHECK_STACK_SOCKET();
InetSocket *inetSock;
if (!GetCallSocket(socket, &inetSock)) {
return InetBadSocket(socket, __FUNCTION__);
@@ -1012,6 +1040,7 @@ static int sceNetInetSend(int socket, u32 bufPtr, u32 bufLen, u32 flags) {
}
static int sceNetInetSocket(MIPSState *mips, int domain, int type, int protocol) {
INET_CHECK_STACK_SOCKET(0xFE0);
INFO_LOG(Log::sceNet, "sceNetInetSocket(%d, %d, %d) at %08x - Socket: Domain = %s, Type = %s, Protocol = %s",
domain, type, protocol, mips->pc, inetSocketDomain2str(domain).c_str(), inetSocketType2str(type).c_str(), inetSocketProto2str(protocol).c_str());
@@ -1202,6 +1231,7 @@ static int InetSetsockopt(MIPSState *mips, int socket, int level, int optname, u
}
static int sceNetInetSetsockopt(MIPSState *mips, int socket, int level, int optname, u32 optvalPtr, int optlen) {
INET_CHECK_STACK_SOCKET();
const int result = InetSetsockopt(mips, socket, level, optname, optvalPtr, optlen);
if (result >= 0) {
ClearInetErrno();
@@ -1210,6 +1240,7 @@ static int sceNetInetSetsockopt(MIPSState *mips, int socket, int level, int optn
}
static int sceNetInetGetsockopt(MIPSState *mips, int socket, int level, int optname, u32 optvalPtr, u32 optlenPtr) {
INET_CHECK_STACK_SOCKET();
DEBUG_LOG(Log::sceNet, "sceNetInetGetsockopt(%i, %i, %i, %08x, %08x) at %08x: Level = %s, OptName = %s", socket, level, optname, optvalPtr, optlenPtr, mips->pc,
inetSockoptLevel2str(level).c_str(), inetSockoptName2str(optname, level).c_str());
@@ -1298,6 +1329,7 @@ static int sceNetInetGetsockopt(MIPSState *mips, int socket, int level, int optn
}
static int sceNetInetBind(MIPSState *mips, int socket, u32 namePtr, int namelen) {
INET_CHECK_STACK_SOCKET();
INFO_LOG(Log::sceNet, "sceNetInetBind(%i, %08x, %i) at %08x", socket, namePtr, namelen, mips->pc);
InetSocket *inetSock;
@@ -1421,6 +1453,7 @@ static InetTry TryConnectDone(SceUID threadID, const u32 *args) {
}
static int sceNetInetConnect(int socket, u32 sockAddrPtr, int sockAddrLen) {
INET_CHECK_STACK_SOCKET();
InetSocket *inetSock;
if (!GetCallSocket(socket, &inetSock)) {
return InetBadSocket(socket, __FUNCTION__);
@@ -1483,6 +1516,7 @@ static int sceNetInetConnect(int socket, u32 sockAddrPtr, int sockAddrLen) {
}
static int sceNetInetListen(int socket, int backlog) {
INET_CHECK_STACK_SOCKET();
InetSocket *inetSock;
if (!GetCallSocket(socket, &inetSock)) {
return InetBadSocket(socket, __FUNCTION__);
@@ -1558,6 +1592,7 @@ static InetTry TryAccept(SceUID threadID, const u32 *args) {
}
static int sceNetInetAccept(int socket, u32 addrPtr, u32 addrLenPtr) {
INET_CHECK_STACK_SOCKET();
InetSocket *inetSock;
if (!GetCallSocket(socket, &inetSock)) {
return InetBadSocket(socket, __FUNCTION__);
@@ -1582,6 +1617,7 @@ static int sceNetInetAccept(int socket, u32 addrPtr, u32 addrLenPtr) {
}
static int sceNetInetShutdown(int socket, int how) {
INET_CHECK_STACK_SOCKET();
InetSocket *inetSock;
if (!GetCallSocket(socket, &inetSock)) {
return InetBadSocket(socket, __FUNCTION__);
@@ -1616,6 +1652,7 @@ static int sceNetInetShutdown(int socket, int how) {
}
static int sceNetInetSocketAbort(int socket) {
INET_CHECK_STACK();
InetSocket *inetSock;
if (!GetCallSocket(socket, &inetSock)) {
return InetBadSocket(socket, __FUNCTION__);
@@ -1627,6 +1664,7 @@ static int sceNetInetSocketAbort(int socket) {
}
static int sceNetInetClose(int socket) {
INET_CHECK_STACK_SOCKET();
InetSocket *inetSock;
if (!GetCallSocket(socket, &inetSock)) {
return InetBadSocket(socket, __FUNCTION__);
@@ -1639,6 +1677,7 @@ static int sceNetInetClose(int socket) {
// TODO: How is this different than just sceNetInetClose?
static int sceNetInetCloseWithRST(int socket) {
INET_CHECK_STACK_SOCKET();
InetSocket *inetSock;
if (!GetCallSocket(socket, &inetSock)) {
return InetBadSocket(socket, __FUNCTION__);
@@ -1689,6 +1728,7 @@ static InetTry TryRecvFrom(SceUID threadID, const u32 *args) {
}
static int sceNetInetRecvfrom(int socket, u32 bufferPtr, int len, int flags, u32 fromPtr, u32 fromlenPtr) {
INET_CHECK_STACK_SOCKET();
InetSocket *inetSock;
if (!GetCallSocket(socket, &inetSock)) {
return InetBadSocket(socket, __FUNCTION__);
@@ -1782,6 +1822,7 @@ static InetTry TrySendTo(SceUID threadID, const u32 *args) {
}
static int sceNetInetSendto(int socket, u32 bufferPtr, int len, int flags, u32 toPtr, int tolen) {
INET_CHECK_STACK_SOCKET();
InetSocket *inetSock;
if (!GetCallSocket(socket, &inetSock)) {
return InetBadSocket(socket, __FUNCTION__);
@@ -1865,6 +1906,7 @@ static bool ReadInetMsghdr(u32 msghdrPtr, InetMsghdr *msg, std::vector<HostIovec
// Any control data fails it with EINVAL, as does an address shorter than a sockaddr_in, on UDP and
// TCP alike (pspautotests net/inet/msg).
static int sceNetInetSendmsg(int socket, u32 msghdrPtr, int flags) {
INET_CHECK_STACK_SOCKET();
InetSocket *inetSock;
if (!GetCallSocket(socket, &inetSock)) {
return InetBadSocket(socket, __FUNCTION__);
@@ -2026,6 +2068,7 @@ static InetTry TryRecvMsg(SceUID threadID, const u32 *args) {
// Games using this: World of Poker.
static int sceNetInetRecvmsg(MIPSState *mips, int socket, u32 msghdrPtr, int flags) {
INET_CHECK_STACK_SOCKET();
InetSocket *inetSock;
if (!GetCallSocket(socket, &inetSock)) {
return InetBadSocket(socket, __FUNCTION__);