diff --git a/core/libs/prx/libSceFiber/Export.cpp b/core/libs/prx/libSceFiber/Export.cpp index de28742c01..b70e32786d 100644 --- a/core/libs/prx/libSceFiber/Export.cpp +++ b/core/libs/prx/libSceFiber/Export.cpp @@ -624,7 +624,13 @@ int32_t APS5_VABI sceFiberRename(FiberObject* object, const char* name) { int32_t APS5_VABI sceFiberOptParamInitialize(FiberOptParam* opt_param) { if (!opt_param) return SCE_FIBER_ERROR_NULL; - std::memset(opt_param, 0, FIBER_OPT_PARAM_SIZE); + const std::uint8_t zero[FIBER_OPT_PARAM_SIZE] = {}; + const auto preserved = errno; + if (!GuestCopyOut_nid_no_patch(opt_param, zero, sizeof(zero))) { + errno = preserved; + return SCE_FIBER_ERROR_INVALID; + } + errno = preserved; return SCE_OK; } diff --git a/core/libs/tests/Fiber.cpp b/core/libs/tests/Fiber.cpp index d4eedb1c3a..7850d50164 100644 --- a/core/libs/tests/Fiber.cpp +++ b/core/libs/tests/Fiber.cpp @@ -566,6 +566,23 @@ int main(int argc, char** argv) { #endif FiberObject* self = nullptr; Require(sceFiberGetSelf(&self) == FiberErrorPermission, "no fiber on the thread"); + Require(sceFiberOptParamInitialize(nullptr) == FiberErrorNull, "null opt param"); + Require(sceFiberOptParamInitialize(reinterpret_cast(0x10)) == FiberErrorInvalid, "unwritable opt param"); + struct alignas(8) AlignedOptParamBuffer { + std::uint8_t bytes[0x80]; + std::uint32_t sentinel = 0x5a5a5a5a; + } storage; + std::memset(storage.bytes, 0xA5, sizeof(storage.bytes)); + Require(sceFiberOptParamInitialize(reinterpret_cast(storage.bytes)) == 0, "initialize opt param"); + bool allZero = true; + for (std::size_t i = 0; i < sizeof(storage.bytes); ++i) { + if (storage.bytes[i] != 0) { + allZero = false; + break; + } + } + Require(allZero, "opt param cleared"); + Require(storage.sentinel == 0x5a5a5a5a, "sentinel intact"); Require(_sceFiberInitializeImpl_nid_postfix(g_first, "first", FirstEntry, 11, g_firstContext.data(), g_firstContext.size(), nullptr, 0) == 0, "initialize first"); Require(_sceFiberInitializeImpl_nid_postfix(g_second, "second", SecondEntry, 22, g_secondContext.data(), g_secondContext.size(), nullptr, 0) == 0, "initialize second"); const auto threadCsr = _mm_getcsr(); diff --git a/docs/dev/debt/libSceFiber.md b/docs/dev/debt/libSceFiber.md index da142ae02b..14aca4f493 100644 --- a/docs/dev/debt/libSceFiber.md +++ b/docs/dev/debt/libSceFiber.md @@ -2,6 +2,7 @@ ### Functional +- [sceFiberOptParamInitialize](../../../core/libs/prx/libSceFiber/Export.cpp) returns SCE_FIBER_ERROR_INVALID when the destination pointer is unwritable. This is a defensive host-protection fallback; whether the console raises a signal or faults on unmapped or read-only memory is not measured. - [sceFiberGetInfo](../../../core/libs/prx/libSceFiber/Export.cpp) reports the requested-size window at the top of the host stack used for execution, rather than the original caller buffer, which is retained and pinned. The additional 1 MiB host-call margin below that window is not included in `size_context`. Planet Coaster (PPSA01736 v01.017) uses this range for ChakraCore garbage-collector stack scans; reporting the unused caller buffer makes the scan cross unmapped memory. The range is covered by a regression test while running, switching and migrating a fiber between threads. Console behavior for substituted host stacks has no direct equivalent and is not measured. ### Unknown function info