Merge pull request #7241 from ErenYeager1836/fix/savedata-native-transaction-resource-validation

fix(libSceSaveData.native): validate initialization and parameters in transaction resource lifecycle
This commit is contained in:
Dean authored and GitHub committed 2026-10-18 10:19:36 +13:00
commit 6447ed1ed7
3 files changed
+19

No files matched your search

@@ -217,6 +217,7 @@ int APS5_VABI sceSaveDataCommit(const SaveDataCommitParam* param) {
int APS5_VABI sceSaveDataCreateTransactionResource(uint32_t size) {
(void)size;
if (g_initializations == 0) return SAVE_DATA_ERROR_NOT_INITIALIZED;
return g_transaction_counter.fetch_add(1);
}
@@ -271,6 +272,7 @@ int APS5_VABI sceSaveDataDelete(const SaveDataDelete* del) {
int APS5_VABI sceSaveDataDeleteTransactionResource(int32_t resource) {
(void)resource;
if (g_initializations == 0) return SAVE_DATA_ERROR_NOT_INITIALIZED;
return SAVE_DATA_OK;
}
@@ -20,10 +20,16 @@
extern "C" {
int APS5_VABI sceSaveDataInitialize3(const void*);
int APS5_VABI sceSaveDataTerminate();
int APS5_VABI sceSaveDataCreateTransactionResource(std::uint32_t);
int APS5_VABI sceSaveDataDeleteTransactionResource(std::int32_t);
int APS5_VABI sceSaveDataSetSaveDataMemory2(const SaveDataMemorySet2*);
int APS5_VABI sceSaveDataSetupSaveDataMemory2(const SaveDataMemorySetup2*, SaveDataMemorySetupResult*);
}
namespace {
constexpr int kSaveDataNotInitialized = -2137063423;
}
static void Check(bool value, int line) {
if (!value) {
std::fprintf(stderr, "Save-data memory check failed at line %d\n", line);
@@ -39,6 +45,8 @@ static std::vector<char> Read(const std::filesystem::path& path) {
}
int main() {
Require(sceSaveDataCreateTransactionResource(1024) == kSaveDataNotInitialized);
Require(sceSaveDataDeleteTransactionResource(1) == kSaveDataNotInitialized);
const auto previous = std::filesystem::current_path();
const auto root = std::filesystem::temp_directory_path() /
("anyps5-metadata-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
@@ -54,6 +62,12 @@ int main() {
Require(static_cast<bool>(file));
}
Require(sceSaveDataInitialize3(nullptr) == 0);
const int tx0 = sceSaveDataCreateTransactionResource(0);
Require(tx0 > 0);
Require(sceSaveDataDeleteTransactionResource(tx0) == 0);
const int tx = sceSaveDataCreateTransactionResource(1024);
Require(tx > 0);
Require(sceSaveDataDeleteTransactionResource(tx) == 0);
Require(sceSaveDataInitialize3(nullptr) == 0);
Require(sceSaveDataTerminate() == 0);
SaveDataParam param{};
@@ -307,6 +321,8 @@ int main() {
Require(Read(setupBinPath) == expectedBlob512);
Require(Read(setupParamPath) == std::vector<char>(param300Bytes, param300Bytes + sizeof(setupParam)));
Require(sceSaveDataTerminate() == 0);
Require(sceSaveDataCreateTransactionResource(1024) == kSaveDataNotInitialized);
Require(sceSaveDataDeleteTransactionResource(tx) == kSaveDataNotInitialized);
std::filesystem::current_path(previous);
std::filesystem::remove_all(root);
}
+1
View File
@@ -2,6 +2,7 @@
### Unknown function info
- [sceSaveDataCreateTransactionResource, sceSaveDataDeleteTransactionResource](../../../core/libs/prx/libSceSaveData.native/Export.cpp) (libSceSaveData.native) - transaction resources return synthetic positive identifiers and validate initialization state; resource parameter validation and transactional rollback semantics on host filesystem storage are unknown
- [sceSaveDataPrepareForTransferring](../../../core/libs/prx/libSceSaveData.native/Export.cpp) (libSceSaveData.native, NID `RvID8BVeg+g`) - declared without parameters as a throwing stub; Minecraft (PPSA17221) imports it beside `sceSaveDataTransferringMountPs4` and `sceSaveDataDirNameSearchPs4` and fails to load without it. The name hashes to the NID but is not in aerolib; it was found by combining words from the libSceSaveData names in aerolib. Its arguments and behaviour are unknown.
- [sceSaveDataTransferringMountPs4, sceSaveDataDirNameSearchPs4](../../../core/libs/prx/libSceSaveData.native/Export.cpp) (libSceSaveData.native) - arguments assumed to be those of `sceSaveDataTransferringMount` and `sceSaveDataDirNameSearch`: KytyPS5 binds both NIDs to the PS5 functions, and [prosper](https://github.com/mattias800/prosper) captured the same result layout on a title for the search. There is no PS4 save area, so the mount returns `NOT_FOUND` and the search reports zero hits
- [sceSaveDataConvert](../../../core/libs/prx/libSceSaveData.native/Export.cpp) (libSceSaveData.native) - the parameter block (user id at `+0x00`, `SceSaveDataDirName` pointer at `+0x08`, 48 opaque bytes after it) is read from PPSA28416's single call, not confirmed by KytyPS5, shadPS4, fpPS4 or SharpProspero, which have no body for it. Host saves have no size limit, so an existing save returns `SCE_OK` without touching its files and no event is queued; what the console returns for a save that is mounted, already converted or needs no conversion is unknown. The directory name is validated as `sceSaveDataDelete` does