From 5dc31f001ef4a7a139f2e1d3e11aa36aac44d0cf Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 17 Oct 2018 16:26:36 -0700 Subject: [PATCH] Stratosphere: Fix FS permissions for <4.0.0 KIPs --- stratosphere/pm/source/pm_main.cpp | 46 ++++++++++--- stratosphere/pm/source/pm_resource_limits.cpp | 3 +- stratosphere/pm/source/smm_ams.c | 69 +++++++++++++++++++ stratosphere/pm/source/smm_ams.h | 21 ++++++ stratosphere/sm/Makefile | 2 +- stratosphere/sm/source/sm_manager_service.cpp | 9 +++ stratosphere/sm/source/sm_manager_service.hpp | 6 +- stratosphere/sm/source/sm_registration.cpp | 37 +++++++++- stratosphere/sm/source/sm_registration.hpp | 3 + stratosphere/sm/source/sm_user_service.cpp | 2 +- 10 files changed, 183 insertions(+), 15 deletions(-) create mode 100644 stratosphere/pm/source/smm_ams.c create mode 100644 stratosphere/pm/source/smm_ams.h diff --git a/stratosphere/pm/source/pm_main.cpp b/stratosphere/pm/source/pm_main.cpp index 2c33145fb..034567ecf 100644 --- a/stratosphere/pm/source/pm_main.cpp +++ b/stratosphere/pm/source/pm_main.cpp @@ -28,6 +28,7 @@ #include "pm_process_track.hpp" #include "pm_registration.hpp" #include "pm_debug_monitor.hpp" +#include "smm_ams.h" extern "C" { extern u32 __start__; @@ -56,6 +57,20 @@ void __libnx_initheap(void) { fake_heap_end = (char*)addr + size; } +void RegisterPrivilegedProcessesWithFs() { + /* Ensures that all privileged processes are registered with full FS permissions. */ + constexpr u64 PRIVILEGED_PROCESS_MIN = 0; + constexpr u64 PRIVILEGED_PROCESS_MAX = 0x4F; + + const u32 PRIVILEGED_FAH[0x1C/sizeof(u32)] = {0x00000001, 0x00000000, 0x80000000, 0x0000001C, 0x00000000, 0x0000001C, 0x00000000}; + const u32 PRIVILEGED_FAC[0x2C/sizeof(u32)] = {0x00000001, 0x00000000, 0x80000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF}; + + for (u64 pid = PRIVILEGED_PROCESS_MIN; pid <= PRIVILEGED_PROCESS_MAX; pid++) { + fsprUnregisterProgram(pid); + fsprRegisterProgram(pid, pid, FsStorageId_NandSystem, PRIVILEGED_FAH, sizeof(PRIVILEGED_FAH), PRIVILEGED_FAC, sizeof(PRIVILEGED_FAC)); + } +} + void __appInit(void) { Result rc; @@ -64,27 +79,33 @@ void __appInit(void) { fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_SM)); } - rc = fsInitialize(); - if (R_FAILED(rc)) { - fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_FS)); - } - - rc = lrInitialize(); + rc = fsprInitialize(); if (R_FAILED(rc)) { fatalSimple(0xCAFE << 4 | 1); } - rc = fsprInitialize(); - if (R_FAILED(rc)) { + /* This works around a bug with process permissions on < 4.0.0. */ + RegisterPrivilegedProcessesWithFs(); + + rc = smManagerAmsInitialize(); + if (R_SUCCEEDED(rc)) { + smManagerAmsEndInitialDefers(); + smManagerAmsExit(); + } else { fatalSimple(0xCAFE << 4 | 2); } - rc = ldrPmInitialize(); + rc = smManagerInitialize(); + if (R_FAILED(rc)) { + fatalSimple(0xCAFE << 4 | 3); + } + + rc = lrInitialize(); if (R_FAILED(rc)) { fatalSimple(0xCAFE << 4 | 4); } - rc = smManagerInitialize(); + rc = ldrPmInitialize(); if (R_FAILED(rc)) { fatalSimple(0xCAFE << 4 | 5); } @@ -94,6 +115,11 @@ void __appInit(void) { fatalSimple(0xCAFE << 4 | 6); } + rc = fsInitialize(); + if (R_FAILED(rc)) { + fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_FS)); + } + CheckAtmosphereVersion(); } diff --git a/stratosphere/pm/source/pm_resource_limits.cpp b/stratosphere/pm/source/pm_resource_limits.cpp index e0d729075..3d311d713 100644 --- a/stratosphere/pm/source/pm_resource_limits.cpp +++ b/stratosphere/pm/source/pm_resource_limits.cpp @@ -182,7 +182,8 @@ void ResourceLimitUtils::InitializeLimits() { /* Atmosphere: Allocate extra memory (24 MiB) to SYSTEM away from Applet. */ for (unsigned int i = 0; i < 6; i++) { g_memory_resource_limits[i][0] += ATMOSPHERE_EXTRA_SYSTEM_MEMORY_FOR_SYSMODULES; - g_memory_resource_limits[i][2] -= ATMOSPHERE_EXTRA_SYSTEM_MEMORY_FOR_SYSMODULES; + //g_memory_resource_limits[i][2] -= ATMOSPHERE_EXTRA_SYSTEM_MEMORY_FOR_SYSMODULES; + g_memory_resource_limits[i][1] -= ATMOSPHERE_EXTRA_SYSTEM_MEMORY_FOR_SYSMODULES; } /* Set resource limits. */ diff --git a/stratosphere/pm/source/smm_ams.c b/stratosphere/pm/source/smm_ams.c new file mode 100644 index 000000000..4437ba949 --- /dev/null +++ b/stratosphere/pm/source/smm_ams.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "smm_ams.h" + +static Service g_smManagerAmsSrv; +static u64 g_smManagerAmsRefcnt; + +Result smManagerAmsInitialize(void) { + atomicIncrement64(&g_smManagerAmsRefcnt); + + if (serviceIsActive(&g_smManagerAmsSrv)) + return 0; + + return smGetService(&g_smManagerAmsSrv, "sm:m"); +} + +void smManagerAmsExit(void) { + if (atomicDecrement64(&g_smManagerAmsRefcnt) == 0) + serviceClose(&g_smManagerAmsSrv); +} + +Result smManagerAmsEndInitialDefers(void) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(&g_smManagerAmsSrv, &c, sizeof(*raw)); + raw->magic = SFCI_MAGIC; + raw->cmd_id = 65000; + + + Result rc = serviceIpcDispatch(&g_smManagerAmsSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&g_smManagerAmsSrv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; + +} \ No newline at end of file diff --git a/stratosphere/pm/source/smm_ams.h b/stratosphere/pm/source/smm_ams.h new file mode 100644 index 000000000..6d6dd5ed0 --- /dev/null +++ b/stratosphere/pm/source/smm_ams.h @@ -0,0 +1,21 @@ +/** + * @file smm_ams.h + * @brief Service manager (sm:m) IPC wrapper for Atmosphere extensions. + * @author SciresM + * @copyright libnx Authors + */ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +Result smManagerAmsInitialize(void); +void smManagerAmsExit(void); + +Result smManagerAmsEndInitialDefers(void); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/stratosphere/sm/Makefile b/stratosphere/sm/Makefile index 3f6b0589d..dae1cae4a 100644 --- a/stratosphere/sm/Makefile +++ b/stratosphere/sm/Makefile @@ -34,7 +34,7 @@ ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE CFLAGS := -g -Wall -O2 -ffunction-sections \ $(ARCH) $(DEFINES) -CFLAGS += $(INCLUDE) -D__SWITCH__ -DSM_ENABLE_SMHAX -DSM_ENABLE_MITM -DSM_MINIMUM_SESSION_LIMIT=8 +CFLAGS += $(INCLUDE) -D__SWITCH__ -DSM_ENABLE_SMHAX -DSM_ENABLE_MITM -DSM_ENABLE_INIT_DEFERS -DSM_MINIMUM_SESSION_LIMIT=8 CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17 diff --git a/stratosphere/sm/source/sm_manager_service.cpp b/stratosphere/sm/source/sm_manager_service.cpp index 04190f7ed..03e0e5d95 100644 --- a/stratosphere/sm/source/sm_manager_service.cpp +++ b/stratosphere/sm/source/sm_manager_service.cpp @@ -28,6 +28,9 @@ Result ManagerService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_ case Manager_Cmd_UnregisterProcess: rc = WrapIpcCommandImpl<&ManagerService::unregister_process>(this, r, out_c, pointer_buffer, pointer_buffer_size); break; + case Manager_Cmd_AtmosphereEndInitDefers: + rc = WrapIpcCommandImpl<&ManagerService::end_init_defers>(this, r, out_c, pointer_buffer, pointer_buffer_size); + break; default: break; } @@ -47,3 +50,9 @@ std::tuple ManagerService::register_process(u64 pid, InBuffer acid_s std::tuple ManagerService::unregister_process(u64 pid) { return {Registration::UnregisterProcess(pid)}; } + +std::tuple ManagerService::end_init_defers() { + Registration::EndInitDefers(); + return {0}; +} + diff --git a/stratosphere/sm/source/sm_manager_service.hpp b/stratosphere/sm/source/sm_manager_service.hpp index f64b26a03..fd7a98647 100644 --- a/stratosphere/sm/source/sm_manager_service.hpp +++ b/stratosphere/sm/source/sm_manager_service.hpp @@ -20,7 +20,10 @@ enum ManagerServiceCmd { Manager_Cmd_RegisterProcess = 0, - Manager_Cmd_UnregisterProcess = 1 + Manager_Cmd_UnregisterProcess = 1, + + + Manager_Cmd_AtmosphereEndInitDefers = 65000, }; class ManagerService final : public IServiceObject { @@ -36,4 +39,5 @@ class ManagerService final : public IServiceObject { /* Actual commands. */ std::tuple register_process(u64 pid, InBuffer acid_sac, InBuffer aci0_sac); std::tuple unregister_process(u64 pid); + std::tuple end_init_defers(); }; diff --git a/stratosphere/sm/source/sm_registration.cpp b/stratosphere/sm/source/sm_registration.cpp index 63def66d9..46c6d62fc 100644 --- a/stratosphere/sm/source/sm_registration.cpp +++ b/stratosphere/sm/source/sm_registration.cpp @@ -26,6 +26,7 @@ static std::array g_servic static u64 g_initial_process_id_low = 0; static u64 g_initial_process_id_high = 0; static bool g_determined_initial_process_ids = false; +static bool g_end_init_defers = false; u64 GetServiceNameLength(u64 service) { u64 service_name_len = 0; @@ -36,6 +37,38 @@ u64 GetServiceNameLength(u64 service) { return service_name_len; } +/* Atmosphere extension utilities. */ +void Registration::EndInitDefers() { + g_end_init_defers = true; +} + +constexpr u64 EncodeNameConstant(const char *name) { + u64 service = 0; + for (unsigned int i = 0; i < sizeof(service); i++) { + if (name[i] == '\x00') { + break; + } + service |= ((u64)name[i]) << (8 * i); + } + return service; +} + +bool Registration::ShouldInitDefer(u64 service) { + /* Only enable if compile-time generated. */ +#ifndef SM_ENABLE_INIT_DEFERS + return false; +#endif + + if (g_end_init_defers) { + return false; + } + + /* This is a mechanism by which certain services will always be deferred until sm:m receives a special command. */ + /* This can be extended with more services as needed at a later date. */ + constexpr u64 FSP_SRV = EncodeNameConstant("fsp-srv"); + return service == FSP_SRV; +} + /* Utilities. */ Registration::Process *Registration::GetProcessForPid(u64 pid) { auto process_it = std::find_if(g_process_list.begin(), g_process_list.end(), member_equals_fn(&Process::pid, pid)); @@ -188,11 +221,13 @@ bool Registration::HasService(u64 service) { Result Registration::GetServiceHandle(u64 pid, u64 service, Handle *out) { Registration::Service *target_service = GetService(service); - if (target_service == NULL) { + if (target_service == NULL || ShouldInitDefer(service)) { /* Note: This defers the result until later. */ return RESULT_DEFER_SESSION; } + /* */ + *out = 0; Result rc; if (target_service->mitm_pid == 0 || target_service->mitm_pid == pid) { diff --git a/stratosphere/sm/source/sm_registration.hpp b/stratosphere/sm/source/sm_registration.hpp index 630a72181..9a30652bc 100644 --- a/stratosphere/sm/source/sm_registration.hpp +++ b/stratosphere/sm/source/sm_registration.hpp @@ -46,6 +46,9 @@ class Registration { }; /* Utilities. */ + static void EndInitDefers(); + static bool ShouldInitDefer(u64 service); + static Registration::Process *GetProcessForPid(u64 pid); static Registration::Process *GetFreeProcess(); static Registration::Service *GetService(u64 service); diff --git a/stratosphere/sm/source/sm_user_service.cpp b/stratosphere/sm/source/sm_user_service.cpp index 0f8927ed8..00cf6f971 100644 --- a/stratosphere/sm/source/sm_user_service.cpp +++ b/stratosphere/sm/source/sm_user_service.cpp @@ -53,7 +53,7 @@ Result UserService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, Result UserService::handle_deferred() { /* If we're deferred, GetService failed. */ - return WrapDeferredIpcCommandImpl<&UserService::deferred_get_service>(this, this->deferred_service);; + return WrapDeferredIpcCommandImpl<&UserService::deferred_get_service>(this, this->deferred_service); }