From f4b99a6a99f8c2d2ff93edddb5606657df74aacb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 5 Nov 2026 12:45:45 +0100 Subject: [PATCH] Ad hoc, PMP: Bound guest-sized buffers and lists - Game mode master and replica data was copied by the game's size after checking one byte, into host buffers allocated by that size (up to 2 GB). If the copy failed, the uninitialized buffer was sent to peers. - sceNetAdhocctlCreateEnterGameMode read up to 16 member MACs after checking one byte. - sceNetAdhocMatchingCreate allocated and cleared the game's receive buffer size on the host with no upper bound. Cap it at 1 MB. - PMP video walked its block descriptors with only the first one checked. Co-Authored-By: Claude Opus 5.5 --- Core/HLE/sceMpeg.cpp | 3 +++ Core/HLE/sceNetAdhoc.cpp | 8 ++++++-- Core/HLE/sceNetAdhocMatching.cpp | 5 +++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 2784f788b9..33b82f5a9b 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -962,6 +962,9 @@ static bool decodePmpVideo(PSPPointer ringbuffer, u32 pmpctxA // joint all blocks into H264Frames for (int i = 0; i < pmp_nBlocks; i++){ auto lli = PSPPointer::Create(pmp_videoSource); + // Each block descriptor is in game memory, after the first. + if (!lli.IsValid()) + break; // add source block into pmpframes const uint8_t *ptr = Memory::GetPointerRangeOrException(lli->pSrc, lli->iSize); if (ptr) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 4ec5b5bc43..0cbcda4faa 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -3391,6 +3391,9 @@ int NetAdhocctl_CreateEnterGameMode(const char* group_name, int game_type, int n if (game_type < ADHOCCTL_GAMETYPE_1A || game_type > ADHOCCTL_GAMETYPE_2A || num_members < 2 || num_members > 16 || (game_type == ADHOCCTL_GAMETYPE_1A && num_members > 4)) return SCE_NET_ADHOCCTL_ERROR_INVALID_ARG; + // The whole member list is read below. + if (!Memory::IsValidRange(membersAddr, num_members * (u32)sizeof(SceNetEtherAddr))) + return SCE_NET_ADHOCCTL_ERROR_INVALID_ARG; // In a normal group, it reports ALREADY_CONNECTED through the handlers and stays in it // (pspautotests net/adhoc/gamemode). @@ -4615,7 +4618,8 @@ static int sceNetAdhocGameModeCreateMaster(MIPSState *mips, u32 dataAddr, int si if (!netAdhocGameModeEntered) return hleLogError(Log::sceNet, SCE_NET_ADHOCCTL_ERROR_NOT_ENTER_GAMEMODE, "not enter gamemode"); - if (size < 0 || !Memory::IsValidAddress(dataAddr)) + // The data is copied (and the buffers are sized) by size. + if (size < 0 || !Memory::IsValidAddress(dataAddr) || !Memory::IsValidRange(dataAddr, (u32)size)) return hleLogError(Log::sceNet, SCE_NET_ADHOCCTL_ERROR_INVALID_ARG, "invalid arg"); // pspautotests net/adhoc/pair/gamemode @@ -4672,7 +4676,7 @@ static int sceNetAdhocGameModeCreateReplica(MIPSState *mips, const char *mac, u3 if (!netAdhocGameModeEntered) return hleLogError(Log::sceNet, SCE_NET_ADHOCCTL_ERROR_NOT_ENTER_GAMEMODE, "not enter gamemode"); - if (mac == nullptr || size < 0 || !Memory::IsValidAddress(dataAddr)) + if (mac == nullptr || size < 0 || !Memory::IsValidAddress(dataAddr) || !Memory::IsValidRange(dataAddr, (u32)size)) return hleLogError(Log::sceNet, SCE_NET_ADHOCCTL_ERROR_INVALID_ARG, "invalid arg"); const SceNetEtherAddr &replicaMac = *(const SceNetEtherAddr *)mac; diff --git a/Core/HLE/sceNetAdhocMatching.cpp b/Core/HLE/sceNetAdhocMatching.cpp index fdce9166c2..687dc54dc1 100644 --- a/Core/HLE/sceNetAdhocMatching.cpp +++ b/Core/HLE/sceNetAdhocMatching.cpp @@ -1576,6 +1576,11 @@ static int sceNetAdhocMatchingCreate(MIPSState *mips, int mode, int maxnum, int // Invalid Receive Buffer Size return hleLogError(Log::sceNet, SCE_NET_ADHOC_MATCHING_ERROR_RXBUF_TOO_SHORT, "adhoc matching rxbuf too short"); } + // We allocate (and clear) this on the host. The PSP takes it from the library's small heap, and + // no packet is anywhere near this big. + if (rxbuflen > 1024 * 1024) { + return hleLogError(Log::sceNet, SCE_NET_ADHOC_MATCHING_ERROR_NO_SPACE, "adhoc matching rxbuf too large"); + } if (mode < 1 || mode > 3) { return hleLogError(Log::sceNet, SCE_NET_ADHOC_MATCHING_ERROR_INVALID_MODE, "adhoc matching invalid mode");