
This was a long-standing bug since Luma3DS v8.0. If you changed the homebrew title and didn't immediately reboot, then the ExHeader during termination would not match the ExHeader that was was used for loading the process, and thus sysmodule process refcounts would get all messed up. The obvious solution to this is to ensure no application is running while changing the hbldr titleID (hence the changes in custom PM). This was quite possibly one of the cause of homebrew failing to load when using N3DS H&S.
118 lines
4.3 KiB
C
118 lines
4.3 KiB
C
#include <3ds.h>
|
|
#include <string.h>
|
|
#include "process_monitor.h"
|
|
#include "exheader_info_heap.h"
|
|
#include "termination.h"
|
|
#include "reslimit.h"
|
|
#include "manager.h"
|
|
#include "util.h"
|
|
#include "luma_shared_config.h"
|
|
|
|
static void cleanupProcess(ProcessData *process)
|
|
{
|
|
if (process->flags & PROCESSFLAG_DEPENDENCIES_LOADED) {
|
|
ExHeader_Info *exheaderInfo = ExHeaderInfoHeap_New();
|
|
|
|
if (exheaderInfo == NULL) {
|
|
panic(0);
|
|
}
|
|
|
|
listAndTerminateDependencies(process, exheaderInfo);
|
|
|
|
ExHeaderInfoHeap_Delete(exheaderInfo);
|
|
}
|
|
|
|
if (!(process->flags & PROCESSFLAG_KIP)) {
|
|
SRVPM_UnregisterProcess(process->pid);
|
|
FSREG_Unregister(process->pid);
|
|
LOADER_UnregisterProgram(process->programHandle);
|
|
}
|
|
|
|
ProcessList_Lock(&g_manager.processList);
|
|
if (g_manager.runningApplicationData != NULL && process->handle == g_manager.runningApplicationData->handle) {
|
|
if (IS_N3DS && OS_KernelConfig->app_memtype == 6) {
|
|
assertSuccess(resetAppMemLimit());
|
|
}
|
|
g_manager.runningApplicationData = NULL;
|
|
|
|
// We need to do this here to ensure that the ExHeader at init matches the ExHeader
|
|
// at termination at all times, otherwise the process refcounts of sysmodules
|
|
// get all messed up.
|
|
Luma_SharedConfig->hbldr_3dsx_tid = Luma_SharedConfig->selected_hbldr_3dsx_tid;
|
|
}
|
|
|
|
if (g_manager.debugData != NULL && process->handle == g_manager.debugData->handle) {
|
|
g_manager.debugData = NULL;
|
|
}
|
|
ProcessList_Unlock(&g_manager.processList);
|
|
|
|
if (process->flags & PROCESSFLAG_NOTIFY_TERMINATION) {
|
|
notifySubscribers(0x110 + process->terminatedNotificationVariation);
|
|
}
|
|
}
|
|
|
|
void processMonitor(void *p)
|
|
{
|
|
(void)p;
|
|
|
|
Handle handles[0x41] = { g_manager.newProcessEvent };
|
|
|
|
for (;;) {
|
|
u32 numProcesses = 0;
|
|
bool atLeastOneTerminating = false;
|
|
ProcessData *process;
|
|
ProcessData processBackup;
|
|
s32 id = -1;
|
|
|
|
ProcessList_Lock(&g_manager.processList);
|
|
FOREACH_PROCESS(&g_manager.processList, process) {
|
|
// Rebuild the handle array
|
|
if (process->terminationStatus != TERMSTATUS_TERMINATED) {
|
|
handles[1 + numProcesses++] = process->handle;
|
|
if (process->terminationStatus == TERMSTATUS_NOTIFICATION_SENT) {
|
|
atLeastOneTerminating = true;
|
|
}
|
|
}
|
|
}
|
|
ProcessList_Unlock(&g_manager.processList);
|
|
|
|
// If no more processes are terminating, signal the event
|
|
if (g_manager.waitingForTermination && !atLeastOneTerminating) {
|
|
assertSuccess(svcSignalEvent(g_manager.allNotifiedTerminationEvent));
|
|
}
|
|
|
|
// Note: lack of assertSuccess is intentional.
|
|
svcWaitSynchronizationN(&id, handles, 1 + numProcesses, false, -1LL);
|
|
|
|
if (id > 0) {
|
|
// Note: official PM conditionally erases the process from the list, cleans up, then conditionally frees the process data
|
|
// Bug in official PM (?): it unlocks the list before setting termstatus = TERMSTATUS_TERMINATED
|
|
ProcessList_Lock(&g_manager.processList);
|
|
process = ProcessList_FindProcessByHandle(&g_manager.processList, handles[id]);
|
|
if (process != NULL) {
|
|
process->terminationStatus = TERMSTATUS_TERMINATED;
|
|
if (process->flags & PROCESSFLAG_NOTIFY_TERMINATION) {
|
|
process->flags |= PROCESSFLAG_NOTIFY_TERMINATION_TERMINATED;
|
|
}
|
|
|
|
processBackup = *process; // <-- make sure no list access is done through this node
|
|
|
|
// Note: PROCESSFLAG_NOTIFY_TERMINATION_TERMINATED can be set by terminateProcessImpl
|
|
// APT is shit, why must an app call APT to ask to terminate itself?
|
|
|
|
if (!(process->flags & PROCESSFLAG_NOTIFY_TERMINATION_TERMINATED)) {
|
|
ProcessList_Delete(&g_manager.processList, process);
|
|
}
|
|
}
|
|
ProcessList_Unlock(&g_manager.processList);
|
|
|
|
if (process != NULL) {
|
|
cleanupProcess(&processBackup);
|
|
if (!(processBackup.flags & PROCESSFLAG_NOTIFY_TERMINATION_TERMINATED)) {
|
|
svcCloseHandle(processBackup.handle);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|