sceNpTrophy: Fix sceNpTrophyRegisterContext for handler abortion (#9586)

* Implement handler abortion during context registration.
* Fixed recursive calls to sceNpTrophy functions such as by callbacks from registering context.
* Turns out the reader lock for context registeration was incorrect because ctxt->tropusr and trophy directory are being modified. Made it an exclusive lock after callback.
This commit is contained in:
Eladash 2021-01-12 18:14:51 +02:00 committed by GitHub
parent eb77495d5f
commit e1f95ceb54
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -18,6 +18,7 @@
#include "Emu/Cell/lv2/sys_process.h"
#include <cmath>
#include <shared_mutex>
#include "util/asm.hpp"
LOG_CHANNEL(sceNpTrophy);
@ -462,7 +463,7 @@ error_code sceNpTrophyRegisterContext(ppu_thread& ppu, u32 context, u32 handle,
const auto trophy_manager = g_fxo->get<sce_np_trophy_manager>();
reader_lock lock(trophy_manager->mtx);
std::shared_lock lock(trophy_manager->mtx);
if (!trophy_manager->is_initialized)
{
@ -470,6 +471,7 @@ error_code sceNpTrophyRegisterContext(ppu_thread& ppu, u32 context, u32 handle,
}
const auto [ctxt, error] = trophy_manager->get_context_ex(context, handle);
const auto handle_ptr = idm::get<trophy_handle_t>(handle);
if (error)
{
@ -529,13 +531,47 @@ error_code sceNpTrophyRegisterContext(ppu_thread& ppu, u32 context, u32 handle,
// * Installed
const std::string trophyPath = "/dev_hdd0/home/" + Emu.GetUsr() + "/trophy/" + ctxt->trp_name;
const s32 trp_status = fs::is_dir(vfs::get(trophyPath)) ? SCE_NP_TROPHY_STATUS_INSTALLED : SCE_NP_TROPHY_STATUS_NOT_INSTALLED;
lock.unlock();
sceNpTrophy.notice("sceNpTrophyRegisterContext(): Callback is being called (trp_status=%u)", trp_status);
// The callback is called once and then if it returns >= 0 the cb is called through events(coming from vsh) that are passed to the CB through cellSysutilCheckCallback
if (statusCb(ppu, context, fs::is_dir(vfs::get(trophyPath)) ? SCE_NP_TROPHY_STATUS_INSTALLED : SCE_NP_TROPHY_STATUS_NOT_INSTALLED, 0, 0, arg) < 0)
if (statusCb(ppu, context, trp_status, 0, 0, arg) < 0)
{
return SCE_NP_TROPHY_ERROR_PROCESSING_ABORTED;
}
std::unique_lock lock2(trophy_manager->mtx);
// Rerun error checks, the callback could have changed stuff by calling sceNpTrophy functions internally
if (!trophy_manager->is_initialized)
{
return SCE_NP_TROPHY_ERROR_NOT_INITIALIZED;
}
const auto [ctxt2, error2] = trophy_manager->get_context_ex(context, handle);
if (error2)
{
// Recheck for any errors, such as if AbortHandle was called
return error2;
}
// Paranoid checks: context/handler could have been destroyed and replaced with new ones with the same IDs
// Return an error for such cases
if (ctxt2 != ctxt)
{
return SCE_NP_TROPHY_ERROR_UNKNOWN_CONTEXT;
}
if (handle_ptr.get() != idm::check<trophy_handle_t>(handle))
{
return SCE_NP_TROPHY_ERROR_UNKNOWN_HANDLE;
}
if (!trp.Install(trophyPath))
{
sceNpTrophy.error("sceNpTrophyRegisterContext(): Failed to install trophy context '%s' (%s)", trophyPath, fs::g_tls_error);
@ -557,6 +593,8 @@ error_code sceNpTrophyRegisterContext(ppu_thread& ppu, u32 context, u32 handle,
{ SCE_NP_TROPHY_STATUS_PROCESSING_COMPLETE, 0 }
};
lock2.unlock();
lv2_obj::sleep(ppu);
// Create a counter which is destroyed after the function ends