From b5eb0a44fb98ee2f015cb625edeb04736a4a3a53 Mon Sep 17 00:00:00 2001 From: szambonurek-88 Date: Sun, 1 Nov 2026 16:34:08 +0100 Subject: [PATCH] fix(libSceVrSetupDialog): return INVALID_ARGUMENT from sceVrSetupDialogGetResult for unwritable result --- core/libs/prx/libSceVrSetupDialog/Export.cpp | 7 ++++++- core/libs/tests/GuestVrSetupDialog.cpp | 10 ++++++++++ docs/dev/debt/libSceVrSetupDialog.md | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceVrSetupDialog/Export.cpp b/core/libs/prx/libSceVrSetupDialog/Export.cpp index f9196d0109..cd0bd2dd98 100644 --- a/core/libs/prx/libSceVrSetupDialog/Export.cpp +++ b/core/libs/prx/libSceVrSetupDialog/Export.cpp @@ -1,5 +1,6 @@ #include "prx/libSceVrSetupDialog/include/Api.hpp" #include +#include #include #include #include @@ -52,7 +53,11 @@ int APS5_VABI sceVrSetupDialogGetResult(std::int32_t* result) { if (result == nullptr) return VR_SETUP_DIALOG_ERROR_INVALID_ARGUMENT; const int status = g_status.load(); if (status == COMMON_DIALOG_STATUS_NONE) return VR_SETUP_DIALOG_ERROR_NOT_INITIALIZED; - *result = COMMON_DIALOG_RESULT_USER_CANCELED; + const std::int32_t canceled = COMMON_DIALOG_RESULT_USER_CANCELED; + const int savedErrno = errno; + const bool copied = GuestCopyOut_nid_no_patch(result, &canceled, sizeof(canceled)); + errno = savedErrno; + if (!copied) return VR_SETUP_DIALOG_ERROR_INVALID_ARGUMENT; if (status != COMMON_DIALOG_STATUS_FINISHED) return VR_SETUP_DIALOG_ERROR_BUSY; return 0; } diff --git a/core/libs/tests/GuestVrSetupDialog.cpp b/core/libs/tests/GuestVrSetupDialog.cpp index c90e9ec239..8009a1820f 100644 --- a/core/libs/tests/GuestVrSetupDialog.cpp +++ b/core/libs/tests/GuestVrSetupDialog.cpp @@ -1,5 +1,6 @@ #include "prx/libSceVrSetupDialog/include/Api.hpp" #include "prx/libc/include/general/VabiMacros.hpp" +#include #include #include #include "prx/libSceCommonDialog/CommonDialog.hpp" @@ -58,6 +59,9 @@ int main() { Require(sceVrSetupDialogGetResult(result) == kErrNotFinished); Require(result[0] == kResultUserCanceled); Require(result[1] == -1); + errno = 71; + Require(sceVrSetupDialogGetResult(reinterpret_cast(0x10)) == kErrArgNull); + Require(errno == 71); param.size = sizeof(param) - 1; Require(sceVrSetupDialogOpen(¶m) == kErrArgNull); @@ -76,6 +80,9 @@ int main() { Require(sceVrSetupDialogUpdateStatus() == kStatusFinished); Require(sceVrSetupDialogGetResult(nullptr) == kErrArgNull); + errno = 71; + Require(sceVrSetupDialogGetResult(reinterpret_cast(0x10)) == kErrArgNull); + Require(errno == 71); Require(sceVrSetupDialogGetResult(result) == 0); Require(result[0] == kResultUserCanceled); Require(result[1] == -1); @@ -106,6 +113,9 @@ int main() { Require(sceVrSetupDialogUpdateStatus() == kStatusInitialized); Require(sceVrSetupDialogGetResult(result) == kErrNotFinished); Require(result[0] == kResultUserCanceled && result[1] == -1); + errno = 71; + Require(sceVrSetupDialogGetResult(reinterpret_cast(0x10)) == kErrArgNull); + Require(errno == 71); Require(sceVrSetupDialogTerminate() == 0); Require(sceVrSetupDialogTerminate() == kErrNotInitialized); Require(sceVrSetupDialogOpen(nullptr) == kErrArgNull); diff --git a/docs/dev/debt/libSceVrSetupDialog.md b/docs/dev/debt/libSceVrSetupDialog.md index 6890166ca4..3b4ae9924f 100644 --- a/docs/dev/debt/libSceVrSetupDialog.md +++ b/docs/dev/debt/libSceVrSetupDialog.md @@ -2,4 +2,4 @@ ### Silent stubs -- [libSceVrSetupDialog](../../../core/libs/prx/libSceVrSetupDialog/Export.cpp) - the VR setup dialog runs the common-dialog state machine but shows nothing and reports the user as having cancelled, as no headset is supported (see libSceHmd2). Initialization does not check the native process eligibility record, and the 46-byte setup payload remains opaque. Open validates the recovered 56-byte parameter header. GetResult writes one 32-bit result after initialization, including when returning the not-finished error; its error codes and validation order are recovered. +- [libSceVrSetupDialog](../../../core/libs/prx/libSceVrSetupDialog/Export.cpp) - the VR setup dialog runs the common-dialog state machine but shows nothing and reports the user as having cancelled, as no headset is supported (see libSceHmd2). Initialization does not check the native process eligibility record, and the 46-byte setup payload remains opaque. Open validates the recovered 56-byte parameter header. GetResult writes one 32-bit result after initialization, including when returning the not-finished error; its error codes and validation order are recovered. Returning `0x8A7C0003` for an unwritable result pointer is a host-safety fallback with console behavior unmeasured.