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