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