sf: implement mitm forwarding + domains.

This commit is contained in:
Michael Scire 2019-10-21 02:45:52 -07:00 committed by SciresM
parent 0b22af1206
commit 4f455dacf4
14 changed files with 509 additions and 68 deletions

View file

@ -26,6 +26,8 @@ static constexpr Result ResultServiceFrameworkInvalidCmifHeaderSize = MAKERESULT
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);

View file

@ -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;

View file

@ -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<Domain *>(object)->~Domain();
this->FreeDomain(object);
}
};

View file

@ -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<u16>(runtime_metadata.GetInDataSize() + runtime_metadata.GetInObjectCount() * sizeof(DomainObjectId)),
.out_data_size = static_cast<u16>(runtime_metadata.GetOutDataSize() + runtime_metadata.GetOutObjectCount() * sizeof(DomainObjectId)),
.in_headers_size = static_cast<u8>(runtime_metadata.GetInHeadersSize() + sizeof(CmifDomainInHeader)),
.out_headers_size = static_cast<u8>(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<DomainServiceObject> {
static_assert(std::is_base_of<sf::IServiceObject, DomainServiceObject>::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<MitmDomainServiceObject> {
static_assert(std::is_base_of<DomainServiceObject, MitmDomainServiceObject>::value, "MitmDomainServiceObject must derive from DomainServiceObject");
using ProcessHandlerType = decltype(ServiceDispatchMeta::ProcessHandler);
using DispatchTableType = DomainServiceObjectDispatchTable;
static constexpr ProcessHandlerType ProcessHandlerImpl = &impl::ServiceDispatchTableBase::ProcessMessageForMitm<DispatchTableType>;
static constexpr inline ServiceDispatchMeta Meta{&DomainServiceObject::s_CmifServiceDispatchTable, ProcessHandlerImpl};
};
}

View file

@ -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<ServerMessageRuntimeMetadata>::value, "std::is_pod<ServerMessageRuntimeMetadata>::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;
};
}

View file

@ -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;

View file

@ -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);
}
};
}

View file

@ -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 {

View file

@ -442,8 +442,7 @@ namespace sts::sf::impl {
/* In/Out data marshalling. */
static constexpr std::array<size_t, NumInDatas+1> InDataOffsets = RawDataOffsetCalculator<InDatas>::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<size_t, NumOutDatas+1> OutDataOffsets = RawDataOffsetCalculator<OutDatas>::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<typename>
@ -686,7 +695,11 @@ namespace sts::sf::impl {
template<typename CommandMeta>
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<u32>((util::AlignUp(CommandMeta::OutDataSize + headers_size, 0x4) + 0x10 /* padding */) / sizeof(u32)),
.num_data_words = static_cast<u32>((util::AlignUp(raw_size, 0x4) + 0x10 /* padding */) / sizeof(u32)),
.num_copy_handles = CommandMeta::NumOutCopyHandles,
.num_move_handles = static_cast<u32>(CommandMeta::NumOutMoveHandles + num_out_object_handles),
.num_move_handles = static_cast<u32>(CommandMeta::NumOutMoveHandles + runtime_metadata.GetOutObjectCount()),
);
out_raw_data = cmif::PointerAndSize(util::AlignUp(reinterpret_cast<uintptr_t>(response.data_words), 0x10), CommandMeta::OutDataSize + headers_size);
out_raw_data = cmif::PointerAndSize(util::AlignUp(reinterpret_cast<uintptr_t>(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<u32>((util::AlignUp(headers_size, 0x4) + 0x10 /* padding */) / sizeof(u32)),
.num_data_words = static_cast<u32>((util::AlignUp(raw_size, 0x4) + 0x10 /* padding */) / sizeof(u32)),
);
out_raw_data = cmif::PointerAndSize(util::AlignUp(reinterpret_cast<uintptr_t>(response.data_words), 0x10), headers_size);
out_raw_data = cmif::PointerAndSize(util::AlignUp(reinterpret_cast<uintptr_t>(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<size_t BufferIndex, size_t Index = GetIndexFromBufferIndex<BufferIndex>>
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<size_t>::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<const u16 *>(reinterpret_cast<uintptr_t>(ctx.request.data.data_words) + in_headers_size + CommandMeta::InDataRawUnfixedOutPointerSizeOffset);
const u16 *recv_pointer_sizes = reinterpret_cast<const u16 *>(reinterpret_cast<uintptr_t>(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<const u16 *>(reinterpret_cast<uintptr_t>(ctx.request.data.data_words) + CommandMeta::InDataRawUnfixedOutPointerSizeOffset);
const u16 *recv_pointer_sizes = reinterpret_cast<const u16 *>(reinterpret_cast<uintptr_t>(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<bool, CommandMeta::NumBuffers> &is_buffer_map_alias, size_t in_headers_size) {
NX_CONSTEXPR Result ProcessBuffers(const cmif::ServiceDispatchContext &ctx, BufferArrayType &buffers, std::array<bool, CommandMeta::NumBuffers> &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<n>(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<n>(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);

View file

@ -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));
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <functional>
#include <stratosphere.hpp>
namespace sts::sf::cmif {
Result DomainServiceObjectDispatchTable::ProcessMessage(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const {
return this->ProcessMessageImpl(ctx, static_cast<DomainServiceObject *>(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<DomainServiceObject *>(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<const CmifDomainInHeader *>(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<bool>(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<DomainObjectId *>(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<const CmifDomainInHeader *>(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<DomainObjectId *>(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<CmifDomainOutHeader *>(raw_data.GetPointer()) = CmifDomainOutHeader{ .num_out_objects = static_cast<u32>(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<DomainObjectId *>(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<CmifDomainOutHeader *>(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];
}
}
}

View file

@ -26,6 +26,7 @@ namespace sts::sf::cmif {
const CmifInHeader *in_header = reinterpret_cast<const CmifInHeader *>(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<const CmifInHeader *>(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;

View file

@ -63,8 +63,50 @@ namespace sts::sf::hipc {
}
Result ConvertCurrentObjectToDomain(sf::Out<cmif::DomainObjectId> 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<cmif::MitmDomainServiceObject *>(domain);
new_holder = cmif::ServiceObjectHolder(std::move(std::shared_ptr<cmif::MitmDomainServiceObject>(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<cmif::DomainServiceObject *>(domain);
new_holder = cmif::ServiceObjectHolder(std::move(std::shared_ptr<cmif::DomainServiceObject>(domain_ptr, [&](cmif::DomainServiceObject *obj) {
this->manager->FreeDomainServiceObject(domain);
})));
}
STS_ASSERT(object_id != cmif::InvalidDomainObjectId);
STS_ASSERT(static_cast<bool>(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) {

View file

@ -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<u64 *>(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<HipcHeader *>(cmd_buffer)->recv_static_mode = 2;
const uintptr_t old_recv_list_entry = reinterpret_cast<uintptr_t>(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<HipcRecvListEntry *>(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<uintptr_t>(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,