1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef SANDBOX_SRC_CROSSCALL_PARAMS_H__
6#define SANDBOX_SRC_CROSSCALL_PARAMS_H__
7
8#include <windows.h>
9#include <lmaccess.h>
10
11#include <memory>
12
13#include "base/basictypes.h"
14#include "sandbox/win/src/internal_types.h"
15#include "sandbox/win/src/sandbox_types.h"
16
17namespace {
18
19// Increases |value| until there is no need for padding given an int64
20// alignment. Returns the increased value.
21uint32 Align(uint32 value) {
22  uint32 alignment = sizeof(int64);
23  return ((value + alignment - 1) / alignment) * alignment;
24}
25
26}
27// This header is part of CrossCall: the sandbox inter-process communication.
28// This header defines the basic types used both in the client IPC and in the
29// server IPC code. CrossCallParams and ActualCallParams model the input
30// parameters of an IPC call and CrossCallReturn models the output params and
31// the return value.
32//
33// An IPC call is defined by its 'tag' which is a (uint32) unique identifier
34// that is used to route the IPC call to the proper server. Every tag implies
35// a complete call signature including the order and type of each parameter.
36//
37// Like most IPC systems. CrossCall is designed to take as inputs 'simple'
38// types such as integers and strings. Classes, generic arrays or pointers to
39// them are not supported.
40//
41// Another limitation of CrossCall is that the return value and output
42// parameters can only be uint32 integers. Returning complex structures or
43// strings is not supported.
44
45namespace sandbox {
46
47// max number of extended return parameters. See CrossCallReturn
48const size_t kExtendedReturnCount = 8;
49
50// Union of multiple types to be used as extended results
51// in the CrossCallReturn.
52union MultiType {
53  uint32 unsigned_int;
54  void* pointer;
55  HANDLE handle;
56  ULONG_PTR ulong_ptr;
57};
58
59// Maximum number of IPC parameters currently supported.
60// To increase this value, we have to:
61//  - Add another Callback typedef to Dispatcher.
62//  - Add another case to the switch on SharedMemIPCServer::InvokeCallback.
63//  - Add another case to the switch in GetActualAndMaxBufferSize
64const int kMaxIpcParams = 9;
65
66// Contains the information about a parameter in the ipc buffer.
67struct ParamInfo {
68  ArgType type_;
69  uint32 offset_;
70  uint32 size_;
71};
72
73// Models the return value and the return parameters of an IPC call
74// currently limited to one status code and eight generic return values
75// which cannot be pointers to other data. For x64 ports this structure
76// might have to use other integer types.
77struct CrossCallReturn {
78  // the IPC tag. It should match the original IPC tag.
79  uint32 tag;
80  // The result of the IPC operation itself.
81  ResultCode call_outcome;
82  // the result of the IPC call as executed in the server. The interpretation
83  // of this value depends on the specific service.
84  union {
85    NTSTATUS nt_status;
86    DWORD    win32_result;
87  };
88  // Number of extended return values.
89  uint32 extended_count;
90  // for calls that should return a windows handle. It is found here.
91  HANDLE handle;
92  // The array of extended values.
93  MultiType extended[kExtendedReturnCount];
94};
95
96// CrossCallParams base class that models the input params all packed in a
97// single compact memory blob. The representation can vary but in general a
98// given child of this class is meant to represent all input parameters
99// necessary to make a IPC call.
100//
101// This class cannot have virtual members because its assumed the IPC
102// parameters start from the 'this' pointer to the end, which is defined by
103// one of the subclasses
104//
105// Objects of this class cannot be constructed directly. Only derived
106// classes have the proper knowledge to construct it.
107class CrossCallParams {
108 public:
109  // Returns the tag (ipc unique id) associated with this IPC.
110  uint32 GetTag() const {
111    return tag_;
112  }
113
114  // Returns the beggining of the buffer where the IPC params can be stored.
115  // prior to an IPC call
116  const void* GetBuffer() const {
117    return this;
118  }
119
120  // Returns how many parameter this IPC call should have.
121  const uint32 GetParamsCount() const {
122    return params_count_;
123  }
124
125  // Returns a pointer to the CrossCallReturn structure.
126  CrossCallReturn* GetCallReturn() {
127    return &call_return;
128  }
129
130  // Returns TRUE if this call contains InOut parameters.
131  const bool IsInOut() const {
132    return (1 == is_in_out_);
133  }
134
135  // Tells the CrossCall object if it contains InOut parameters.
136  void SetIsInOut(bool value) {
137    if (value)
138      is_in_out_ = 1;
139    else
140      is_in_out_ = 0;
141  }
142
143 protected:
144  // constructs the IPC call params. Called only from the derived classes
145  CrossCallParams(uint32 tag, uint32 params_count)
146      : tag_(tag),
147        params_count_(params_count),
148        is_in_out_(0) {
149  }
150
151 private:
152  uint32 tag_;
153  uint32 is_in_out_;
154  CrossCallReturn call_return;
155  const uint32 params_count_;
156  DISALLOW_COPY_AND_ASSIGN(CrossCallParams);
157};
158
159// ActualCallParams models an specific IPC call parameters with respect to the
160// storage allocation that the packed parameters should need.
161// NUMBER_PARAMS: the number of parameters, valid from 1 to N
162// BLOCK_SIZE: the total storage that the NUMBER_PARAMS parameters can take,
163// typically the block size is defined by the channel size of the underlying
164// ipc mechanism.
165// In practice this class is used to levergage C++ capacity to properly
166// calculate sizes and displacements given the possibility of the packed params
167// blob to be complex.
168//
169// As is, this class assumes that the layout of the blob is as follows. Assume
170// that NUMBER_PARAMS = 2 and a 32-bit build:
171//
172// [ tag                4 bytes]
173// [ IsOnOut            4 bytes]
174// [ call return       52 bytes]
175// [ params count       4 bytes]
176// [ parameter 0 type   4 bytes]
177// [ parameter 0 offset 4 bytes] ---delta to ---\
178// [ parameter 0 size   4 bytes]                |
179// [ parameter 1 type   4 bytes]                |
180// [ parameter 1 offset 4 bytes] ---------------|--\
181// [ parameter 1 size   4 bytes]                |  |
182// [ parameter 2 type   4 bytes]                |  |
183// [ parameter 2 offset 4 bytes] ----------------------\
184// [ parameter 2 size   4 bytes]                |  |   |
185// |---------------------------|                |  |   |
186// | value 0     (x bytes)     | <--------------/  |   |
187// | value 1     (y bytes)     | <-----------------/   |
188// |                           |                       |
189// | end of buffer             | <---------------------/
190// |---------------------------|
191//
192// Note that the actual number of params is NUMBER_PARAMS + 1
193// so that the size of each actual param can be computed from the difference
194// between one parameter and the next down. The offset of the last param
195// points to the end of the buffer and the type and size are undefined.
196//
197template <size_t NUMBER_PARAMS, size_t BLOCK_SIZE>
198class ActualCallParams : public CrossCallParams {
199 public:
200  // constructor. Pass the ipc unique tag as input
201  explicit ActualCallParams(uint32 tag)
202      : CrossCallParams(tag, NUMBER_PARAMS) {
203    param_info_[0].offset_ = parameters_ - reinterpret_cast<char*>(this);
204  }
205
206  // Testing-only constructor. Allows setting the |number_params| to a
207  // wrong value.
208  ActualCallParams(uint32 tag, uint32 number_params)
209      : CrossCallParams(tag, number_params) {
210    param_info_[0].offset_ = parameters_ - reinterpret_cast<char*>(this);
211  }
212
213  // Testing-only method. Allows setting the apparent size to a wrong value.
214  // returns the previous size.
215  uint32 OverrideSize(uint32 new_size) {
216    uint32 previous_size = param_info_[NUMBER_PARAMS].offset_;
217    param_info_[NUMBER_PARAMS].offset_ = new_size;
218    return previous_size;
219  }
220
221  // Copies each paramter into the internal buffer. For each you must supply:
222  // index: 0 for the first param, 1 for the next an so on
223  bool CopyParamIn(uint32 index, const void* parameter_address, uint32 size,
224                   bool is_in_out, ArgType type) {
225    if (index >= NUMBER_PARAMS) {
226      return false;
227    }
228
229    if (kuint32max == size) {
230      // Memory error while getting the size.
231      return false;
232    }
233
234    if (size && !parameter_address) {
235      return false;
236    }
237
238    if ((size > sizeof(*this)) ||
239        (param_info_[index].offset_ > (sizeof(*this) - size))) {
240      // It does not fit, abort copy.
241      return false;
242    }
243
244    char* dest = reinterpret_cast<char*>(this) +  param_info_[index].offset_;
245
246    // We might be touching user memory, this has to be done from inside a try
247    // except.
248    __try {
249      memcpy(dest, parameter_address, size);
250    }
251    __except(EXCEPTION_EXECUTE_HANDLER) {
252      return false;
253    }
254
255    // Set the flag to tell the broker to update the buffer once the call is
256    // made.
257    if (is_in_out)
258      SetIsInOut(true);
259
260    param_info_[index + 1].offset_ = Align(param_info_[index].offset_ +
261                                                size);
262    param_info_[index].size_ = size;
263    param_info_[index].type_ = type;
264    return true;
265  }
266
267  // Returns a pointer to a parameter in the memory section.
268  void* GetParamPtr(size_t index) {
269    return reinterpret_cast<char*>(this) + param_info_[index].offset_;
270  }
271
272  // Returns the total size of the buffer. Only valid once all the paramters
273  // have been copied in with CopyParamIn.
274  uint32 GetSize() const {
275    return param_info_[NUMBER_PARAMS].offset_;
276  }
277
278 protected:
279  ActualCallParams() : CrossCallParams(0, NUMBER_PARAMS) { }
280
281 private:
282  ParamInfo param_info_[NUMBER_PARAMS + 1];
283  char parameters_[BLOCK_SIZE - sizeof(CrossCallParams)
284                   - sizeof(ParamInfo) * (NUMBER_PARAMS + 1)];
285  DISALLOW_COPY_AND_ASSIGN(ActualCallParams);
286};
287
288COMPILE_ASSERT(sizeof(ActualCallParams<1, 1024>) == 1024, bad_size_buffer);
289COMPILE_ASSERT(sizeof(ActualCallParams<2, 1024>) == 1024, bad_size_buffer);
290COMPILE_ASSERT(sizeof(ActualCallParams<3, 1024>) == 1024, bad_size_buffer);
291
292}  // namespace sandbox
293
294#endif  // SANDBOX_SRC_CROSSCALL_PARAMS_H__
295