From 4f455dacf4d8ed77db100c29994642303ca1e64a Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 21 Oct 2019 02:45:52 -0700 Subject: [PATCH] sf: implement mitm forwarding + domains. --- .../stratosphere/results/sf_results.hpp | 22 +- .../sf/cmif/sf_cmif_domain_api.hpp | 2 +- .../sf/cmif/sf_cmif_domain_manager.hpp | 9 +- .../sf/cmif/sf_cmif_domain_service_object.hpp | 62 ++++- .../cmif/sf_cmif_server_message_processor.hpp | 48 +++- .../sf/cmif/sf_cmif_service_dispatch.hpp | 2 + .../sf_hipc_server_domain_session_manager.hpp | 8 + .../hipc/sf_hipc_server_session_manager.hpp | 8 + .../sf/impl/sf_impl_command_serialization.hpp | 61 +++-- .../source/sf/cmif/sf_cmif_domain_manager.cpp | 22 +- .../sf/cmif/sf_cmif_domain_service_object.cpp | 219 ++++++++++++++++++ .../sf/cmif/sf_cmif_service_dispatch.cpp | 13 +- .../sf_hipc_server_domain_session_manager.cpp | 46 +++- .../hipc/sf_hipc_server_session_manager.cpp | 55 +++++ 14 files changed, 509 insertions(+), 68 deletions(-) create mode 100644 stratosphere/libstratosphere/source/sf/cmif/sf_cmif_domain_service_object.cpp diff --git a/stratosphere/libstratosphere/include/stratosphere/results/sf_results.hpp b/stratosphere/libstratosphere/include/stratosphere/results/sf_results.hpp index a68a29572..9cf251cfe 100644 --- a/stratosphere/libstratosphere/include/stratosphere/results/sf_results.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/results/sf_results.hpp @@ -19,18 +19,20 @@ static constexpr u32 Module_ServiceFramework = 10; -static constexpr Result ResultServiceFrameworkNotSupported = MAKERESULT(Module_ServiceFramework, 1); -static constexpr Result ResultServiceFrameworkPreconditionViolation = MAKERESULT(Module_ServiceFramework, 3); +static constexpr Result ResultServiceFrameworkNotSupported = MAKERESULT(Module_ServiceFramework, 1); +static constexpr Result ResultServiceFrameworkPreconditionViolation = MAKERESULT(Module_ServiceFramework, 3); -static constexpr Result ResultServiceFrameworkInvalidCmifHeaderSize = MAKERESULT(Module_ServiceFramework, 202); -static constexpr Result ResultServiceFrameworkInvalidCmifInHeader = MAKERESULT(Module_ServiceFramework, 211); -static constexpr Result ResultServiceFrameworkUnknownCmifCommandId = MAKERESULT(Module_ServiceFramework, 221); -static constexpr Result ResultServiceFrameworkInvalidCmifOutRawSize = MAKERESULT(Module_ServiceFramework, 232); +static constexpr Result ResultServiceFrameworkInvalidCmifHeaderSize = MAKERESULT(Module_ServiceFramework, 202); +static constexpr Result ResultServiceFrameworkInvalidCmifInHeader = MAKERESULT(Module_ServiceFramework, 211); +static constexpr Result ResultServiceFrameworkUnknownCmifCommandId = MAKERESULT(Module_ServiceFramework, 221); +static constexpr Result ResultServiceFrameworkInvalidCmifOutRawSize = MAKERESULT(Module_ServiceFramework, 232); +static constexpr Result ResultServiceFrameworkInvalidCmifNumInObjects = MAKERESULT(Module_ServiceFramework, 235); +static constexpr Result ResultServiceFrameworkInvalidCmifNumOutObjects = MAKERESULT(Module_ServiceFramework, 236); -static constexpr Result ResultServiceFrameworkTargetNotFound = MAKERESULT(Module_ServiceFramework, 261); +static constexpr Result ResultServiceFrameworkTargetNotFound = MAKERESULT(Module_ServiceFramework, 261); -static constexpr Result ResultServiceFrameworkOutOfDomainEntries = MAKERESULT(Module_ServiceFramework, 301); +static constexpr Result ResultServiceFrameworkOutOfDomainEntries = MAKERESULT(Module_ServiceFramework, 301); -static constexpr Result ResultServiceFrameworkRequestDeferred = MAKERESULT(Module_ServiceFramework, 811); -static constexpr Result ResultServiceFrameworkRequestDeferredByUser = MAKERESULT(Module_ServiceFramework, 812); +static constexpr Result ResultServiceFrameworkRequestDeferred = MAKERESULT(Module_ServiceFramework, 811); +static constexpr Result ResultServiceFrameworkRequestDeferredByUser = MAKERESULT(Module_ServiceFramework, 812); diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_api.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_api.hpp index d45e66c87..68ae2ed99 100644 --- a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_api.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_api.hpp @@ -57,7 +57,7 @@ namespace sts::sf::cmif { class ServerDomainBase { public: virtual Result ReserveIds(DomainObjectId *out_ids, size_t count) = 0; - virtual Result AlterReservedIds(const DomainObjectId *old_reserved_ids, const DomainObjectId *new_reserved_ids, size_t count) = 0; + virtual void ReserveSpecificIds(const DomainObjectId *ids, size_t count) = 0; virtual void UnreserveIds(const DomainObjectId *ids, size_t count) = 0; virtual void RegisterObject(DomainObjectId id, ServiceObjectHolder &&obj) = 0; diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp index 85a6e008e..90beb3a28 100644 --- a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp @@ -56,7 +56,7 @@ namespace sts::sf::cmif { } virtual Result ReserveIds(DomainObjectId *out_ids, size_t count) override final; - virtual Result AlterReservedIds(const DomainObjectId *old_reserved_ids, const DomainObjectId *new_reserved_ids, size_t count) override final; + virtual void ReserveSpecificIds(const DomainObjectId *ids, size_t count) override final; virtual void UnreserveIds(const DomainObjectId *ids, size_t count) override final; virtual void RegisterObject(DomainObjectId id, ServiceObjectHolder &&obj) override final; @@ -81,7 +81,7 @@ namespace sts::sf::cmif { Entry *AllocateEntry(); void FreeEntry(Entry *); - void ReallocateEntries(const DomainObjectId *old_reserved_ids, const DomainObjectId *new_reserved_ids, size_t count); + void AllocateSpecificEntries(const DomainObjectId *ids, size_t count); inline DomainObjectId GetId(Entry *e) { const size_t index = e - this->entries; @@ -116,6 +116,11 @@ namespace sts::sf::cmif { } return new (storage) Domain(this); } + + inline void FreeDomainServiceObject(DomainServiceObject *object) { + static_cast(object)->~Domain(); + this->FreeDomain(object); + } }; diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_service_object.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_service_object.hpp index 31b0657a9..ad11979ed 100644 --- a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_service_object.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_service_object.hpp @@ -24,8 +24,10 @@ namespace sts::sf::cmif { class DomainServiceObjectDispatchTable : public impl::ServiceDispatchTableBase { private: Result ProcessMessageImpl(ServiceDispatchContext &ctx, ServerDomainBase *domain, const cmif::PointerAndSize &in_raw_data) const; + Result ProcessMessageForMitmImpl(ServiceDispatchContext &ctx, ServerDomainBase *domain, const cmif::PointerAndSize &in_raw_data) const; public: Result ProcessMessage(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const; + Result ProcessMessageForMitm(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const; }; @@ -33,6 +35,30 @@ namespace sts::sf::cmif { private: ServerMessageProcessor *impl_processor; ServerDomainBase *domain; + DomainObjectId *in_object_ids; + DomainObjectId *out_object_ids; + size_t num_in_objects; + ServerMessageRuntimeMetadata impl_metadata; + public: + DomainServiceObjectProcessor(ServerDomainBase *d, DomainObjectId *in_obj_ids, size_t num_in_objs) : domain(d), in_object_ids(in_obj_ids), num_in_objects(num_in_objs) { + STS_ASSERT(this->domain != nullptr); + STS_ASSERT(this->in_object_ids != nullptr); + this->impl_processor = nullptr; + this->out_object_ids = nullptr; + this->impl_metadata = {}; + } + + constexpr size_t GetInObjectCount() const { + return this->num_in_objects; + } + + constexpr size_t GetOutObjectCount() const { + return this->impl_metadata.GetOutObjectCount(); + } + + constexpr size_t GetImplOutDataTotalSize() const { + return this->impl_metadata.GetOutDataSize() + this->impl_metadata.GetOutHeadersSize(); + } public: /* Used to enabled templated message processors. */ virtual void SetImplementationProcessor(ServerMessageProcessor *impl) override final { @@ -41,16 +67,32 @@ namespace sts::sf::cmif { } else { this->impl_processor->SetImplementationProcessor(impl); } + + this->impl_metadata = this->impl_processor->GetRuntimeMetadata(); } - virtual Result PrepareForProcess(const ServiceDispatchContext &ctx, size_t &headers_size) const override final; + virtual const ServerMessageRuntimeMetadata GetRuntimeMetadata() const override final { + const auto runtime_metadata = this->impl_processor->GetRuntimeMetadata(); + + return ServerMessageRuntimeMetadata { + .in_data_size = static_cast(runtime_metadata.GetInDataSize() + runtime_metadata.GetInObjectCount() * sizeof(DomainObjectId)), + .out_data_size = static_cast(runtime_metadata.GetOutDataSize() + runtime_metadata.GetOutObjectCount() * sizeof(DomainObjectId)), + .in_headers_size = static_cast(runtime_metadata.GetInHeadersSize() + sizeof(CmifDomainInHeader)), + .out_headers_size = static_cast(runtime_metadata.GetOutHeadersSize() + sizeof(CmifDomainOutHeader)), + .in_object_count = 0, + .out_object_count = 0, + }; + } + + virtual Result PrepareForProcess(const ServiceDispatchContext &ctx, const ServerMessageRuntimeMetadata runtime_metadata) const override final; virtual Result GetInObjects(ServiceObjectHolder *in_objects) const override final; - virtual HipcRequest PrepareForReply(const cmif::ServiceDispatchContext &ctx, PointerAndSize &out_raw_data, const size_t headers_size, size_t &num_out_object_handles) override final; - virtual void PrepareForErrorReply(const cmif::ServiceDispatchContext &ctx, PointerAndSize &out_raw_data, const size_t headers_size) override final; + virtual HipcRequest PrepareForReply(const cmif::ServiceDispatchContext &ctx, PointerAndSize &out_raw_data, const ServerMessageRuntimeMetadata runtime_metadata) override final; + virtual void PrepareForErrorReply(const cmif::ServiceDispatchContext &ctx, PointerAndSize &out_raw_data, const ServerMessageRuntimeMetadata runtime_metadata) override final; virtual void SetOutObjects(const cmif::ServiceDispatchContext &ctx, const HipcRequest &response, ServiceObjectHolder *out_objects, DomainObjectId *ids) override final; }; class DomainServiceObject : public IServiceObject, public ServerDomainBase { + friend class DomainServiceObjectDispatchTable; public: static constexpr inline DomainServiceObjectDispatchTable s_CmifServiceDispatchTable{}; private: @@ -59,6 +101,8 @@ namespace sts::sf::cmif { /* TODO: Implement to use domain object processor. */ }; + class MitmDomainServiceObject : public DomainServiceObject{}; + template<> struct ServiceDispatchTraits { static_assert(std::is_base_of::value, "DomainServiceObject must derive from sf::IServiceObject"); @@ -71,4 +115,16 @@ namespace sts::sf::cmif { static constexpr inline ServiceDispatchMeta Meta{&DomainServiceObject::s_CmifServiceDispatchTable, ProcessHandlerImpl}; }; + template<> + struct ServiceDispatchTraits { + static_assert(std::is_base_of::value, "MitmDomainServiceObject must derive from DomainServiceObject"); + using ProcessHandlerType = decltype(ServiceDispatchMeta::ProcessHandler); + + using DispatchTableType = DomainServiceObjectDispatchTable; + static constexpr ProcessHandlerType ProcessHandlerImpl = &impl::ServiceDispatchTableBase::ProcessMessageForMitm; + + static constexpr inline ServiceDispatchMeta Meta{&DomainServiceObject::s_CmifServiceDispatchTable, ProcessHandlerImpl}; + }; + + } diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_server_message_processor.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_server_message_processor.hpp index e77b06118..547551754 100644 --- a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_server_message_processor.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_server_message_processor.hpp @@ -25,15 +25,57 @@ namespace sts::sf::cmif { class ServiceObjectHolder; struct DomainObjectId; + /* This is needed for non-templated domain message processing. */ + struct ServerMessageRuntimeMetadata { + u16 in_data_size; + u16 out_data_size; + u8 in_headers_size; + u8 out_headers_size; + u8 in_object_count; + u8 out_object_count; + + constexpr size_t GetInDataSize() const { + return size_t(this->in_data_size); + } + + constexpr size_t GetOutDataSize() const { + return size_t(this->out_data_size); + } + + constexpr size_t GetInHeadersSize() const { + return size_t(this->in_headers_size); + } + + constexpr size_t GetOutHeadersSize() const { + return size_t(this->out_headers_size); + } + + constexpr size_t GetInObjectCount() const { + return size_t(this->in_object_count); + } + + constexpr size_t GetOutObjectCount() const { + return size_t(this->out_object_count); + } + + constexpr size_t GetUnfixedOutPointerSizeOffset() const { + return this->GetInDataSize() + this->GetInHeadersSize() + 0x10 /* padding. */; + } + }; + + static_assert(std::is_pod::value, "std::is_pod::value"); + static_assert(sizeof(ServerMessageRuntimeMetadata) == sizeof(u64), "sizeof(ServerMessageRuntimeMetadata)"); + class ServerMessageProcessor { public: /* Used to enabled templated message processors. */ virtual void SetImplementationProcessor(ServerMessageProcessor *impl) { /* ... */ } + virtual const ServerMessageRuntimeMetadata GetRuntimeMetadata() const = 0; - virtual Result PrepareForProcess(const ServiceDispatchContext &ctx, size_t &headers_size) const = 0; + virtual Result PrepareForProcess(const ServiceDispatchContext &ctx, const ServerMessageRuntimeMetadata runtime_metadata) const = 0; virtual Result GetInObjects(ServiceObjectHolder *in_objects) const = 0; - virtual HipcRequest PrepareForReply(const cmif::ServiceDispatchContext &ctx, PointerAndSize &out_raw_data, const size_t headers_size, size_t &num_out_object_handles) = 0; - virtual void PrepareForErrorReply(const cmif::ServiceDispatchContext &ctx, PointerAndSize &out_raw_data, const size_t headers_size) = 0; + virtual HipcRequest PrepareForReply(const cmif::ServiceDispatchContext &ctx, PointerAndSize &out_raw_data, const ServerMessageRuntimeMetadata runtime_metadata) = 0; + virtual void PrepareForErrorReply(const cmif::ServiceDispatchContext &ctx, PointerAndSize &out_raw_data, const ServerMessageRuntimeMetadata runtime_metadata) = 0; virtual void SetOutObjects(const cmif::ServiceDispatchContext &ctx, const HipcRequest &response, ServiceObjectHolder *out_objects, DomainObjectId *ids) = 0; }; } diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp index fdc493e38..511aa125a 100644 --- a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp @@ -22,6 +22,7 @@ namespace sts::sf::hipc { class ServerSessionManager; + class ServerSession; } @@ -37,6 +38,7 @@ namespace sts::sf::cmif { struct ServiceDispatchContext { sf::IServiceObject *srv_obj; hipc::ServerSessionManager *manager; + hipc::ServerSession *session; ServerMessageProcessor *processor; HandlesToClose *handles_to_close; const PointerAndSize pointer_buffer; diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_domain_session_manager.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_domain_session_manager.hpp index f21f941ff..a8d6b74ef 100644 --- a/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_domain_session_manager.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_domain_session_manager.hpp @@ -28,6 +28,14 @@ namespace sts::sf::hipc { virtual Result DispatchManagerRequest(ServerSession *session, const cmif::PointerAndSize &in_message, const cmif::PointerAndSize &out_message) override final; public: ServerDomainSessionManager(DomainEntryStorage *entry_storage, size_t entry_count) : ServerDomainManager(entry_storage, entry_count) { /* ... */ } + + inline cmif::DomainServiceObject *AllocateDomainServiceObject() { + return cmif::ServerDomainManager::AllocateDomainServiceObject(); + } + + inline void FreeDomainServiceObject(cmif::DomainServiceObject *object) { + cmif::ServerDomainManager::FreeDomainServiceObject(object); + } }; } diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_session_manager.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_session_manager.hpp index 1be5e91a2..05b3f581f 100644 --- a/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_session_manager.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_session_manager.hpp @@ -21,6 +21,12 @@ #include "../cmif/sf_cmif_service_object_holder.hpp" #include "sf_hipc_api.hpp" +namespace sts::sf::cmif { + + struct ServiceDispatchContext; + +} + namespace sts::sf::hipc { class ServerSessionManager; @@ -65,6 +71,8 @@ namespace sts::sf::hipc { bool IsMitmSession() const { return this->forward_service != nullptr; } + + Result ForwardRequest(const cmif::ServiceDispatchContext &ctx) const; }; class ServerSessionManager { diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp index b9d3dc207..37fcc91c3 100644 --- a/stratosphere/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp @@ -442,8 +442,7 @@ namespace sts::sf::impl { /* In/Out data marshalling. */ static constexpr std::array InDataOffsets = RawDataOffsetCalculator::Offsets; - static constexpr size_t InDataRawUnfixedOutPointerSizeOffset = util::AlignUp(InDataOffsets[NumInDatas] + 0x10, alignof(u16)); - static constexpr size_t InDataSize = util::AlignUp(util::AlignUp(InDataOffsets[NumInDatas], alignof(u16)) + sizeof(u16) * NumUnfixedSizeOutHipcPointerBuffers, alignof(u32)); + static constexpr size_t InDataSize = util::AlignUp(InDataOffsets[NumInDatas], alignof(u16)); static constexpr std::array OutDataOffsets = RawDataOffsetCalculator::Offsets; static constexpr size_t OutDataSize = util::AlignUp(OutDataOffsets[NumOutDatas], alignof(u32)); @@ -464,6 +463,16 @@ namespace sts::sf::impl { static_assert(NumInMoveHandles + NumInCopyHandles == NumInHandles, "NumInMoveHandles + NumInCopyHandles == NumInHandles"); static_assert(NumOutMoveHandles + NumOutCopyHandles == NumOutHandles, "NumOutMoveHandles + NumOutCopyHandles == NumOutHandles"); + /* Used by server message processor at runtime. */ + static constexpr inline const cmif::ServerMessageRuntimeMetadata RuntimeMetadata = cmif::ServerMessageRuntimeMetadata{ + .in_data_size = InDataSize, + .out_data_size = OutDataSize, + .in_headers_size = sizeof(CmifInHeader), + .out_headers_size = sizeof(CmifOutHeader), + .in_object_count = NumInObjects, + .out_object_count = NumOutObjects, + }; + /* Construction of argument serialization structs. */ private: template @@ -686,7 +695,11 @@ namespace sts::sf::impl { template struct HipcCommandProcessor : public sf::cmif::ServerMessageProcessor { public: - virtual Result PrepareForProcess(const cmif::ServiceDispatchContext &ctx, size_t &headers_size) const override final { + virtual const cmif::ServerMessageRuntimeMetadata GetRuntimeMetadata() const override final { + return CommandMeta::RuntimeMetadata; + } + + virtual Result PrepareForProcess(const cmif::ServiceDispatchContext &ctx, const cmif::ServerMessageRuntimeMetadata runtime_metadata) const override final { const auto &meta = ctx.request.meta; bool is_request_valid = true; is_request_valid &= meta.send_pid == CommandMeta::HasInProcessIdHolder; @@ -699,30 +712,33 @@ namespace sts::sf::impl { is_request_valid &= meta.num_move_handles == CommandMeta::NumInMoveHandles; const size_t meta_raw_size = meta.num_data_words * sizeof(u32); - is_request_valid &= meta_raw_size >= CommandMeta::InDataSize + 0x10 /* padding */ + headers_size /* headers */; + const size_t command_raw_size = util::AlignUp(runtime_metadata.GetUnfixedOutPointerSizeOffset() + (CommandMeta::NumUnfixedSizeOutHipcPointerBuffers * sizeof(u16)), alignof(u32)); + is_request_valid &= meta_raw_size >= command_raw_size; R_UNLESS(is_request_valid, ResultHipcInvalidRequest); return ResultSuccess; } - virtual HipcRequest PrepareForReply(const cmif::ServiceDispatchContext &ctx, cmif::PointerAndSize &out_raw_data, const size_t headers_size, size_t &num_out_object_handles) override final { + virtual HipcRequest PrepareForReply(const cmif::ServiceDispatchContext &ctx, cmif::PointerAndSize &out_raw_data, const cmif::ServerMessageRuntimeMetadata runtime_metadata) override final { + const size_t raw_size = runtime_metadata.GetOutDataSize() + runtime_metadata.GetOutHeadersSize(); const auto response = hipcMakeRequestInline(ctx.out_message_buffer.GetPointer(), .type = CmifCommandType_Invalid, /* Really response */ .num_send_statics = CommandMeta::NumOutHipcPointerBuffers, - .num_data_words = static_cast((util::AlignUp(CommandMeta::OutDataSize + headers_size, 0x4) + 0x10 /* padding */) / sizeof(u32)), + .num_data_words = static_cast((util::AlignUp(raw_size, 0x4) + 0x10 /* padding */) / sizeof(u32)), .num_copy_handles = CommandMeta::NumOutCopyHandles, - .num_move_handles = static_cast(CommandMeta::NumOutMoveHandles + num_out_object_handles), + .num_move_handles = static_cast(CommandMeta::NumOutMoveHandles + runtime_metadata.GetOutObjectCount()), ); - out_raw_data = cmif::PointerAndSize(util::AlignUp(reinterpret_cast(response.data_words), 0x10), CommandMeta::OutDataSize + headers_size); + out_raw_data = cmif::PointerAndSize(util::AlignUp(reinterpret_cast(response.data_words), 0x10), raw_size); return response; } - virtual void PrepareForErrorReply(const cmif::ServiceDispatchContext &ctx, cmif::PointerAndSize &out_raw_data, const size_t headers_size) override final { + virtual void PrepareForErrorReply(const cmif::ServiceDispatchContext &ctx, cmif::PointerAndSize &out_raw_data, const cmif::ServerMessageRuntimeMetadata runtime_metadata) override final { + const size_t raw_size = runtime_metadata.GetOutHeadersSize(); const auto response = hipcMakeRequestInline(ctx.out_message_buffer.GetPointer(), .type = CmifCommandType_Invalid, /* Really response */ - .num_data_words = static_cast((util::AlignUp(headers_size, 0x4) + 0x10 /* padding */) / sizeof(u32)), + .num_data_words = static_cast((util::AlignUp(raw_size, 0x4) + 0x10 /* padding */) / sizeof(u32)), ); - out_raw_data = cmif::PointerAndSize(util::AlignUp(reinterpret_cast(response.data_words), 0x10), headers_size); + out_raw_data = cmif::PointerAndSize(util::AlignUp(reinterpret_cast(response.data_words), 0x10), raw_size); } virtual Result GetInObjects(cmif::ServiceObjectHolder *in_objects) const override final { @@ -789,7 +805,7 @@ namespace sts::sf::impl { }(); template> - NX_CONSTEXPR void ProcessBufferImpl(const cmif::ServiceDispatchContext &ctx, cmif::PointerAndSize &buffer, bool &is_buffer_map_alias, bool &map_alias_buffers_valid, size_t &pointer_buffer_head, size_t &pointer_buffer_tail, size_t in_headers_size) { + NX_CONSTEXPR void ProcessBufferImpl(const cmif::ServiceDispatchContext &ctx, cmif::PointerAndSize &buffer, bool &is_buffer_map_alias, bool &map_alias_buffers_valid, size_t &pointer_buffer_head, size_t &pointer_buffer_tail, const cmif::ServerMessageRuntimeMetadata runtime_metadata) { static_assert(Index != std::numeric_limits::max(), "Invalid Index From Buffer Index"); constexpr auto Info = CommandMeta::ArgumentSerializationInfos[Index]; constexpr auto Attributes = CommandMeta::BufferAttributes[BufferIndex]; @@ -824,7 +840,7 @@ namespace sts::sf::impl { pointer_buffer_head = util::AlignDown(pointer_buffer_head - size, 0x10); buffer = cmif::PointerAndSize(pointer_buffer_head, size); } else { - const u16 *recv_pointer_sizes = reinterpret_cast(reinterpret_cast(ctx.request.data.data_words) + in_headers_size + CommandMeta::InDataRawUnfixedOutPointerSizeOffset); + const u16 *recv_pointer_sizes = reinterpret_cast(reinterpret_cast(ctx.request.data.data_words) + runtime_metadata.GetUnfixedOutPointerSizeOffset()); const size_t size = size_t(recv_pointer_sizes[Info.unfixed_recv_pointer_index]); pointer_buffer_head = util::AlignDown(pointer_buffer_head - size, 0x10); buffer = cmif::PointerAndSize(pointer_buffer_head, size); @@ -860,7 +876,7 @@ namespace sts::sf::impl { pointer_buffer_head = util::AlignDown(pointer_buffer_head - size, 0x10); buffer = cmif::PointerAndSize(pointer_buffer_head, size); } else { - const u16 *recv_pointer_sizes = reinterpret_cast(reinterpret_cast(ctx.request.data.data_words) + CommandMeta::InDataRawUnfixedOutPointerSizeOffset); + const u16 *recv_pointer_sizes = reinterpret_cast(reinterpret_cast(ctx.request.data.data_words) + runtime_metadata.GetUnfixedOutPointerSizeOffset()); const size_t size = size_t(recv_pointer_sizes[Info.unfixed_recv_pointer_index]); pointer_buffer_head = util::AlignDown(pointer_buffer_head - size, 0x10); buffer = cmif::PointerAndSize(pointer_buffer_head, size); @@ -894,11 +910,11 @@ namespace sts::sf::impl { } } public: - NX_CONSTEXPR Result ProcessBuffers(const cmif::ServiceDispatchContext &ctx, BufferArrayType &buffers, std::array &is_buffer_map_alias, size_t in_headers_size) { + NX_CONSTEXPR Result ProcessBuffers(const cmif::ServiceDispatchContext &ctx, BufferArrayType &buffers, std::array &is_buffer_map_alias, const cmif::ServerMessageRuntimeMetadata runtime_metadata) { bool map_alias_buffers_valid = true; size_t pointer_buffer_tail = ctx.pointer_buffer.GetAddress(); size_t pointer_buffer_head = pointer_buffer_tail + ctx.pointer_buffer.GetSize(); - #define _SF_IMPL_PROCESSOR_PROCESS_BUFFER_IMPL(n) do { if constexpr (CommandMeta::NumBuffers > n) { ProcessBufferImpl(ctx, buffers[n], is_buffer_map_alias[n], map_alias_buffers_valid, pointer_buffer_head, pointer_buffer_tail, in_headers_size); } } while (0) + #define _SF_IMPL_PROCESSOR_PROCESS_BUFFER_IMPL(n) do { if constexpr (CommandMeta::NumBuffers > n) { ProcessBufferImpl(ctx, buffers[n], is_buffer_map_alias[n], map_alias_buffers_valid, pointer_buffer_head, pointer_buffer_tail, runtime_metadata); } } while (0) _SF_IMPL_PROCESSOR_PROCESS_BUFFER_IMPL(0); _SF_IMPL_PROCESSOR_PROCESS_BUFFER_IMPL(1); _SF_IMPL_PROCESSOR_PROCESS_BUFFER_IMPL(2); @@ -1032,8 +1048,8 @@ namespace sts::sf::impl { } /* Validate the metadata has the expected counts. */ - size_t in_headers_size = sizeof(CmifInHeader); - R_TRY(ctx.processor->PrepareForProcess(ctx, in_headers_size)); + const auto runtime_metadata = ctx.processor->GetRuntimeMetadata(); + R_TRY(ctx.processor->PrepareForProcess(ctx, runtime_metadata)); /* Storage for output. */ BufferArrayType buffers; @@ -1043,7 +1059,7 @@ namespace sts::sf::impl { InOutObjectHolderType in_out_objects_holder; /* Process buffers. */ - R_TRY(ImplProcessorType::ProcessBuffers(ctx, buffers, is_buffer_map_alias, in_headers_size)); + R_TRY(ImplProcessorType::ProcessBuffers(ctx, buffers, is_buffer_map_alias, runtime_metadata)); /* Process input/output objects. */ R_TRY(in_out_objects_holder.GetInObjects(ctx.processor)); @@ -1062,7 +1078,7 @@ namespace sts::sf::impl { if constexpr (CommandMeta::ReturnsResult) { R_TRY_CLEANUP(std::apply([=](auto&&... args) { return (this_ptr->*ServiceCommandImpl)(args...); }, args_tuple), { cmif::PointerAndSize out_raw_data; - ctx.processor->PrepareForErrorReply(ctx, out_raw_data, sizeof(CmifOutHeader)); + ctx.processor->PrepareForErrorReply(ctx, out_raw_data, runtime_metadata); R_TRY(GetCmifOutHeaderPointer(out_header_ptr, out_raw_data)); }); } else { @@ -1072,8 +1088,7 @@ namespace sts::sf::impl { /* Encode. */ cmif::PointerAndSize out_raw_data; - size_t num_out_object_handles = CommandMeta::NumOutObjects; - const auto response = ctx.processor->PrepareForReply(ctx, out_raw_data, sizeof(CmifOutHeader), num_out_object_handles); + const auto response = ctx.processor->PrepareForReply(ctx, out_raw_data, runtime_metadata); R_TRY(GetCmifOutHeaderPointer(out_header_ptr, out_raw_data)); /* Copy raw data output struct. */ @@ -1084,7 +1099,7 @@ namespace sts::sf::impl { ImplProcessorType::SetOutBuffers(response, buffers, is_buffer_map_alias); /* Set out handles. */ - out_handles_holder.CopyTo(response, num_out_object_handles); + out_handles_holder.CopyTo(response, runtime_metadata.GetOutObjectCount()); /* Set output objects. */ in_out_objects_holder.SetOutObjects(ctx, response); diff --git a/stratosphere/libstratosphere/source/sf/cmif/sf_cmif_domain_manager.cpp b/stratosphere/libstratosphere/source/sf/cmif/sf_cmif_domain_manager.cpp index f59d00424..4ccc5ac93 100644 --- a/stratosphere/libstratosphere/source/sf/cmif/sf_cmif_domain_manager.cpp +++ b/stratosphere/libstratosphere/source/sf/cmif/sf_cmif_domain_manager.cpp @@ -41,9 +41,8 @@ namespace sts::sf::cmif { return ResultSuccess; } - Result ServerDomainManager::Domain::AlterReservedIds(const DomainObjectId *old_reserved_ids, const DomainObjectId *new_reserved_ids, size_t count) { - this->manager->entry_manager.ReallocateEntries(old_reserved_ids, new_reserved_ids, count); - return ResultSuccess; + void ServerDomainManager::Domain::ReserveSpecificIds(const DomainObjectId *ids, size_t count) { + this->manager->entry_manager.AllocateSpecificEntries(ids, count); } void ServerDomainManager::Domain::UnreserveIds(const DomainObjectId *ids, size_t count) { @@ -132,27 +131,16 @@ namespace sts::sf::cmif { this->free_list.push_front(*entry); } - void ServerDomainManager::EntryManager::ReallocateEntries(const DomainObjectId *old_reserved_ids, const DomainObjectId *new_reserved_ids, size_t count) { + void ServerDomainManager::EntryManager::AllocateSpecificEntries(const DomainObjectId *ids, size_t count) { std::scoped_lock lk(this->lock); - /* Free old ids. */ + /* Allocate new IDs. */ for (size_t i = 0; i < count; i++) { - const auto id = old_reserved_ids[i]; + const auto id = ids[i]; Entry *entry = this->GetEntry(id); if (id != InvalidDomainObjectId) { STS_ASSERT(entry != nullptr); STS_ASSERT(entry->owner == nullptr); - STS_ASSERT(!entry->object); - this->free_list.push_front(*entry); - } - } - - /* Allocate new IDs. */ - for (size_t i = 0; i < count; i++) { - const auto id = old_reserved_ids[i]; - Entry *entry = this->GetEntry(id); - if (id != InvalidDomainObjectId) { - STS_ASSERT(entry != nullptr); this->free_list.erase(this->free_list.iterator_to(*entry)); } } diff --git a/stratosphere/libstratosphere/source/sf/cmif/sf_cmif_domain_service_object.cpp b/stratosphere/libstratosphere/source/sf/cmif/sf_cmif_domain_service_object.cpp new file mode 100644 index 000000000..3655b5d0e --- /dev/null +++ b/stratosphere/libstratosphere/source/sf/cmif/sf_cmif_domain_service_object.cpp @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2018-2019 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 + +namespace sts::sf::cmif { + + Result DomainServiceObjectDispatchTable::ProcessMessage(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const { + return this->ProcessMessageImpl(ctx, static_cast(ctx.srv_obj)->GetServerDomain(), in_raw_data); + } + + Result DomainServiceObjectDispatchTable::ProcessMessageForMitm(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const { + return this->ProcessMessageForMitmImpl(ctx, static_cast(ctx.srv_obj)->GetServerDomain(), in_raw_data); + } + + Result DomainServiceObjectDispatchTable::ProcessMessageImpl(ServiceDispatchContext &ctx, ServerDomainBase *domain, const cmif::PointerAndSize &in_raw_data) const { + const CmifDomainInHeader *in_header = reinterpret_cast(in_raw_data.GetPointer()); + R_UNLESS(in_raw_data.GetSize() >= sizeof(*in_header), ResultServiceFrameworkInvalidCmifHeaderSize); + const cmif::PointerAndSize in_domain_raw_data = cmif::PointerAndSize(in_raw_data.GetAddress() + sizeof(*in_header), in_raw_data.GetSize() - sizeof(*in_header)); + + const DomainObjectId target_object_id = DomainObjectId{in_header->object_id}; + switch (in_header->type) { + case CmifDomainRequestType_SendMessage: + { + auto target_object = domain->GetObject(target_object_id); + R_UNLESS(static_cast(target_object), ResultServiceFrameworkTargetNotFound); + R_UNLESS(in_header->data_size + in_header->num_in_objects * sizeof(DomainObjectId) <= in_domain_raw_data.GetSize(), ResultServiceFrameworkInvalidCmifHeaderSize); + const cmif::PointerAndSize in_message_raw_data = cmif::PointerAndSize(in_domain_raw_data.GetAddress(), in_header->data_size); + DomainObjectId in_object_ids[8]; + R_UNLESS(in_header->num_in_objects <= util::size(in_object_ids), ResultServiceFrameworkInvalidCmifNumInObjects); + std::memcpy(in_object_ids, reinterpret_cast(in_message_raw_data.GetAddress() + in_message_raw_data.GetSize()), sizeof(DomainObjectId) * in_header->num_in_objects); + DomainServiceObjectProcessor domain_processor(domain, in_object_ids, in_header->num_in_objects); + if (ctx.processor == nullptr) { + ctx.processor = &domain_processor; + } else { + ctx.processor->SetImplementationProcessor(&domain_processor); + } + return target_object.ProcessMessage(ctx, in_message_raw_data); + } + case CmifDomainRequestType_Close: + /* TODO: N doesn't error check here. Should we? */ + domain->UnregisterObject(target_object_id); + return ResultSuccess; + default: + return ResultServiceFrameworkInvalidCmifInHeader; + } + } + + Result DomainServiceObjectDispatchTable::ProcessMessageForMitmImpl(ServiceDispatchContext &ctx, ServerDomainBase *domain, const cmif::PointerAndSize &in_raw_data) const { + const CmifDomainInHeader *in_header = reinterpret_cast(in_raw_data.GetPointer()); + R_UNLESS(in_raw_data.GetSize() >= sizeof(*in_header), ResultServiceFrameworkInvalidCmifHeaderSize); + const cmif::PointerAndSize in_domain_raw_data = cmif::PointerAndSize(in_raw_data.GetAddress() + sizeof(*in_header), in_raw_data.GetSize() - sizeof(*in_header)); + + const DomainObjectId target_object_id = DomainObjectId{in_header->object_id}; + switch (in_header->type) { + case CmifDomainRequestType_SendMessage: + { + auto target_object = domain->GetObject(target_object_id); + + /* Mitm. If we don't have a target object, we should forward to let the server handle. */ + if (!target_object) { + return ctx.session->ForwardRequest(ctx); + } + + R_UNLESS(in_header->data_size + in_header->num_in_objects * sizeof(DomainObjectId) <= in_domain_raw_data.GetSize(), ResultServiceFrameworkInvalidCmifHeaderSize); + const cmif::PointerAndSize in_message_raw_data = cmif::PointerAndSize(in_domain_raw_data.GetAddress(), in_header->data_size); + DomainObjectId in_object_ids[8]; + R_UNLESS(in_header->num_in_objects <= util::size(in_object_ids), ResultServiceFrameworkInvalidCmifNumInObjects); + std::memcpy(in_object_ids, reinterpret_cast(in_message_raw_data.GetAddress() + in_message_raw_data.GetSize()), sizeof(DomainObjectId) * in_header->num_in_objects); + DomainServiceObjectProcessor domain_processor(domain, in_object_ids, in_header->num_in_objects); + if (ctx.processor == nullptr) { + ctx.processor = &domain_processor; + } else { + ctx.processor->SetImplementationProcessor(&domain_processor); + } + return target_object.ProcessMessage(ctx, in_message_raw_data); + } + case CmifDomainRequestType_Close: + { + auto target_object = domain->GetObject(target_object_id); + + /* If the object is not in the domain, tell the server to close it. */ + if (!target_object) { + return ctx.session->ForwardRequest(ctx); + } + + /* If the object is in the domain, close our copy of it. Mitm objects are required to close their associated domain id, so this shouldn't cause desynch. */ + domain->UnregisterObject(target_object_id); + return ResultSuccess; + } + default: + return ResultServiceFrameworkInvalidCmifInHeader; + } + } + + Result DomainServiceObjectProcessor::PrepareForProcess(const ServiceDispatchContext &ctx, const ServerMessageRuntimeMetadata runtime_metadata) const { + /* Validate in object count. */ + R_UNLESS(this->impl_metadata.GetInObjectCount() == this->GetInObjectCount(), ResultServiceFrameworkInvalidCmifNumInObjects); + + /* Nintendo reserves domain object IDs here. We do this later, to support mitm semantics. */ + + /* Pass onwards. */ + return this->impl_processor->PrepareForProcess(ctx, runtime_metadata); + } + + Result DomainServiceObjectProcessor::GetInObjects(ServiceObjectHolder *in_objects) const { + for (size_t i = 0; i < this->GetInObjectCount(); i++) { + in_objects[i] = this->domain->GetObject(this->in_object_ids[i]); + } + return ResultSuccess; + } + + HipcRequest DomainServiceObjectProcessor::PrepareForReply(const cmif::ServiceDispatchContext &ctx, PointerAndSize &out_raw_data, const ServerMessageRuntimeMetadata runtime_metadata) { + /* Call into impl processor, get request. */ + PointerAndSize raw_data; + HipcRequest request = this->impl_processor->PrepareForReply(ctx, raw_data, runtime_metadata); + + /* Write out header. */ + constexpr size_t out_header_size = sizeof(CmifDomainOutHeader); + const size_t impl_out_data_total_size = this->GetImplOutDataTotalSize(); + STS_ASSERT(out_header_size + impl_out_data_total_size + sizeof(DomainObjectId) * this->GetOutObjectCount() <= raw_data.GetSize()); + *reinterpret_cast(raw_data.GetPointer()) = CmifDomainOutHeader{ .num_out_objects = static_cast(this->GetOutObjectCount()), }; + + /* Set output raw data. */ + out_raw_data = cmif::PointerAndSize(raw_data.GetAddress() + out_header_size, raw_data.GetSize() - out_header_size); + this->out_object_ids = reinterpret_cast(out_raw_data.GetAddress() + out_raw_data.GetSize()); + + return request; + } + + void DomainServiceObjectProcessor::PrepareForErrorReply(const cmif::ServiceDispatchContext &ctx, PointerAndSize &out_raw_data, const ServerMessageRuntimeMetadata runtime_metadata) { + /* Call into impl processor, get request. */ + PointerAndSize raw_data; + this->impl_processor->PrepareForErrorReply(ctx, raw_data, runtime_metadata); + + /* Write out header. */ + constexpr size_t out_header_size = sizeof(CmifDomainOutHeader); + const size_t impl_out_data_total_size = this->GetImplOutDataTotalSize(); + STS_ASSERT(out_header_size + impl_out_data_total_size <= raw_data.GetSize()); + *reinterpret_cast(raw_data.GetPointer()) = CmifDomainOutHeader{ .num_out_objects = 0, }; + + /* Set output raw data. */ + out_raw_data = cmif::PointerAndSize(raw_data.GetAddress() + out_header_size, raw_data.GetSize() - out_header_size); + + /* Nintendo unreserves domain entries here, but we haven't reserved them yet. */ + } + + void DomainServiceObjectProcessor::SetOutObjects(const cmif::ServiceDispatchContext &ctx, const HipcRequest &response, ServiceObjectHolder *out_objects, DomainObjectId *selected_ids) { + const size_t num_out_objects = this->GetOutObjectCount(); + + /* Copy input object IDs from command impl (normally these are Invalid, in mitm they should be set). */ + DomainObjectId object_ids[8]; + bool is_reserved[8]; + for (size_t i = 0; i < num_out_objects; i++) { + object_ids[i] = selected_ids[i]; + is_reserved[i] = false; + } + + /* Reserve object IDs as necessary. */ + { + DomainObjectId reservations[8]; + { + size_t num_unreserved_ids = 0; + DomainObjectId specific_ids[8]; + size_t num_specific_ids = 0; + for (size_t i = 0; i < num_out_objects; i++) { + /* In the mitm case, we must not reserve IDs in use by other objects, so mitm objects will set this. */ + if (object_ids[i] == InvalidDomainObjectId) { + num_unreserved_ids++; + } else { + specific_ids[num_specific_ids++] = object_ids[i]; + } + } + /* TODO: Can we make this error non-fatal? It isn't for N, since they can reserve IDs earlier due to not having to worry about mitm. */ + R_ASSERT(this->domain->ReserveIds(reservations, num_unreserved_ids)); + this->domain->ReserveSpecificIds(specific_ids, num_specific_ids); + } + + size_t reservation_index = 0; + for (size_t i = 0; i < num_out_objects; i++) { + if (object_ids[i] == InvalidDomainObjectId) { + object_ids[i] = reservations[reservation_index++]; + is_reserved[i] = true; + } + } + } + + /* Actually set out objects. */ + for (size_t i = 0; i < num_out_objects; i++) { + if (!out_objects[i]) { + if (is_reserved[i]) { + this->domain->UnreserveIds(object_ids + i, 1); + } + object_ids[i] = InvalidDomainObjectId; + continue; + } + this->domain->RegisterObject(object_ids[i], std::move(out_objects[i])); + } + + /* Set out object IDs in message. */ + for (size_t i = 0; i < num_out_objects; i++) { + this->out_object_ids[i] = object_ids[i]; + } + } + +} diff --git a/stratosphere/libstratosphere/source/sf/cmif/sf_cmif_service_dispatch.cpp b/stratosphere/libstratosphere/source/sf/cmif/sf_cmif_service_dispatch.cpp index 62b6c09f2..673c3d980 100644 --- a/stratosphere/libstratosphere/source/sf/cmif/sf_cmif_service_dispatch.cpp +++ b/stratosphere/libstratosphere/source/sf/cmif/sf_cmif_service_dispatch.cpp @@ -26,6 +26,7 @@ namespace sts::sf::cmif { const CmifInHeader *in_header = reinterpret_cast(in_raw_data.GetPointer()); R_UNLESS(in_raw_data.GetSize() >= sizeof(*in_header), ResultServiceFrameworkInvalidCmifHeaderSize); R_UNLESS(in_header->magic == CMIF_IN_HEADER_MAGIC && in_header->version <= max_cmif_version, ResultServiceFrameworkInvalidCmifInHeader); + const cmif::PointerAndSize in_message_raw_data = cmif::PointerAndSize(in_raw_data.GetAddress() + sizeof(*in_header), in_raw_data.GetSize() - sizeof(*in_header)); const u32 cmd_id = in_header->command_id; /* Find a handler. */ @@ -40,7 +41,7 @@ namespace sts::sf::cmif { /* Invoke handler. */ CmifOutHeader *out_header = nullptr; - Result command_result = cmd_handler(&out_header, ctx, cmif::PointerAndSize(in_raw_data.GetAddress() + sizeof(*in_header), in_raw_data.GetSize() - sizeof(*in_header))); + Result command_result = cmd_handler(&out_header, ctx, in_message_raw_data); /* Forward forwardable results, otherwise ensure we can send result to user. */ R_TRY_CATCH(command_result) { @@ -65,6 +66,7 @@ namespace sts::sf::cmif { const CmifInHeader *in_header = reinterpret_cast(in_raw_data.GetPointer()); R_UNLESS(in_raw_data.GetSize() >= sizeof(*in_header), ResultServiceFrameworkInvalidCmifHeaderSize); R_UNLESS(in_header->magic == CMIF_IN_HEADER_MAGIC && in_header->version <= max_cmif_version, ResultServiceFrameworkInvalidCmifInHeader); + const cmif::PointerAndSize in_message_raw_data = cmif::PointerAndSize(in_raw_data.GetAddress() + sizeof(*in_header), in_raw_data.GetSize() - sizeof(*in_header)); const u32 cmd_id = in_header->command_id; /* Find a handler. */ @@ -78,21 +80,18 @@ namespace sts::sf::cmif { /* If we didn't find a handler, forward the request. */ if (cmd_handler == nullptr) { - /* TODO: FORWARD REQUEST */ - STS_ASSERT(false); + return ctx.session->ForwardRequest(ctx); } /* Invoke handler. */ CmifOutHeader *out_header = nullptr; - Result command_result = cmd_handler(&out_header, ctx, cmif::PointerAndSize(in_raw_data.GetAddress() + sizeof(*in_header), in_raw_data.GetSize() - sizeof(*in_header))); + Result command_result = cmd_handler(&out_header, ctx, in_message_raw_data); /* Forward forwardable results, otherwise ensure we can send result to user. */ R_TRY_CATCH(command_result) { R_CATCH(ResultServiceFrameworkRequestDeferredByUser) { return ResultServiceFrameworkRequestDeferredByUser; } R_CATCH(ResultAtmosphereMitmShouldForwardToSession) { - /* TODO: Restore TLS. */ - /* TODO: FORWARD REQUEST */ - STS_ASSERT(false); + return ctx.session->ForwardRequest(ctx); } R_CATCH_ALL() { STS_ASSERT(out_header != nullptr); } } R_END_TRY_CATCH; diff --git a/stratosphere/libstratosphere/source/sf/hipc/sf_hipc_server_domain_session_manager.cpp b/stratosphere/libstratosphere/source/sf/hipc/sf_hipc_server_domain_session_manager.cpp index e7ebe4c70..10eb7b80b 100644 --- a/stratosphere/libstratosphere/source/sf/hipc/sf_hipc_server_domain_session_manager.cpp +++ b/stratosphere/libstratosphere/source/sf/hipc/sf_hipc_server_domain_session_manager.cpp @@ -63,8 +63,50 @@ namespace sts::sf::hipc { } Result ConvertCurrentObjectToDomain(sf::Out out) { - /* TODO */ - return ResultHipcOutOfDomains; + /* Allocate a domain. */ + auto domain = this->manager->AllocateDomainServiceObject(); + R_UNLESS(domain, ResultHipcOutOfDomains); + + cmif::DomainObjectId object_id = cmif::InvalidDomainObjectId; + + cmif::ServiceObjectHolder new_holder; + + if (this->is_mitm_session) { + /* If we're a mitm session, we need to convert the remote session to domain. */ + STS_ASSERT(session->forward_service->own_handle); + R_TRY_CLEANUP(serviceConvertToDomain(session->forward_service.get()), { + this->manager->FreeDomainServiceObject(domain); + }); + + /* The object ID reservation cannot fail here, as that would cause desynchronization from target domain. */ + object_id = cmif::DomainObjectId{session->forward_service->object_id}; + domain->ReserveSpecificIds(&object_id, 1); + + /* Create new object. */ + cmif::MitmDomainServiceObject *domain_ptr = static_cast(domain); + new_holder = cmif::ServiceObjectHolder(std::move(std::shared_ptr(domain_ptr, [&](cmif::MitmDomainServiceObject *obj) { + this->manager->FreeDomainServiceObject(domain); + }))); + } else { + /* We're not a mitm session. Reserve a new object in the domain. */ + R_TRY(domain->ReserveIds(&object_id, 1)); + + /* Create new object. */ + cmif::DomainServiceObject *domain_ptr = static_cast(domain); + new_holder = cmif::ServiceObjectHolder(std::move(std::shared_ptr(domain_ptr, [&](cmif::DomainServiceObject *obj) { + this->manager->FreeDomainServiceObject(domain); + }))); + } + + STS_ASSERT(object_id != cmif::InvalidDomainObjectId); + STS_ASSERT(static_cast(new_holder)); + + /* We succeeded! */ + domain->RegisterObject(object_id, std::move(session->srv_obj_holder)); + session->srv_obj_holder = std::move(new_holder); + out.SetValue(object_id); + + return ResultSuccess; } Result CopyFromCurrentDomain(sf::OutMoveHandle out, cmif::DomainObjectId object_id) { diff --git a/stratosphere/libstratosphere/source/sf/hipc/sf_hipc_server_session_manager.cpp b/stratosphere/libstratosphere/source/sf/hipc/sf_hipc_server_session_manager.cpp index cc794bc04..5290ba67e 100644 --- a/stratosphere/libstratosphere/source/sf/hipc/sf_hipc_server_session_manager.cpp +++ b/stratosphere/libstratosphere/source/sf/hipc/sf_hipc_server_session_manager.cpp @@ -17,6 +17,57 @@ namespace sts::sf::hipc { + namespace { + + constexpr inline void PreProcessCommandBufferForMitm(const cmif::ServiceDispatchContext &ctx, const cmif::PointerAndSize &pointer_buffer, uintptr_t cmd_buffer) { + /* TODO: Less gross method of editing command buffer? */ + if (ctx.request.meta.send_pid) { + constexpr u64 MitmProcessIdTag = 0xFFFE000000000000ul; + constexpr u64 OldProcessIdMask = 0x0000FFFFFFFFFFFFul; + u64 *process_id = reinterpret_cast(cmd_buffer + sizeof(HipcHeader) + sizeof(HipcSpecialHeader)); + *process_id = (MitmProcessIdTag) | (*process_id & OldProcessIdMask); + } + + if (ctx.request.meta.num_recv_statics) { + /* TODO: Can we do this without gross bit-hackery? */ + reinterpret_cast(cmd_buffer)->recv_static_mode = 2; + const uintptr_t old_recv_list_entry = reinterpret_cast(ctx.request.data.recv_list); + const size_t old_recv_list_offset = old_recv_list_entry - util::AlignDown(old_recv_list_entry, TlsMessageBufferSize); + *reinterpret_cast(cmd_buffer + old_recv_list_offset) = hipcMakeRecvStatic(pointer_buffer.GetPointer(), pointer_buffer.GetSize()); + } + } + + } + + Result ServerSession::ForwardRequest(const cmif::ServiceDispatchContext &ctx) const { + STS_ASSERT(this->IsMitmSession()); + /* TODO: Support non-TLS messages? */ + STS_ASSERT(this->saved_message.GetPointer() != nullptr); + STS_ASSERT(this->saved_message.GetSize() == TlsMessageBufferSize); + + /* Copy saved TLS in. */ + std::memcpy(armGetTls(), this->saved_message.GetPointer(), this->saved_message.GetSize()); + + /* Prepare buffer. */ + PreProcessCommandBufferForMitm(ctx, this->pointer_buffer, reinterpret_cast(armGetTls())); + + /* Dispatch forwards. */ + R_TRY(svcSendSyncRequest(this->forward_service->session)); + + /* Parse, to ensure we catch any copy handles and close them. */ + { + const auto response = hipcParseResponse(armGetTls()); + if (response.num_copy_handles) { + ctx.handles_to_close->num_handles = response.num_copy_handles; + for (size_t i = 0; i < response.num_copy_handles; i++) { + ctx.handles_to_close->handles[i] = response.copy_handles[i]; + } + } + } + + return ResultSuccess; + } + void ServerSessionManager::DestroySession(ServerSession *session) { /* Destroy object. */ session->~ServerSession(); @@ -63,6 +114,9 @@ namespace sts::sf::hipc { /* Assign session resources. */ session_memory->pointer_buffer = this->GetSessionPointerBuffer(session_memory); session_memory->saved_message = this->GetSessionSavedMessageBuffer(session_memory); + /* Validate session pointer buffer. */ + STS_ASSERT(session_memory->pointer_buffer.GetSize() >= session_memory->forward_service->pointer_buffer_size); + session_memory->pointer_buffer = cmif::PointerAndSize(session_memory->pointer_buffer.GetAddress(), session_memory->forward_service->pointer_buffer_size); /* Register to wait list. */ this->RegisterSessionToWaitList(session_memory); return ResultSuccess; @@ -205,6 +259,7 @@ namespace sts::sf::hipc { cmif::ServiceDispatchContext dispatch_ctx = { .srv_obj = obj_holder.GetServiceObjectUnsafe(), .manager = this, + .session = session, .processor = nullptr, /* Filled in by template implementations. */ .handles_to_close = &handles_to_close, .pointer_buffer = session->pointer_buffer,