sceNet, sceNetApctl: Check arguments like the PSP

From pspautotests net/inet/offline, recorded on a PSP:

- sceNetGetMallocStat(NULL) gives 0x80410103, and sceNetThreadAbort and
  sceNetFreeThreadinfo on something that isn't a thread 0x80410104.
- sceNetApctlAddHandler takes two handlers (the internal handler calls
  aren't limited), DelHandler tells an impossible ID (0x80410A82) from a
  free one (INVALID_ID), and GetState(NULL) and
  GetBSSDescIDListUser(NULL size) give 0x80410A03. A NULL buffer in
  GetBSSDescIDListUser asks for the size instead of failing.

GetState before Init and GetInfo while disconnected still succeed: the
first check was disabled on purpose at some point, and the simulated
infrastructure connection relies on the second.

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 09:52:22 +01:00
1 parent 8a48a157d4
commit 03076115bd
2 files changed
+33 -9

No files matched your search

+32 -9
View File
@@ -1065,12 +1065,24 @@ static int sceNetInit(MIPSState *mips, u32 poolSize, u32 calloutPri, u32 callout
// Free(delete) thread info / data.
// Normal usage: sceKernelDeleteThread followed by sceNetFreeThreadInfo with the same threadID as argument
// Both take any thread, also the current one or one that has ended (pspautotests net/inet/offline).
static bool IsThreadID(SceUID thid) {
int type = 0;
return thid != 0 && kernelObjects.GetIDType(thid, &type) && type == SCE_KERNEL_TMID_Thread;
}
static int sceNetFreeThreadinfo(SceUID thid) {
if (!IsThreadID(thid)) {
return hleLogError(Log::sceNet, 0x80410104, "not a thread");
}
return hleLogError(Log::sceNet, 0, "UNIMPL");
}
// Abort a thread.
static int sceNetThreadAbort(SceUID thid) {
if (!IsThreadID(thid)) {
return hleLogError(Log::sceNet, 0x80410104, "not a thread");
}
return hleLogError(Log::sceNet, 0, "UNIMPL");
}
@@ -1184,7 +1196,7 @@ static void sceNetEtherStrton(u32 bufferPtr, u32 macPtr) {
static int sceNetGetMallocStat(u32 statPtr) {
auto stat = PSPPointer<SceNetMallocStat>::Create(statPtr);
if (!stat.IsValid())
return hleLogError(Log::sceNet, 0, "invalid address");
return hleLogError(Log::sceNet, 0x80410103, "invalid address");
*stat = netMallocStat;
stat.NotifyWrite("sceNetGetMallocStat");
@@ -1497,6 +1509,10 @@ static int NetApctl_AddHandler(u32 handlerPtr, u32 handlerArg) {
// TODO: Should we allow the same handler to be added more than once?
static u32 sceNetApctlAddHandler(u32 handlerPtr, u32 handlerArg) {
INFO_LOG(Log::sceNet, "%s(%08x, %08x)", __FUNCTION__, handlerPtr, handlerArg);
// A game gets two (pspautotests net/inet/offline).
if (apctlHandlers.size() >= MAX_APCTL_GAME_HANDLERS) {
return hleLogError(Log::sceNet, 0x80410A0F, "too many handlers");
}
return NetApctl_AddHandler(handlerPtr, handlerArg);
}
@@ -1513,6 +1529,14 @@ static int NetApctl_DelHandler(u32 handlerID) {
static int sceNetApctlDelHandler(u32 handlerID) {
INFO_LOG(Log::sceNet, "%s(%d)", __FUNCTION__, handlerID);
// An ID that can't be a handler's (100, -1) and a free one give different errors
// (pspautotests net/inet/offline). Where the range ends is inferred from the handler limit.
if (handlerID >= MAX_APCTL_GAME_HANDLERS) {
return hleLogError(Log::sceNet, 0x80410A82, "bad handler id");
}
if (apctlHandlers.find(handlerID) == apctlHandlers.end()) {
return hleLogError(Log::sceNet, SCE_NET_APCTL_ERROR_INVALID_ID, "no such handler");
}
return NetApctl_DelHandler(handlerID);
}
@@ -1575,7 +1599,7 @@ static int sceNetApctlGetState(u32 pStateAddr) {
return hleLogDebug(Log::sceNet, 0);
}
return hleLogError(Log::sceNet, -1, "apctl invalid arg"); // 0x8002013A or ERROR_NET_WLAN_INVALID_ARG ?
return hleLogError(Log::sceNet, 0x80410A03, "apctl invalid arg"); // pspautotests net/inet/offline
}
// This one logs like a syscall because it's called at the end of some.
@@ -1601,8 +1625,9 @@ int NetApctl_GetBSSDescIDListUser(u32 sizeAddr, u32 bufAddr) {
const int userInfoSize = 8; // 8 bytes per entry (next address + entry id)
// Faking 4 entries, games like MGS:PW Recruit will need to have a different AP for each entry
int entries = 4;
if (!Memory::IsValid4AlignedAddress(sizeAddr) || !Memory::IsValidAddress(bufAddr))
return hleLogError(Log::sceNet, -1, "apctl invalid arg"); // 0x8002013A or ERROR_NET_WLAN_INVALID_ARG ?
// A NULL buffer asks for the size (pspautotests net/inet/offline).
if (!Memory::IsValid4AlignedAddress(sizeAddr) || (bufAddr != 0 && !Memory::IsValidAddress(bufAddr)))
return hleLogError(Log::sceNet, 0x80410A03, "apctl invalid arg");
int size = Memory::ReadUnchecked_U32(sizeAddr);
// Return size required
@@ -1748,16 +1773,14 @@ static int sceNetApctlDelInternalHandler(u32 handlerID) {
static int sceNetApctlAddInternal03Handler(u32 handlerPtr, u32 handlerArg) {
ERROR_LOG(Log::sceNet, "UNIMPL %s(%08x, %08x)", __FUNCTION__, handlerPtr, handlerArg);
// This seems to be a 3rd kind of handler
// Simple forward, don't need to use hleCall
return sceNetApctlAddHandler(handlerPtr, handlerArg);
// This seems to be a 3rd kind of handler. The game handler limit doesn't apply.
return NetApctl_AddHandler(handlerPtr, handlerArg);
}
static int sceNetApctlDelInternal03Handler(u32 handlerID) {
ERROR_LOG(Log::sceNet, "UNIMPL %s(%i)", __FUNCTION__, handlerID);
// This seems to be a 3rd kind of handler
// Simple forward, don't need to use hleCall
return sceNetApctlDelHandler(handlerID);
return NetApctl_DelHandler(handlerID);
}
static int sceNetApctl_lib2_69745F0A(int handlerId) {
+1
View File
@@ -38,6 +38,7 @@
// TODO: Determine how many handlers we can actually have
const size_t MAX_APCTL_HANDLERS = 32;
const size_t MAX_APCTL_GAME_HANDLERS = 2; // Through sceNetApctlAddHandler (pspautotests net/inet/offline)
typedef void(*sceNetApctlHandler)(int oldState, int newState, int event, int error, void* pArg);