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_PARAMS_H__ michael@0: #define SANDBOX_SRC_CROSSCALL_PARAMS_H__ michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: michael@0: #include "base/basictypes.h" michael@0: #include "sandbox/win/src/internal_types.h" michael@0: #include "sandbox/win/src/sandbox_types.h" michael@0: michael@0: namespace { michael@0: michael@0: // Increases |value| until there is no need for padding given an int64 michael@0: // alignment. Returns the increased value. michael@0: uint32 Align(uint32 value) { michael@0: uint32 alignment = sizeof(int64); michael@0: return ((value + alignment - 1) / alignment) * alignment; michael@0: } michael@0: michael@0: } michael@0: // This header is part of CrossCall: the sandbox inter-process communication. michael@0: // This header defines the basic types used both in the client IPC and in the michael@0: // server IPC code. CrossCallParams and ActualCallParams model the input michael@0: // parameters of an IPC call and CrossCallReturn models the output params and michael@0: // the return value. michael@0: // michael@0: // An IPC call is defined by its 'tag' which is a (uint32) unique identifier michael@0: // that is used to route the IPC call to the proper server. Every tag implies michael@0: // a complete call signature including the order and type of each parameter. michael@0: // michael@0: // Like most IPC systems. CrossCall is designed to take as inputs 'simple' michael@0: // types such as integers and strings. Classes, generic arrays or pointers to michael@0: // them are not supported. michael@0: // michael@0: // Another limitation of CrossCall is that the return value and output michael@0: // parameters can only be uint32 integers. Returning complex structures or michael@0: // strings is not supported. michael@0: michael@0: namespace sandbox { michael@0: michael@0: // max number of extended return parameters. See CrossCallReturn michael@0: const size_t kExtendedReturnCount = 8; michael@0: michael@0: // Union of multiple types to be used as extended results michael@0: // in the CrossCallReturn. michael@0: union MultiType { michael@0: uint32 unsigned_int; michael@0: void* pointer; michael@0: HANDLE handle; michael@0: ULONG_PTR ulong_ptr; michael@0: }; michael@0: michael@0: // Maximum number of IPC parameters currently supported. michael@0: // To increase this value, we have to: michael@0: // - Add another Callback typedef to Dispatcher. michael@0: // - Add another case to the switch on SharedMemIPCServer::InvokeCallback. michael@0: // - Add another case to the switch in GetActualAndMaxBufferSize michael@0: const int kMaxIpcParams = 9; michael@0: michael@0: // Contains the information about a parameter in the ipc buffer. michael@0: struct ParamInfo { michael@0: ArgType type_; michael@0: uint32 offset_; michael@0: uint32 size_; michael@0: }; michael@0: michael@0: // Models the return value and the return parameters of an IPC call michael@0: // currently limited to one status code and eight generic return values michael@0: // which cannot be pointers to other data. For x64 ports this structure michael@0: // might have to use other integer types. michael@0: struct CrossCallReturn { michael@0: // the IPC tag. It should match the original IPC tag. michael@0: uint32 tag; michael@0: // The result of the IPC operation itself. michael@0: ResultCode call_outcome; michael@0: // the result of the IPC call as executed in the server. The interpretation michael@0: // of this value depends on the specific service. michael@0: union { michael@0: NTSTATUS nt_status; michael@0: DWORD win32_result; michael@0: }; michael@0: // Number of extended return values. michael@0: uint32 extended_count; michael@0: // for calls that should return a windows handle. It is found here. michael@0: HANDLE handle; michael@0: // The array of extended values. michael@0: MultiType extended[kExtendedReturnCount]; michael@0: }; michael@0: michael@0: // CrossCallParams base class that models the input params all packed in a michael@0: // single compact memory blob. The representation can vary but in general a michael@0: // given child of this class is meant to represent all input parameters michael@0: // necessary to make a IPC call. michael@0: // michael@0: // This class cannot have virtual members because its assumed the IPC michael@0: // parameters start from the 'this' pointer to the end, which is defined by michael@0: // one of the subclasses michael@0: // michael@0: // Objects of this class cannot be constructed directly. Only derived michael@0: // classes have the proper knowledge to construct it. michael@0: class CrossCallParams { michael@0: public: michael@0: // Returns the tag (ipc unique id) associated with this IPC. michael@0: uint32 GetTag() const { michael@0: return tag_; michael@0: } michael@0: michael@0: // Returns the beggining of the buffer where the IPC params can be stored. michael@0: // prior to an IPC call michael@0: const void* GetBuffer() const { michael@0: return this; michael@0: } michael@0: michael@0: // Returns how many parameter this IPC call should have. michael@0: const uint32 GetParamsCount() const { michael@0: return params_count_; michael@0: } michael@0: michael@0: // Returns a pointer to the CrossCallReturn structure. michael@0: CrossCallReturn* GetCallReturn() { michael@0: return &call_return; michael@0: } michael@0: michael@0: // Returns TRUE if this call contains InOut parameters. michael@0: const bool IsInOut() const { michael@0: return (1 == is_in_out_); michael@0: } michael@0: michael@0: // Tells the CrossCall object if it contains InOut parameters. michael@0: void SetIsInOut(bool value) { michael@0: if (value) michael@0: is_in_out_ = 1; michael@0: else michael@0: is_in_out_ = 0; michael@0: } michael@0: michael@0: protected: michael@0: // constructs the IPC call params. Called only from the derived classes michael@0: CrossCallParams(uint32 tag, uint32 params_count) michael@0: : tag_(tag), michael@0: params_count_(params_count), michael@0: is_in_out_(0) { michael@0: } michael@0: michael@0: private: michael@0: uint32 tag_; michael@0: uint32 is_in_out_; michael@0: CrossCallReturn call_return; michael@0: const uint32 params_count_; michael@0: DISALLOW_COPY_AND_ASSIGN(CrossCallParams); michael@0: }; michael@0: michael@0: // ActualCallParams models an specific IPC call parameters with respect to the michael@0: // storage allocation that the packed parameters should need. michael@0: // NUMBER_PARAMS: the number of parameters, valid from 1 to N michael@0: // BLOCK_SIZE: the total storage that the NUMBER_PARAMS parameters can take, michael@0: // typically the block size is defined by the channel size of the underlying michael@0: // ipc mechanism. michael@0: // In practice this class is used to levergage C++ capacity to properly michael@0: // calculate sizes and displacements given the possibility of the packed params michael@0: // blob to be complex. michael@0: // michael@0: // As is, this class assumes that the layout of the blob is as follows. Assume michael@0: // that NUMBER_PARAMS = 2 and a 32-bit build: michael@0: // michael@0: // [ tag 4 bytes] michael@0: // [ IsOnOut 4 bytes] michael@0: // [ call return 52 bytes] michael@0: // [ params count 4 bytes] michael@0: // [ parameter 0 type 4 bytes] michael@0: // [ parameter 0 offset 4 bytes] ---delta to ---\ michael@0: // [ parameter 0 size 4 bytes] | michael@0: // [ parameter 1 type 4 bytes] | michael@0: // [ parameter 1 offset 4 bytes] ---------------|--\ michael@0: // [ parameter 1 size 4 bytes] | | michael@0: // [ parameter 2 type 4 bytes] | | michael@0: // [ parameter 2 offset 4 bytes] ----------------------\ michael@0: // [ parameter 2 size 4 bytes] | | | michael@0: // |---------------------------| | | | michael@0: // | value 0 (x bytes) | <--------------/ | | michael@0: // | value 1 (y bytes) | <-----------------/ | michael@0: // | | | michael@0: // | end of buffer | <---------------------/ michael@0: // |---------------------------| michael@0: // michael@0: // Note that the actual number of params is NUMBER_PARAMS + 1 michael@0: // so that the size of each actual param can be computed from the difference michael@0: // between one parameter and the next down. The offset of the last param michael@0: // points to the end of the buffer and the type and size are undefined. michael@0: // michael@0: template michael@0: class ActualCallParams : public CrossCallParams { michael@0: public: michael@0: // constructor. Pass the ipc unique tag as input michael@0: explicit ActualCallParams(uint32 tag) michael@0: : CrossCallParams(tag, NUMBER_PARAMS) { michael@0: param_info_[0].offset_ = parameters_ - reinterpret_cast(this); michael@0: } michael@0: michael@0: // Testing-only constructor. Allows setting the |number_params| to a michael@0: // wrong value. michael@0: ActualCallParams(uint32 tag, uint32 number_params) michael@0: : CrossCallParams(tag, number_params) { michael@0: param_info_[0].offset_ = parameters_ - reinterpret_cast(this); michael@0: } michael@0: michael@0: // Testing-only method. Allows setting the apparent size to a wrong value. michael@0: // returns the previous size. michael@0: uint32 OverrideSize(uint32 new_size) { michael@0: uint32 previous_size = param_info_[NUMBER_PARAMS].offset_; michael@0: param_info_[NUMBER_PARAMS].offset_ = new_size; michael@0: return previous_size; michael@0: } michael@0: michael@0: // Copies each paramter into the internal buffer. For each you must supply: michael@0: // index: 0 for the first param, 1 for the next an so on michael@0: bool CopyParamIn(uint32 index, const void* parameter_address, uint32 size, michael@0: bool is_in_out, ArgType type) { michael@0: if (index >= NUMBER_PARAMS) { michael@0: return false; michael@0: } michael@0: michael@0: if (kuint32max == size) { michael@0: // Memory error while getting the size. michael@0: return false; michael@0: } michael@0: michael@0: if (size && !parameter_address) { michael@0: return false; michael@0: } michael@0: michael@0: if (param_info_[index].offset_ > sizeof(*this)) { michael@0: // It does not fit, abort copy. michael@0: return false; michael@0: } michael@0: michael@0: char* dest = reinterpret_cast(this) + param_info_[index].offset_; michael@0: michael@0: // We might be touching user memory, this has to be done from inside a try michael@0: // except. michael@0: __try { michael@0: memcpy(dest, parameter_address, size); michael@0: } michael@0: __except(EXCEPTION_EXECUTE_HANDLER) { michael@0: return false; michael@0: } michael@0: michael@0: // Set the flag to tell the broker to update the buffer once the call is michael@0: // made. michael@0: if (is_in_out) michael@0: SetIsInOut(true); michael@0: michael@0: param_info_[index + 1].offset_ = Align(param_info_[index].offset_ + michael@0: size); michael@0: param_info_[index].size_ = size; michael@0: param_info_[index].type_ = type; michael@0: return true; michael@0: } michael@0: michael@0: // Returns a pointer to a parameter in the memory section. michael@0: void* GetParamPtr(size_t index) { michael@0: return reinterpret_cast(this) + param_info_[index].offset_; michael@0: } michael@0: michael@0: // Returns the total size of the buffer. Only valid once all the paramters michael@0: // have been copied in with CopyParamIn. michael@0: uint32 GetSize() const { michael@0: return param_info_[NUMBER_PARAMS].offset_; michael@0: } michael@0: michael@0: protected: michael@0: ActualCallParams() : CrossCallParams(0, NUMBER_PARAMS) { } michael@0: michael@0: private: michael@0: ParamInfo param_info_[NUMBER_PARAMS + 1]; michael@0: char parameters_[BLOCK_SIZE - sizeof(CrossCallParams) michael@0: - sizeof(ParamInfo) * (NUMBER_PARAMS + 1)]; michael@0: DISALLOW_COPY_AND_ASSIGN(ActualCallParams); michael@0: }; michael@0: michael@0: COMPILE_ASSERT(sizeof(ActualCallParams<1, 1024>) == 1024, bad_size_buffer); michael@0: COMPILE_ASSERT(sizeof(ActualCallParams<2, 1024>) == 1024, bad_size_buffer); michael@0: COMPILE_ASSERT(sizeof(ActualCallParams<3, 1024>) == 1024, bad_size_buffer); michael@0: michael@0: } // namespace sandbox michael@0: michael@0: #endif // SANDBOX_SRC_CROSSCALL_PARAMS_H__