From 4029b5d2c98abdeb443849be15920c9e1422cbff Mon Sep 17 00:00:00 2001 From: David Date: Sun, 11 Oct 2026 12:20:14 +0200 Subject: [PATCH] fix(libSceRudp): swap sceRudpEnd and sceRudpTerminate sceRudpEnd takes no argument and shuts the library down; sceRudpTerminate takes a context id and releases that context. main had the two swapped, so a title calling sceRudpEnd() passed a stray register as a context id and the library stayed initialised. Evidence: - PS5 console measurement by hugodearaujo in #3073 (firmware 13.60): "After `sceRudpEnd()` all three return `0x80770001` again" (sceRudpGetMaxSegmentSize, sceRudpProcessEvents, sceRudpNetFlush). - AlexAltea/orbital cc408367da6a6ebbf5ac1ac559fefeadc73b2831 tools/ida/db_types.json:1971,1996: `int sceRudpEnd(void)`, `int sceRudpTerminate(int ctxId)`. - RPCS3 26098fb42d090cd522b4afcf3b754dd987c82978 rpcs3/Emu/Cell/Modules/cellRudp.cpp:108: `cellRudpEnd()` tears the library down. - AndnixSH/Vita3K-PS-Vita-OS-Fork c86ac732bc811057e690bed280c5076bdf95385e vita3k/modules/SceRudp/SceLibRudp.cpp:1032,1614: `sceRudpEnd()` clears every context, `sceRudpTerminate(int ctx_id)` erases one. The bodies are unchanged, only exchanged. guest_offline_imports now ends the library with sceRudpEnd(), and guest_rudp_status checks that sceRudpTerminate releases one context and sceRudpEnd all of them. Co-Authored-By: Claude Fable 5.1 --- core/libs/prx/libSceRudp/Export.cpp | 18 +++++++++--------- core/libs/tests/GuestOfflineImports.cpp | 4 ++-- core/libs/tests/GuestRudpStatus.cpp | 19 +++++++++++++++++++ 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/core/libs/prx/libSceRudp/Export.cpp b/core/libs/prx/libSceRudp/Export.cpp index bbd31b09a..2bf31d06e 100644 --- a/core/libs/prx/libSceRudp/Export.cpp +++ b/core/libs/prx/libSceRudp/Export.cpp @@ -77,15 +77,7 @@ int APS5_VABI sceRudpGetStatus(void* status, std::size_t size) { return 0; } -int APS5_VABI sceRudpEnd(int ctx_id) { - std::lock_guard lk(g_mutex); - if (!g_inited) { - return RUDP_ERROR_NOT_INITIALIZED; - } - return g_ctx.erase(ctx_id) != 0 ? 0 : RUDP_ERROR_INVALID_CONTEXT_ID; -} - -int APS5_VABI sceRudpTerminate(void) { +int APS5_VABI sceRudpEnd(void) { std::lock_guard lk(g_mutex); if (!g_inited) { return RUDP_ERROR_NOT_INITIALIZED; @@ -95,6 +87,14 @@ int APS5_VABI sceRudpTerminate(void) { return 0; } +int APS5_VABI sceRudpTerminate(int ctx_id) { + std::lock_guard lk(g_mutex); + if (!g_inited) { + return RUDP_ERROR_NOT_INITIALIZED; + } + return g_ctx.erase(ctx_id) != 0 ? 0 : RUDP_ERROR_INVALID_CONTEXT_ID; +} + int APS5_VABI sceRudpEnableInternalIOThread(uint32_t stack_size, uint32_t priority) { (void)stack_size; (void)priority; diff --git a/core/libs/tests/GuestOfflineImports.cpp b/core/libs/tests/GuestOfflineImports.cpp index a3607a27b..e5b4c79a1 100644 --- a/core/libs/tests/GuestOfflineImports.cpp +++ b/core/libs/tests/GuestOfflineImports.cpp @@ -25,7 +25,7 @@ int APS5_VABI sceNpEntitlementAccessPollServiceEntitlementInfo(); int APS5_VABI sceRudpInit_nid_postfix(void*, int); int APS5_VABI sceRudpActivate(); int APS5_VABI sceRudpGetStatus(void*, std::size_t); -int APS5_VABI sceRudpTerminate(); +int APS5_VABI sceRudpEnd(); } static void Require(bool value) { if (!value) std::abort(); } @@ -95,6 +95,6 @@ int main() { Require(sceRudpGetStatus(status.data(), status.size()) == 0); for (unsigned char byte : status) Require(byte == 0); Require(sceRudpGetStatus(nullptr, 0) == static_cast(0x80770004)); - Require(sceRudpTerminate() == 0); + Require(sceRudpEnd() == 0); return 0; } diff --git a/core/libs/tests/GuestRudpStatus.cpp b/core/libs/tests/GuestRudpStatus.cpp index 3a6e229e3..ab845fffa 100644 --- a/core/libs/tests/GuestRudpStatus.cpp +++ b/core/libs/tests/GuestRudpStatus.cpp @@ -11,6 +11,8 @@ using GuestRudpEventHandler = void(APS5_VABI*)(int, int, int, void*); int APS5_VABI sceRudpInit_nid_postfix(void*, int); int APS5_VABI sceRudpGetStatus(void*, std::size_t); int APS5_VABI sceRudpCreateContext(GuestRudpEventHandler, void*, int*); +int APS5_VABI sceRudpTerminate(int); +int APS5_VABI sceRudpEnd(); } static void Require(bool value) { if (!value) std::abort(); } @@ -20,6 +22,7 @@ static void APS5_VABI Handler(int, int, int, void*) {} int main() { constexpr int notInitialized = static_cast(0x80770001); constexpr int alreadyInitialized = static_cast(0x80770002); + constexpr int invalidContextId = static_cast(0x80770003); constexpr int invalidArgument = static_cast(0x80770004); constexpr int memory = static_cast(0x80770007); constexpr std::size_t statusSize = 0xF8; @@ -33,6 +36,8 @@ int main() { Require(sceRudpGetStatus(nullptr, 0) == notInitialized); Require(sceRudpGetStatus(status.data(), statusSize + 1) == notInitialized); Require(status == untouched); + Require(sceRudpEnd() == notInitialized); + Require(sceRudpTerminate(0) == notInitialized); Require(sceRudpInit_nid_postfix(nullptr, sizeof(pool)) == invalidArgument); Require(sceRudpInit_nid_postfix(pool, 0) == invalidArgument); @@ -69,5 +74,19 @@ int main() { Require(sceRudpGetStatus(status.data(), 1) == 0); Require(status[0] == 0); for (std::size_t i = 1; i < status.size(); ++i) Require(status[i] == 0x5a); + + Require(sceRudpTerminate(first) == 0); + Require(sceRudpTerminate(first) == invalidContextId); + Require(sceRudpGetStatus(status.data(), statusSize) == 0); + std::memcpy(&contexts, status.data() + currentContexts, sizeof(contexts)); + Require(contexts == 1); + + Require(sceRudpEnd() == 0); + Require(sceRudpEnd() == notInitialized); + Require(sceRudpTerminate(second) == notInitialized); + Require(sceRudpGetStatus(status.data(), statusSize) == notInitialized); + Require(sceRudpInit_nid_postfix(pool + 8, 0xC58) == 0); + Require(sceRudpTerminate(second) == invalidContextId); + Require(sceRudpEnd() == 0); return 0; }