michael@0: // Copyright (c) 2012 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #ifndef SANDBOX_SRC_CROSSCALL_CLIENT_H_ michael@0: #define SANDBOX_SRC_CROSSCALL_CLIENT_H_ michael@0: michael@0: #include "sandbox/win/src/crosscall_params.h" michael@0: #include "sandbox/win/src/sandbox.h" michael@0: michael@0: // This header defines the CrossCall(..) family of templated functions michael@0: // Their purpose is to simulate the syntax of regular call but to generate michael@0: // and IPC from the client-side. michael@0: // michael@0: // The basic pattern is to michael@0: // 1) use template argument deduction to compute the size of each michael@0: // parameter and the appropriate copy method michael@0: // 2) pack the parameters in the appropriate ActualCallParams< > object michael@0: // 3) call the IPC interface IPCProvider::DoCall( ) michael@0: // michael@0: // The general interface of CrossCall is: michael@0: // ResultCode CrossCall(IPCProvider& ipc_provider, michael@0: // uint32 tag, michael@0: // const Par1& p1, const Par2& p2,...pn michael@0: // CrossCallReturn* answer) michael@0: // michael@0: // where: michael@0: // ipc_provider: is a specific implementation of the ipc transport see michael@0: // sharedmem_ipc_server.h for an example. michael@0: // tag : is the unique id for this IPC call. Is used to route the call to michael@0: // the appropriate service. michael@0: // p1, p2,.. pn : The input parameters of the IPC. Use only simple types michael@0: // and wide strings (can add support for others). michael@0: // answer : If the IPC was successful. The server-side answer is here. The michael@0: // interpretation of the answer is private to client and server. michael@0: // michael@0: // The return value is ALL_OK if the IPC was delivered to the server, other michael@0: // return codes indicate that the IPC transport failed to deliver it. michael@0: namespace sandbox { michael@0: michael@0: // this is the assumed channel size. This can be overridden in a given michael@0: // IPC implementation. michael@0: const uint32 kIPCChannelSize = 1024; michael@0: michael@0: // The copy helper uses templates to deduce the appropriate copy function to michael@0: // copy the input parameters in the buffer that is going to be send across the michael@0: // IPC. These template facility can be made more sophisticated as need arises. michael@0: michael@0: // The default copy helper. It catches the general case where no other michael@0: // specialized template matches better. We set the type to ULONG_TYPE, so this michael@0: // only works with objects whose size is 32 bits. michael@0: template michael@0: class CopyHelper { michael@0: public: michael@0: CopyHelper(const T& t) : t_(t) {} michael@0: michael@0: // Returns the pointer to the start of the input. michael@0: const void* GetStart() const { michael@0: return &t_; michael@0: } michael@0: michael@0: // Update the stored value with the value in the buffer. This is not michael@0: // supported for this type. michael@0: bool Update(void* buffer) { michael@0: // Not supported; michael@0: return true; michael@0: } michael@0: michael@0: // Returns the size of the input in bytes. michael@0: uint32 GetSize() const { michael@0: return sizeof(T); michael@0: } michael@0: michael@0: // Returns true if the current type is used as an In or InOut parameter. michael@0: bool IsInOut() { michael@0: return false; michael@0: } michael@0: michael@0: // Returns this object's type. michael@0: ArgType GetType() { michael@0: COMPILE_ASSERT(sizeof(T) == sizeof(uint32), need_specialization); michael@0: return ULONG_TYPE; michael@0: } michael@0: michael@0: private: michael@0: const T& t_; michael@0: }; michael@0: michael@0: // This copy helper template specialization if for the void pointer michael@0: // case both 32 and 64 bit. michael@0: template<> michael@0: class CopyHelper { michael@0: public: michael@0: CopyHelper(void* t) : t_(t) {} michael@0: michael@0: // Returns the pointer to the start of the input. michael@0: const void* GetStart() const { michael@0: return &t_; michael@0: } michael@0: michael@0: // Update the stored value with the value in the buffer. This is not michael@0: // supported for this type. michael@0: bool Update(void* buffer) { michael@0: // Not supported; michael@0: return true; michael@0: } michael@0: michael@0: // Returns the size of the input in bytes. michael@0: uint32 GetSize() const { michael@0: return sizeof(t_); michael@0: } michael@0: michael@0: // Returns true if the current type is used as an In or InOut parameter. michael@0: bool IsInOut() { michael@0: return false; michael@0: } michael@0: michael@0: // Returns this object's type. michael@0: ArgType GetType() { michael@0: return VOIDPTR_TYPE; michael@0: } michael@0: michael@0: private: michael@0: const void* t_; michael@0: }; michael@0: michael@0: // This copy helper template specialization catches the cases where the michael@0: // parameter is a pointer to a string. michael@0: template<> michael@0: class CopyHelper { michael@0: public: michael@0: CopyHelper(const wchar_t* t) michael@0: : t_(t) { michael@0: } michael@0: michael@0: // Returns the pointer to the start of the string. michael@0: const void* GetStart() const { michael@0: return t_; michael@0: } michael@0: michael@0: // Update the stored value with the value in the buffer. This is not michael@0: // supported for this type. michael@0: bool Update(void* buffer) { michael@0: // Not supported; michael@0: return true; michael@0: } michael@0: michael@0: // Returns the size of the string in bytes. We define a NULL string to michael@0: // be of zero length. michael@0: uint32 GetSize() const { michael@0: __try { michael@0: return (!t_) ? 0 : static_cast(StringLength(t_) * sizeof(t_[0])); michael@0: } michael@0: __except(EXCEPTION_EXECUTE_HANDLER) { michael@0: return kuint32max; michael@0: } michael@0: } michael@0: michael@0: // Returns true if the current type is used as an In or InOut parameter. michael@0: bool IsInOut() { michael@0: return false; michael@0: } michael@0: michael@0: ArgType GetType() { michael@0: return WCHAR_TYPE; michael@0: } michael@0: michael@0: private: michael@0: // We provide our not very optimized version of wcslen(), since we don't michael@0: // want to risk having the linker use the version in the CRT since the CRT michael@0: // might not be present when we do an early IPC call. michael@0: static size_t __cdecl StringLength(const wchar_t* wcs) { michael@0: const wchar_t *eos = wcs; michael@0: while (*eos++); michael@0: return static_cast(eos - wcs - 1); michael@0: } michael@0: michael@0: const wchar_t* t_; michael@0: }; michael@0: michael@0: // Specialization for non-const strings. We just reuse the implementation of the michael@0: // const string specialization. michael@0: template<> michael@0: class CopyHelper : public CopyHelper { michael@0: public: michael@0: typedef CopyHelper Base; michael@0: CopyHelper(wchar_t* t) : Base(t) {} michael@0: michael@0: const void* GetStart() const { michael@0: return Base::GetStart(); michael@0: } michael@0: michael@0: bool Update(void* buffer) { michael@0: return Base::Update(buffer); michael@0: } michael@0: michael@0: uint32 GetSize() const { michael@0: return Base::GetSize(); michael@0: } michael@0: michael@0: bool IsInOut() { michael@0: return Base::IsInOut(); michael@0: } michael@0: michael@0: ArgType GetType() { michael@0: return Base::GetType(); michael@0: } michael@0: }; michael@0: michael@0: // Specialization for wchar_t arrays strings. We just reuse the implementation michael@0: // of the const string specialization. michael@0: template michael@0: class CopyHelper : public CopyHelper { michael@0: public: michael@0: typedef const wchar_t array[n]; michael@0: typedef CopyHelper Base; michael@0: CopyHelper(array t) : Base(t) {} michael@0: michael@0: const void* GetStart() const { michael@0: return Base::GetStart(); michael@0: } michael@0: michael@0: bool Update(void* buffer) { michael@0: return Base::Update(buffer); michael@0: } michael@0: michael@0: uint32 GetSize() const { michael@0: return Base::GetSize(); michael@0: } michael@0: michael@0: bool IsInOut() { michael@0: return Base::IsInOut(); michael@0: } michael@0: michael@0: ArgType GetType() { michael@0: return Base::GetType(); michael@0: } michael@0: }; michael@0: michael@0: // Generic encapsulation class containing a pointer to a buffer and the michael@0: // size of the buffer. It is used by the IPC to be able to pass in/out michael@0: // parameters. michael@0: class InOutCountedBuffer : public CountedBuffer { michael@0: public: michael@0: InOutCountedBuffer(void* buffer, uint32 size) : CountedBuffer(buffer, size) {} michael@0: }; michael@0: michael@0: // This copy helper template specialization catches the cases where the michael@0: // parameter is a an input/output buffer. michael@0: template<> michael@0: class CopyHelper { michael@0: public: michael@0: CopyHelper(const InOutCountedBuffer t) : t_(t) {} michael@0: michael@0: // Returns the pointer to the start of the string. michael@0: const void* GetStart() const { michael@0: return t_.Buffer(); michael@0: } michael@0: michael@0: // Updates the buffer with the value from the new buffer in parameter. michael@0: bool Update(void* buffer) { michael@0: // We are touching user memory, this has to be done from inside a try michael@0: // except. michael@0: __try { michael@0: memcpy(t_.Buffer(), buffer, t_.Size()); michael@0: } michael@0: __except(EXCEPTION_EXECUTE_HANDLER) { michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: // Returns the size of the string in bytes. We define a NULL string to michael@0: // be of zero length. michael@0: uint32 GetSize() const { michael@0: return t_.Size(); michael@0: } michael@0: michael@0: // Returns true if the current type is used as an In or InOut parameter. michael@0: bool IsInOut() { michael@0: return true; michael@0: } michael@0: michael@0: ArgType GetType() { michael@0: return INOUTPTR_TYPE; michael@0: } michael@0: michael@0: private: michael@0: const InOutCountedBuffer t_; michael@0: }; michael@0: michael@0: // The following two macros make it less error prone the generation michael@0: // of CrossCall functions with ever more input parameters. michael@0: michael@0: #define XCALL_GEN_PARAMS_OBJ(num, params) \ michael@0: typedef ActualCallParams ActualParams; \ michael@0: void* raw_mem = ipc_provider.GetBuffer(); \ michael@0: if (NULL == raw_mem) \ michael@0: return SBOX_ERROR_NO_SPACE; \ michael@0: ActualParams* params = new(raw_mem) ActualParams(tag); michael@0: michael@0: #define XCALL_GEN_COPY_PARAM(num, params) \ michael@0: COMPILE_ASSERT(kMaxIpcParams >= num, too_many_parameters); \ michael@0: CopyHelper ch##num(p##num); \ michael@0: if (!params->CopyParamIn(num - 1, ch##num.GetStart(), ch##num.GetSize(), \ michael@0: ch##num.IsInOut(), ch##num.GetType())) \ michael@0: return SBOX_ERROR_NO_SPACE; michael@0: michael@0: #define XCALL_GEN_UPDATE_PARAM(num, params) \ michael@0: if (!ch##num.Update(params->GetParamPtr(num-1))) {\ michael@0: ipc_provider.FreeBuffer(raw_mem); \ michael@0: return SBOX_ERROR_BAD_PARAMS; \ michael@0: } michael@0: michael@0: #define XCALL_GEN_FREE_CHANNEL() \ michael@0: ipc_provider.FreeBuffer(raw_mem); michael@0: michael@0: // CrossCall template with one input parameter michael@0: template michael@0: ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, michael@0: CrossCallReturn* answer) { michael@0: XCALL_GEN_PARAMS_OBJ(1, call_params); michael@0: XCALL_GEN_COPY_PARAM(1, call_params); michael@0: michael@0: ResultCode result = ipc_provider.DoCall(call_params, answer); michael@0: michael@0: if (SBOX_ERROR_CHANNEL_ERROR != result) { michael@0: XCALL_GEN_UPDATE_PARAM(1, call_params); michael@0: XCALL_GEN_FREE_CHANNEL(); michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: // CrossCall template with two input parameters. michael@0: template michael@0: ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, michael@0: const Par2& p2, CrossCallReturn* answer) { michael@0: XCALL_GEN_PARAMS_OBJ(2, call_params); michael@0: XCALL_GEN_COPY_PARAM(1, call_params); michael@0: XCALL_GEN_COPY_PARAM(2, call_params); michael@0: michael@0: ResultCode result = ipc_provider.DoCall(call_params, answer); michael@0: michael@0: if (SBOX_ERROR_CHANNEL_ERROR != result) { michael@0: XCALL_GEN_UPDATE_PARAM(1, call_params); michael@0: XCALL_GEN_UPDATE_PARAM(2, call_params); michael@0: XCALL_GEN_FREE_CHANNEL(); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: // CrossCall template with three input parameters. michael@0: template michael@0: ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, michael@0: const Par2& p2, const Par3& p3, CrossCallReturn* answer) { michael@0: XCALL_GEN_PARAMS_OBJ(3, call_params); michael@0: XCALL_GEN_COPY_PARAM(1, call_params); michael@0: XCALL_GEN_COPY_PARAM(2, call_params); michael@0: XCALL_GEN_COPY_PARAM(3, call_params); michael@0: michael@0: ResultCode result = ipc_provider.DoCall(call_params, answer); michael@0: michael@0: if (SBOX_ERROR_CHANNEL_ERROR != result) { michael@0: XCALL_GEN_UPDATE_PARAM(1, call_params); michael@0: XCALL_GEN_UPDATE_PARAM(2, call_params); michael@0: XCALL_GEN_UPDATE_PARAM(3, call_params); michael@0: XCALL_GEN_FREE_CHANNEL(); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: // CrossCall template with four input parameters. michael@0: template michael@0: ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, michael@0: const Par2& p2, const Par3& p3, const Par4& p4, michael@0: CrossCallReturn* answer) { michael@0: XCALL_GEN_PARAMS_OBJ(4, call_params); michael@0: XCALL_GEN_COPY_PARAM(1, call_params); michael@0: XCALL_GEN_COPY_PARAM(2, call_params); michael@0: XCALL_GEN_COPY_PARAM(3, call_params); michael@0: XCALL_GEN_COPY_PARAM(4, call_params); michael@0: michael@0: ResultCode result = ipc_provider.DoCall(call_params, answer); michael@0: michael@0: if (SBOX_ERROR_CHANNEL_ERROR != result) { michael@0: XCALL_GEN_UPDATE_PARAM(1, call_params); michael@0: XCALL_GEN_UPDATE_PARAM(2, call_params); michael@0: XCALL_GEN_UPDATE_PARAM(3, call_params); michael@0: XCALL_GEN_UPDATE_PARAM(4, call_params); michael@0: XCALL_GEN_FREE_CHANNEL(); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: // CrossCall template with five input parameters. michael@0: template michael@0: ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, michael@0: const Par2& p2, const Par3& p3, const Par4& p4, michael@0: const Par5& p5, CrossCallReturn* answer) { michael@0: XCALL_GEN_PARAMS_OBJ(5, call_params); michael@0: XCALL_GEN_COPY_PARAM(1, call_params); michael@0: XCALL_GEN_COPY_PARAM(2, call_params); michael@0: XCALL_GEN_COPY_PARAM(3, call_params); michael@0: XCALL_GEN_COPY_PARAM(4, call_params); michael@0: XCALL_GEN_COPY_PARAM(5, call_params); michael@0: michael@0: ResultCode result = ipc_provider.DoCall(call_params, answer); michael@0: michael@0: if (SBOX_ERROR_CHANNEL_ERROR != result) { michael@0: XCALL_GEN_UPDATE_PARAM(1, call_params); michael@0: XCALL_GEN_UPDATE_PARAM(2, call_params); michael@0: XCALL_GEN_UPDATE_PARAM(3, call_params); michael@0: XCALL_GEN_UPDATE_PARAM(4, call_params); michael@0: XCALL_GEN_UPDATE_PARAM(5, call_params); michael@0: XCALL_GEN_FREE_CHANNEL(); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: // CrossCall template with six input parameters. michael@0: template michael@0: ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, michael@0: const Par2& p2, const Par3& p3, const Par4& p4, michael@0: const Par5& p5, const Par6& p6, CrossCallReturn* answer) { michael@0: XCALL_GEN_PARAMS_OBJ(6, call_params); michael@0: XCALL_GEN_COPY_PARAM(1, call_params); michael@0: XCALL_GEN_COPY_PARAM(2, call_params); michael@0: XCALL_GEN_COPY_PARAM(3, call_params); michael@0: XCALL_GEN_COPY_PARAM(4, call_params); michael@0: XCALL_GEN_COPY_PARAM(5, call_params); michael@0: XCALL_GEN_COPY_PARAM(6, call_params); michael@0: michael@0: ResultCode result = ipc_provider.DoCall(call_params, answer); michael@0: michael@0: if (SBOX_ERROR_CHANNEL_ERROR != result) { michael@0: XCALL_GEN_UPDATE_PARAM(1, call_params); michael@0: XCALL_GEN_UPDATE_PARAM(2, call_params); michael@0: XCALL_GEN_UPDATE_PARAM(3, call_params); michael@0: XCALL_GEN_UPDATE_PARAM(4, call_params); michael@0: XCALL_GEN_UPDATE_PARAM(5, call_params); michael@0: XCALL_GEN_UPDATE_PARAM(6, call_params); michael@0: XCALL_GEN_FREE_CHANNEL(); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: // CrossCall template with seven input parameters. michael@0: template michael@0: ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, michael@0: const Par2& p2, const Par3& p3, const Par4& p4, michael@0: const Par5& p5, const Par6& p6, const Par7& p7, michael@0: CrossCallReturn* answer) { michael@0: XCALL_GEN_PARAMS_OBJ(7, call_params); michael@0: XCALL_GEN_COPY_PARAM(1, call_params); michael@0: XCALL_GEN_COPY_PARAM(2, call_params); michael@0: XCALL_GEN_COPY_PARAM(3, call_params); michael@0: XCALL_GEN_COPY_PARAM(4, call_params); michael@0: XCALL_GEN_COPY_PARAM(5, call_params); michael@0: XCALL_GEN_COPY_PARAM(6, call_params); michael@0: XCALL_GEN_COPY_PARAM(7, call_params); michael@0: michael@0: ResultCode result = ipc_provider.DoCall(call_params, answer); michael@0: michael@0: if (SBOX_ERROR_CHANNEL_ERROR != result) { michael@0: XCALL_GEN_UPDATE_PARAM(1, call_params); michael@0: XCALL_GEN_UPDATE_PARAM(2, call_params); michael@0: XCALL_GEN_UPDATE_PARAM(3, call_params); michael@0: XCALL_GEN_UPDATE_PARAM(4, call_params); michael@0: XCALL_GEN_UPDATE_PARAM(5, call_params); michael@0: XCALL_GEN_UPDATE_PARAM(6, call_params); michael@0: XCALL_GEN_UPDATE_PARAM(7, call_params); michael@0: XCALL_GEN_FREE_CHANNEL(); michael@0: } michael@0: return result; michael@0: } michael@0: } // namespace sandbox michael@0: michael@0: #endif // SANDBOX_SRC_CROSSCALL_CLIENT_H__